summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsaurabhb172020-02-26 16:37:17 +0530
committerGitHub2020-02-26 16:37:17 +0530
commit07a8c86216b6b1f694b136ec64c281d62941952e (patch)
treead18839d8b4eb1f13419d07878cc4ec4c9b70032
parente255d0622297488c1c52755be670733418c994cf (diff)
parent1fa449fed953fa11f6bd0ea82cc2d3b115ee0781 (diff)
downloadKiCad-eSim-07a8c86216b6b1f694b136ec64c281d62941952e.tar.gz
KiCad-eSim-07a8c86216b6b1f694b136ec64c281d62941952e.tar.bz2
KiCad-eSim-07a8c86216b6b1f694b136ec64c281d62941952e.zip
Merge pull request #2 from saurabhb17/develop
Remaining files transfered
-rw-r--r--AUTHORS.txt2
-rw-r--r--CTestConfig.cmake12
-rw-r--r--Doxyfile1799
-rw-r--r--README.txt5
-rw-r--r--copyright.h34
-rw-r--r--polygon/CMakeLists.txt17
-rw-r--r--polygon/PolyLine.cpp1374
-rw-r--r--polygon/PolyLine.h490
-rw-r--r--polygon/SutherlandHodgmanClipPoly.h273
-rw-r--r--polygon/clipper.cpp4642
-rw-r--r--polygon/clipper.hpp404
-rw-r--r--polygon/determine_if_point_inside_polygon.odtbin0 -> 26791 bytes
-rw-r--r--polygon/math_for_graphics.cpp520
-rw-r--r--polygon/math_for_graphics.h71
-rw-r--r--polygon/poly2tri/common/shapes.cc500
-rw-r--r--polygon/poly2tri/common/shapes.h351
-rw-r--r--polygon/poly2tri/common/utils.h133
-rw-r--r--polygon/poly2tri/poly2tri.h39
-rw-r--r--polygon/poly2tri/sweep/advancing_front.cc109
-rw-r--r--polygon/poly2tri/sweep/advancing_front.h118
-rw-r--r--polygon/poly2tri/sweep/cdt.cc72
-rw-r--r--polygon/poly2tri/sweep/cdt.h105
-rw-r--r--polygon/poly2tri/sweep/sweep.cc817
-rw-r--r--polygon/poly2tri/sweep/sweep.h284
-rw-r--r--polygon/poly2tri/sweep/sweep_context.cc216
-rw-r--r--polygon/poly2tri/sweep/sweep_context.h186
-rw-r--r--polygon/polygon_test_point_inside.cpp171
-rw-r--r--polygon/polygon_test_point_inside.h59
-rw-r--r--polygon/polygons_defs.h89
-rw-r--r--potrace/AUTHORS5
-rw-r--r--potrace/CMakeLists.txt16
-rw-r--r--potrace/auxiliary.h89
-rw-r--r--potrace/bitmap.h118
-rw-r--r--potrace/bitmap_io.cpp969
-rw-r--r--potrace/bitmap_io.h23
-rw-r--r--potrace/bitops.h50
-rw-r--r--potrace/curve.cpp122
-rw-r--r--potrace/curve.h80
-rw-r--r--potrace/decompose.cpp600
-rw-r--r--potrace/decompose.h18
-rw-r--r--potrace/greymap.cpp1108
-rw-r--r--potrace/greymap.h58
-rw-r--r--potrace/lists.h293
-rw-r--r--potrace/platform.h39
-rw-r--r--potrace/potrace_version.h1
-rw-r--r--potrace/potracelib.cpp129
-rw-r--r--potrace/potracelib.h139
-rw-r--r--potrace/progress.h96
-rw-r--r--potrace/render.cpp294
-rw-r--r--potrace/render.h35
-rw-r--r--potrace/trace.cpp1464
-rw-r--r--potrace/trace.h15
-rw-r--r--qa/CMakeLists.txt15
-rw-r--r--qa/data/complex_hierarchy.kicad_pcb3795
-rw-r--r--qa/test.py18
-rw-r--r--qa/testcases/test_000_qa_works.py17
-rw-r--r--qa/testcases/test_001_pcb_load.py48
-rw-r--r--qa/testcases/test_002_board_class.py132
-rw-r--r--resources/linux/mime/applications/bitmap2component.desktop10
-rw-r--r--resources/linux/mime/applications/cvpcb.desktop10
-rw-r--r--resources/linux/mime/applications/eeschema.desktop11
-rw-r--r--resources/linux/mime/applications/gerbview.desktop10
-rw-r--r--resources/linux/mime/applications/kicad.desktop11
-rw-r--r--resources/linux/mime/applications/pcbcalculator.desktop10
-rw-r--r--resources/linux/mime/applications/pcbnew.desktop10
-rw-r--r--resources/linux/mime/icons/hicolor/128x128/apps/3d.pngbin0 -> 10917 bytes
-rw-r--r--resources/linux/mime/icons/hicolor/128x128/apps/bitmap2component.pngbin0 -> 13176 bytes
-rw-r--r--resources/linux/mime/icons/hicolor/128x128/apps/cvpcb.pngbin0 -> 12149 bytes
-rw-r--r--resources/linux/mime/icons/hicolor/128x128/apps/eeschema.pngbin0 -> 11777 bytes
-rw-r--r--resources/linux/mime/icons/hicolor/128x128/apps/gerbview.pngbin0 -> 21775 bytes
-rw-r--r--resources/linux/mime/icons/hicolor/128x128/apps/kicad.pngbin0 -> 5144 bytes
-rw-r--r--resources/linux/mime/icons/hicolor/128x128/apps/pcbcalculator.pngbin0 -> 9585 bytes
-rw-r--r--resources/linux/mime/icons/hicolor/128x128/apps/pcbnew.pngbin0 -> 12292 bytes
-rw-r--r--resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-3d-project.pngbin0 -> 10917 bytes
-rw-r--r--resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-bitmap2component-project.pngbin0 -> 13176 bytes
-rw-r--r--resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-cvpcb-project.pngbin0 -> 12149 bytes
-rw-r--r--resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-eeschema-project.pngbin0 -> 11777 bytes
-rw-r--r--resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-gerbview-project.pngbin0 -> 21775 bytes
-rw-r--r--resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-kicad-project.pngbin0 -> 5144 bytes
-rw-r--r--resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-pcbcalculator-project.pngbin0 -> 9585 bytes
-rw-r--r--resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-pcbnew-project.pngbin0 -> 12292 bytes
-rw-r--r--resources/linux/mime/icons/hicolor/16x16/mimetypes/application-x-kicad-project.pngbin0 -> 679 bytes
-rw-r--r--resources/linux/mime/icons/hicolor/22x22/apps/kicad.pngbin0 -> 1083 bytes
-rw-r--r--resources/linux/mime/icons/hicolor/22x22/mimetypes/application-x-kicad-project.pngbin0 -> 1083 bytes
-rw-r--r--resources/linux/mime/icons/hicolor/24x24/apps/kicad.pngbin0 -> 1160 bytes
-rw-r--r--resources/linux/mime/icons/hicolor/24x24/mimetypes/application-x-kicad-project.pngbin0 -> 1160 bytes
-rw-r--r--resources/linux/mime/icons/hicolor/32x32/apps/kicad.pngbin0 -> 1448 bytes
-rw-r--r--resources/linux/mime/icons/hicolor/32x32/mimetypes/application-x-kicad-project.pngbin0 -> 1448 bytes
-rw-r--r--resources/linux/mime/icons/hicolor/48x48/apps/bitmap2component.pngbin0 -> 3598 bytes
-rw-r--r--resources/linux/mime/icons/hicolor/48x48/apps/cvpcb.pngbin0 -> 2435 bytes
-rw-r--r--resources/linux/mime/icons/hicolor/48x48/apps/eeschema.pngbin0 -> 3347 bytes
-rw-r--r--resources/linux/mime/icons/hicolor/48x48/apps/gerbview.pngbin0 -> 5333 bytes
-rw-r--r--resources/linux/mime/icons/hicolor/48x48/apps/kicad.pngbin0 -> 2178 bytes
-rw-r--r--resources/linux/mime/icons/hicolor/48x48/apps/pcbcalculator.pngbin0 -> 3256 bytes
-rw-r--r--resources/linux/mime/icons/hicolor/48x48/apps/pcbnew.pngbin0 -> 3366 bytes
-rw-r--r--resources/linux/mime/icons/hicolor/48x48/mimetypes/application-x-bitmap2component-project.pngbin0 -> 3598 bytes
-rw-r--r--resources/linux/mime/icons/hicolor/48x48/mimetypes/application-x-cvpcb-project.pngbin0 -> 2435 bytes
-rw-r--r--resources/linux/mime/icons/hicolor/48x48/mimetypes/application-x-eeschema-project.pngbin0 -> 3347 bytes
-rw-r--r--resources/linux/mime/icons/hicolor/48x48/mimetypes/application-x-gerbview-project.pngbin0 -> 5333 bytes
-rw-r--r--resources/linux/mime/icons/hicolor/48x48/mimetypes/application-x-kicad-project.pngbin0 -> 2178 bytes
-rw-r--r--resources/linux/mime/icons/hicolor/48x48/mimetypes/application-x-pcbcalculator-project.pngbin0 -> 3256 bytes
-rw-r--r--resources/linux/mime/icons/hicolor/48x48/mimetypes/application-x-pcbnew-project.pngbin0 -> 3366 bytes
-rw-r--r--resources/linux/mime/icons/hicolor/scalable/apps/3d.svg32
-rw-r--r--resources/linux/mime/icons/hicolor/scalable/apps/bitmap2component.svg1125
-rw-r--r--resources/linux/mime/icons/hicolor/scalable/apps/cvpcb.svg41
-rw-r--r--resources/linux/mime/icons/hicolor/scalable/apps/eeschema.svg394
-rw-r--r--resources/linux/mime/icons/hicolor/scalable/apps/gerbview.svg109
-rw-r--r--resources/linux/mime/icons/hicolor/scalable/apps/kicad.svg276
-rw-r--r--resources/linux/mime/icons/hicolor/scalable/apps/pcbcalculator.svg1100
-rw-r--r--resources/linux/mime/icons/hicolor/scalable/apps/pcbnew.svg1554
-rw-r--r--resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-3d-project.svg32
-rw-r--r--resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-bitmap2component-project.svg1125
-rw-r--r--resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-cvpcb-project.svg41
-rw-r--r--resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-eeschema-project.svg394
-rw-r--r--resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-gerbview-project.svg109
-rw-r--r--resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-kicad-project.svg276
-rw-r--r--resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-pcbcalculator-project.svg1100
-rw-r--r--resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-pcbnew-project.svg1554
-rw-r--r--resources/linux/mime/mime/packages/kicad.xml21
-rw-r--r--resources/linux/mime/mimelnk/application/x-kicad-pcb.desktop8
-rw-r--r--resources/linux/mime/mimelnk/application/x-kicad-project.desktop7
-rw-r--r--resources/linux/mime/mimelnk/application/x-kicad-schematic.desktop7
-rw-r--r--rules27
-rw-r--r--scripting/build_tools/extract_docstrings.py405
-rw-r--r--scripting/build_tools/fix_swig_imports.py77
-rw-r--r--scripting/dlist.i67
-rw-r--r--scripting/kicad.i160
-rw-r--r--scripting/kicadplugins.i271
-rw-r--r--scripting/python_scripting.cpp364
-rw-r--r--scripting/python_scripting.h62
-rw-r--r--scripting/wx.i301
-rw-r--r--scripting/wx_python_helpers.cpp196
-rw-r--r--scripting/wx_python_helpers.h18
-rwxr-xr-xscripts/kicad-install.sh408
-rwxr-xr-xscripts/lib_convert.py51
-rw-r--r--scripts/library-repos-install.bat89
-rwxr-xr-xscripts/library-repos-install.sh273
-rwxr-xr-xscripts/osx_build_wx.sh166
-rwxr-xr-xscripts/osx_fixbundle.sh129
-rwxr-xr-xscripts/test_kicad_plugin.py85
-rwxr-xr-xscripts/test_plugin.py48
-rw-r--r--template/CMakeLists.txt10
-rw-r--r--template/gost_landscape.kicad_wks87
-rw-r--r--template/gost_portrait.kicad_wks87
-rw-r--r--template/kicad.pro65
-rw-r--r--template/pagelayout_default.kicad_wks34
-rw-r--r--template/pagelayout_logo.kicad_wks191
-rw-r--r--tools/CMakeLists.txt80
-rw-r--r--tools/Info.plist0
-rwxr-xr-xtools/checkcoding.py134
-rw-r--r--tools/container_test.cpp132
-rw-r--r--tools/property_tree.cpp91
-rw-r--r--tools/test-nm-biu-to-ascii-mm-round-tripping.cpp120
-rw-r--r--uncrustify.cfg1612
-rw-r--r--utils/CMakeLists.txt2
-rw-r--r--utils/idftools/CMakeLists.txt36
-rw-r--r--utils/idftools/dxf2idf.cpp460
-rw-r--r--utils/idftools/dxf2idf.h102
-rw-r--r--utils/idftools/dxf2idfmain.cpp190
-rw-r--r--utils/idftools/idf2vrml.cpp990
-rw-r--r--utils/idftools/idf_common.cpp1387
-rw-r--r--utils/idftools/idf_common.h711
-rw-r--r--utils/idftools/idf_cylinder.cpp681
-rw-r--r--utils/idftools/idf_examples/Arduino_MEGA_2560-Rev3.emn149
-rw-r--r--utils/idftools/idf_examples/Arduino_MEGA_2560-Rev3.emp4
-rw-r--r--utils/idftools/idf_examples/idf_example.emn269
-rw-r--r--utils/idftools/idf_examples/idf_example.emp69
-rw-r--r--utils/idftools/idf_examples/test_donut.emn45
-rw-r--r--utils/idftools/idf_examples/test_donut.emp5
-rw-r--r--utils/idftools/idf_examples/test_idf2.emn71
-rw-r--r--utils/idftools/idf_examples/test_idf2.emp290
-rw-r--r--utils/idftools/idf_helpers.cpp323
-rw-r--r--utils/idftools/idf_helpers.h175
-rw-r--r--utils/idftools/idf_outlines.cpp3614
-rw-r--r--utils/idftools/idf_outlines.h771
-rw-r--r--utils/idftools/idf_parser.cpp4288
-rw-r--r--utils/idftools/idf_parser.h721
-rw-r--r--utils/idftools/idf_rect.cpp433
-rw-r--r--utils/idftools/vrml_layer.cpp1788
-rw-r--r--utils/idftools/vrml_layer.h461
180 files changed, 55929 insertions, 1 deletions
diff --git a/AUTHORS.txt b/AUTHORS.txt
index f447c6d..aa27d0b 100644
--- a/AUTHORS.txt
+++ b/AUTHORS.txt
@@ -40,6 +40,8 @@ Mario Luzeiro <mrluzeiro[at]ua-dot-pt>
Mateusz Skowroński <skowri[at]gmail-dot-com>
See also CHANGELOG.txt for contributors.
+== eSim Contributors
+Saurabh Bansode <saurabhbansode[at]gmail-dot-com>
== Document Writers
Jean-Pierre Charras <jean-pierre.charras[at]gipsa-lab.inpg-dot-fr>
diff --git a/CTestConfig.cmake b/CTestConfig.cmake
new file mode 100644
index 0000000..ca1c78e
--- /dev/null
+++ b/CTestConfig.cmake
@@ -0,0 +1,12 @@
+##
+# KiCad CDash/CTest Support
+# Run cmake, then ctest -D Experimental to push to cdash.
+##
+set(CTEST_PROJECT_NAME "KiCad")
+set(CTEST_NIGHTLY_START_TIME "00:00:00 EST")
+
+set(CTEST_DROP_METHOD "http")
+set(CTEST_DROP_SITE "my.cdash.org")
+set(CTEST_DROP_LOCATION "/submit.php?project=KiCad")
+set(CTEST_DROP_SITE_CDASH TRUE)
+
diff --git a/Doxyfile b/Doxyfile
new file mode 100644
index 0000000..eb066f7
--- /dev/null
+++ b/Doxyfile
@@ -0,0 +1,1799 @@
+# Doxyfile 1.8.2
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or sequence of words) that should
+# identify the project. Note that if you do not use Doxywizard you need
+# to put quotes around the project name if it contains spaces.
+
+PROJECT_NAME = "KiCad PCB EDA Suite"
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER =
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer
+# a quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF =
+
+# With the PROJECT_LOGO tag one can specify an logo or icon that is
+# included in the documentation. The maximum height of the logo should not
+# exceed 55 pixels and the maximum width should not exceed 200 pixels.
+# Doxygen will copy the logo to the output directory.
+
+PROJECT_LOGO =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = Documentation/doxygen
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
+# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
+# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
+# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak,
+# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip. Note that you specify absolute paths here, but also
+# relative paths, which will be relative from the directory where doxygen is
+# started.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful if your file system
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF = YES
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 4
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES =
+
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding
+# "class=itcl::class" will allow you to use the command class in the
+# itcl::class meaning.
+
+TCL_SUBST =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension,
+# and language is one of the parsers supported by doxygen: IDL, Java,
+# Javascript, CSharp, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL, C,
+# C++. For instance to make doxygen treat .inc files as Fortran files (default
+# is PHP), and .f files as C (default is Fortran), use: inc=Fortran f=C. Note
+# that for custom extensions you also need to set FILE_PATTERNS otherwise the
+# files are not read by doxygen.
+
+EXTENSION_MAPPING =
+
+# If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all
+# comments according to the Markdown format, which allows for more readable
+# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you
+# can mix doxygen, HTML, and XML commands with Markdown formatting.
+# Disable only in case of backward compatibilities issues.
+
+MARKDOWN_SUPPORT = YES
+
+# When enabled doxygen tries to link words that correspond to documented classes,
+# or namespaces to their corresponding documentation. Such a link can be
+# prevented in individual cases by by putting a % sign in front of the word or
+# globally by setting AUTOLINK_SUPPORT to NO.
+
+AUTOLINK_SUPPORT = YES
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also makes the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter and setter methods for a property. Setting this option to YES (the default) will make doxygen replace the get and set methods by a property in the documentation. This will only work if the methods are indeed getting or setting a simple type. If this is not the case, or you want to show the methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and
+# unions are shown inside the group in which they are included (e.g. using
+# @ingroup) instead of on a separate page (for HTML and Man pages) or
+# section (for LaTeX and RTF).
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and
+# unions with only public data fields will be shown inline in the documentation
+# of the scope in which they are defined (i.e. file, namespace, or group
+# documentation), provided this scope is documented. If set to NO (the default),
+# structs, classes, and unions are shown on a separate page (for HTML and Man
+# pages) or section (for LaTeX and RTF).
+
+INLINE_SIMPLE_STRUCTS = NO
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+# Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be
+# set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given
+# their name and scope. Since this can be an expensive process and often the
+# same symbol appear multiple times in the code, doxygen keeps a cache of
+# pre-resolved symbols. If the cache is too small doxygen will become slower.
+# If the cache is too large, memory is wasted. The cache size is given by this
+# formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols.
+
+LOOKUP_CACHE_SIZE = 6
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = YES
+
+# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal
+# scope will be included in the documentation.
+
+EXTRACT_PACKAGE = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespaces are hidden.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
+# will list include files with double quotes in the documentation
+# rather than with sharp brackets.
+
+FORCE_LOCAL_INCLUDES = NO
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
+# will sort the (brief and detailed) documentation of class members so that
+# constructors and destructors are listed first. If set to NO (the default)
+# the constructors will appear in the respective orders defined by
+# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS.
+# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
+# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to
+# do proper type resolution of all parameters of a function it will reject a
+# match between the prototype and the implementation of a member function even
+# if there is only one candidate or it is obvious which candidate to choose
+# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen
+# will still accept a match between prototype and implementation in such cases.
+
+STRICT_PROTO_MATCHING = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or macro consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and macros in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page.
+# This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option.
+# You can optionally specify a file name after the option, if omitted
+# DoxygenLayout.xml will be used as the name of the layout file.
+
+LAYOUT_FILE =
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files
+# containing the references data. This must be a list of .bib files. The
+# .bib extension is automatically appended if omitted. Using this command
+# requires the bibtex tool to be installed. See also
+# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style
+# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this
+# feature you need bibtex and perl available in the search path.
+
+CITE_BIB_FILES =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR = YES
+
+# The WARN_NO_PARAMDOC option can be enabled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT = "$file:$line: $text "
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT = Documentation/development/coding-style-policy.md \
+ Documentation/development/stable-release-policy.md \
+ Documentation/development/road-map.md \
+ Documentation/development/compiling.md \
+ kicad \
+ pcbnew \
+ cvpcb \
+ eeschema \
+ 3d-viewer \
+ common \
+ gerbview \
+ pagelayout_editor \
+ include \
+ polygon \
+ potrace
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# the list of possible encodings.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh
+# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py
+# *.f90 *.f *.for *.vhd *.vhdl
+
+FILE_PATTERNS = *.h \
+ *.cpp
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
+
+EXCLUDE = polygon/clipper.cpp
+
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+# If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis.
+# Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match.
+# The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty or if
+# non of the patterns match the file name, INPUT_FILTER is applied.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any)
+# and it is also possible to disable source filtering for a specific pattern
+# using *.ext= (so without naming a filter). This option only has effect when
+# FILTER_SOURCE_FILES is enabled.
+
+FILTER_SOURCE_PATTERNS =
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER = YES
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = YES
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C, C++ and Fortran comments will always remain visible.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = YES
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.
+# Otherwise they will link to the documentation.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = YES
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header. Note that when using a custom header you are responsible
+# for the proper inclusion of any scripts and style sheets that doxygen
+# needs, which is dependent on the configuration options used.
+# It is advised to generate a default header using "doxygen -w html
+# header.html footer.html stylesheet.css YourConfigFile" and then modify
+# that header. Note that the header is subject to change so you typically
+# have to redo this when upgrading to a newer version of doxygen or when
+# changing the value of configuration settings such as GENERATE_TREEVIEW!
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If left blank doxygen will
+# generate a default style sheet. Note that it is recommended to use
+# HTML_EXTRA_STYLESHEET instead of this one, as it is more robust and this
+# tag will in the future become obsolete.
+
+HTML_STYLESHEET =
+
+# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional
+# user-defined cascading style sheet that is included after the standard
+# style sheets created by doxygen. Using this option one can overrule
+# certain style aspects. This is preferred over using HTML_STYLESHEET
+# since it does not replace the standard style sheet and is therefor more
+# robust against future updates. Doxygen will copy the style sheet file to
+# the output directory.
+
+HTML_EXTRA_STYLESHEET =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that
+# the files will be copied as-is; there are no commands or markers available.
+
+HTML_EXTRA_FILES =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
+# Doxygen will adjust the colors in the style sheet and background images
+# according to this color. Hue is specified as an angle on a colorwheel,
+# see http://en.wikipedia.org/wiki/Hue for more information.
+# For instance the value 0 represents red, 60 is yellow, 120 is green,
+# 180 is cyan, 240 is blue, 300 purple, and 360 is red again.
+# The allowed range is 0 to 359.
+
+HTML_COLORSTYLE_HUE = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of
+# the colors in the HTML output. For a value of 0 the output will use
+# grayscales only. A value of 255 will produce the most vivid colors.
+
+HTML_COLORSTYLE_SAT = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to
+# the luminance component of the colors in the HTML output. Values below
+# 100 gradually make the output lighter, whereas values above 100 make
+# the output darker. The value divided by 100 is the actual gamma applied,
+# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2,
+# and 100 does not change the gamma.
+
+HTML_COLORSTYLE_GAMMA = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting
+# this to NO can help when comparing the output of multiple runs.
+
+HTML_TIMESTAMP = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded.
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of
+# entries shown in the various tree structured indices initially; the user
+# can expand and collapse entries dynamically later on. Doxygen will expand
+# the tree to such a level that at most the specified number of entries are
+# visible (unless a fully collapsed tree already exceeds this amount).
+# So setting the number of entries 1 will produce a full collapsed tree by
+# default. 0 is a special value representing an infinite number of entries
+# and will result in a full expanded tree by default.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+
+GENERATE_DOCSET = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely
+# identify the documentation publisher. This should be a reverse domain-name
+# style string, e.g. com.mycompany.MyDocSet.documentation.
+
+DOCSET_PUBLISHER_ID = org.doxygen.Publisher
+
+# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher.
+
+DOCSET_PUBLISHER_NAME = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated
+# that can be used as input for Qt's qhelpgenerator to generate a
+# Qt Compressed Help (.qch) of the generated HTML documentation.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#namespace
+
+QHP_NAMESPACE = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to
+# add. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#custom-filters
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">
+# Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">
+# Qt Help Project / Filter Attributes</a>.
+
+QHP_SECT_FILTER_ATTRS =
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
+# .qhp file.
+
+QHG_LOCATION =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
+# will be generated, which together with the HTML files, form an Eclipse help
+# plugin. To install this plugin and make it available under the help contents
+# menu in Eclipse, the contents of the directory containing the HTML and XML
+# files needs to be copied into the plugins directory of eclipse. The name of
+# the directory within the plugins directory should be the same as
+# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before
+# the help appears.
+
+GENERATE_ECLIPSEHELP = NO
+
+# A unique identifier for the eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have
+# this name.
+
+ECLIPSE_DOC_ID = org.doxygen.Project
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs)
+# at top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it. Since the tabs have the same information as the
+# navigation tree you can set this option to NO if you already set
+# GENERATE_TREEVIEW to YES.
+
+DISABLE_INDEX = NO
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to YES, a side panel will be generated
+# containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
+# Windows users are probably better off using the HTML help feature.
+# Since the tree basically has the same information as the tab index you
+# could consider to set DISABLE_INDEX to NO when enabling this option.
+
+GENERATE_TREEVIEW = YES
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values
+# (range [0,1..20]) that doxygen will group on one line in the generated HTML
+# documentation. Note that a value of 0 will completely suppress the enum
+# values from appearing in the overview section.
+
+ENUM_VALUES_PER_LINE = 4
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open
+# links to external symbols imported via tag files in a separate window.
+
+EXT_LINKS_IN_WINDOW = NO
+
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are
+# not supported properly for IE 6.0, but are supported on all modern browsers.
+# Note that when changing this option you need to delete any form_*.png files
+# in the HTML output before the changes have effect.
+
+FORMULA_TRANSPARENT = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax
+# (see http://www.mathjax.org) which uses client side Javascript for the
+# rendering instead of using prerendered bitmaps. Use this if you do not
+# have LaTeX installed or if you want to formulas look prettier in the HTML
+# output. When enabled you may also need to install MathJax separately and
+# configure the path to it using the MATHJAX_RELPATH option.
+
+USE_MATHJAX = NO
+
+# When MathJax is enabled you need to specify the location relative to the
+# HTML output directory using the MATHJAX_RELPATH option. The destination
+# directory should contain the MathJax.js script. For instance, if the mathjax
+# directory is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to
+# the MathJax Content Delivery Network so you can quickly see the result without
+# installing MathJax.
+# However, it is strongly recommended to install a local
+# copy of MathJax from http://www.mathjax.org before deployment.
+
+MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension
+# names that should be enabled during MathJax rendering.
+
+MATHJAX_EXTENSIONS =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box
+# for the HTML output. The underlying search engine uses javascript
+# and DHTML and should work on any modern browser. Note that when using
+# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets
+# (GENERATE_DOCSET) there is already a search function so this one should
+# typically be disabled. For large projects the javascript based search engine
+# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
+
+SEARCHENGINE = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a PHP enabled web server instead of at the web client
+# using Javascript. Doxygen will generate the search PHP script and index
+# file to put on the web server. The advantage of the server
+# based approach is that it scales better to large projects and allows
+# full text search. The disadvantages are that it is more difficult to setup
+# and does not have live searching capabilities.
+
+SERVER_BASED_SEARCH = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+# Note that when enabling USE_PDFLATEX this option is only used for
+# generating bitmaps for formulas in the HTML output, but not in the
+# Makefile that is written to the output directory.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for
+# the generated latex document. The footer should contain everything after
+# the last chapter. If it is left blank doxygen will generate a
+# standard footer. Notice: only use this tag if you know what you are doing!
+
+LATEX_FOOTER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = NO
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = NO
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES = NO
+
+# If LATEX_SOURCE_CODE is set to YES then doxygen will include
+# source code with syntax highlighting in the LaTeX output.
+# Note that which sources are shown also depends on other settings
+# such as SOURCE_BROWSER.
+
+LATEX_SOURCE_CODE = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See
+# http://en.wikipedia.org/wiki/BibTeX for more info.
+
+LATEX_BIB_STYLE = plain
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load style sheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT = xml
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader.
+# This is useful
+# if you want to understand what is going on.
+# On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# pointed to by INCLUDE_PATH will be searched when a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH = include \
+ pcbnew \
+ eeschema \
+ gerbview
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS = *.h
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition that
+# overrules the definition found in the source code.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all references to function-like macros
+# that are alone on a line, have an all uppercase name, and do not end with a
+# semicolon, because these will confuse the parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles. For each
+# tag file the location of the external documentation should be added. The
+# format of a tag file without this location is as follows:
+#
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+#
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths
+# or URLs. Note that each tag file must have a unique name (where the name does
+# NOT include the path). If a tag file is not located in the directory in which
+# doxygen is run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option also works with HAVE_DOT disabled, but it is recommended to
+# install and use dot, since it yields more powerful graphs.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = NO
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is
+# allowed to run in parallel. When set to 0 (the default) doxygen will
+# base this on the number of processors available in the system. You can set it
+# explicitly to a value larger than 0 to get control over the balance
+# between CPU load and processing speed.
+
+DOT_NUM_THREADS = 0
+
+# By default doxygen will use the Helvetica font for all dot files that
+# doxygen generates. When you want a differently looking font you can specify
+# the font name using DOT_FONTNAME. You need to make sure dot is able to find
+# the font, which can be done by putting it in a standard location or by setting
+# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the
+# directory containing the font.
+
+DOT_FONTNAME = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
+# The default size is 10pt.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the Helvetica font.
+# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to
+# set the path where dot can find it.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK = NO
+
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside
+# the class node. If there are many fields or methods and many nodes the
+# graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS
+# threshold limits the number of items for each type to make the size more
+# managable. Set this to 0 for no limit. Note that the threshold may be
+# exceeded by 50% before the limit is enforced.
+
+UML_LIMIT_NUM_FIELDS = 10
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will generate a graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are svg, png, jpg, or gif.
+# If left blank png will be used. If you choose svg you need to set
+# HTML_FILE_EXTENSION to xhtml in order to make the SVG files
+# visible in IE 9+ (other browsers do not have this requirement).
+
+DOT_IMAGE_FORMAT = png
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+# Note that this requires a modern browser other than Internet Explorer.
+# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you
+# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files
+# visible. Older versions of IE do not have SVG support.
+
+INTERACTIVE_SVG = NO
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the
+# \mscfile command).
+
+MSCFILE_DIRS =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
+# a graph (i.e. they become hard to read).
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
diff --git a/README.txt b/README.txt
index 5a7fe27..b33bbd8 100644
--- a/README.txt
+++ b/README.txt
@@ -1,8 +1,11 @@
-KiCad README
+KiCad-eSim README
+* This particular repository is of version 4.0.7 from KiCad EDA tool, maintained by eSim Team of FOSSEE project at IIT Bombay (contact-esim@fossee.in) to be used in eSim EDA tool*
+
============
For specific documentation like Compiling, GUI translation, Old changelogs see the
Documentation subfolder.
+This partic
Files
-----
AUTHORS.txt - The authors, contributors, document writers and translators list
diff --git a/copyright.h b/copyright.h
new file mode 100644
index 0000000..c99a81b
--- /dev/null
+++ b/copyright.h
@@ -0,0 +1,34 @@
+Copyright template
+
+
+A copyright message which may be added to all source files at their top.
+There is one line that represents the main copyright holder which is its
+original author.
+
+Workers who earn partial copyright holder status of a source module may
+choose to document this corresponding work in the CHANGELOG.txt file.
+
+
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2015 <author>
+ * Copyright (C) 2015 KiCad Developers, see CHANGELOG.TXT for contributors.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
diff --git a/polygon/CMakeLists.txt b/polygon/CMakeLists.txt
new file mode 100644
index 0000000..2f09cbe
--- /dev/null
+++ b/polygon/CMakeLists.txt
@@ -0,0 +1,17 @@
+
+include_directories(BEFORE ${INC_BEFORE})
+include_directories(
+ ${INC_AFTER}
+ )
+
+set(POLYGON_SRCS
+ math_for_graphics.cpp
+ PolyLine.cpp
+ polygon_test_point_inside.cpp
+ clipper.cpp
+)
+
+add_library(polygon STATIC ${POLYGON_SRCS})
+
+add_dependencies( polygon lib-dependencies )
+
diff --git a/polygon/PolyLine.cpp b/polygon/PolyLine.cpp
new file mode 100644
index 0000000..1b99845
--- /dev/null
+++ b/polygon/PolyLine.cpp
@@ -0,0 +1,1374 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Few parts of this code come from FreePCB, released under the GNU General Public License V2.
+ * (see http://www.freepcb.com/ )
+ *
+ * Copyright (C) 2012-2014 Jean-Pierre Charras, jp.charras at wanadoo.fr
+ * Copyright (C) 2012-2014 KiCad Developers, see CHANGELOG.TXT for contributors.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * @file PolyLine.cpp
+ * @note implementation of CPolyLine class
+ */
+
+//
+// implementation for kicad, using clipper polygon clipping library
+// for transformations not handled (at least for Kicad) by boost::polygon
+//
+#include <cmath>
+#include <vector>
+#include <algorithm>
+
+#include <fctsys.h>
+#include <common.h> // KiROUND
+
+#include <PolyLine.h>
+#include <bezier_curves.h>
+#include <polygon_test_point_inside.h>
+#include <math_for_graphics.h>
+#include <polygon_test_point_inside.h>
+
+
+CPolyLine::CPolyLine()
+{
+ m_hatchStyle = NO_HATCH;
+ m_hatchPitch = 0;
+ m_layer = F_Cu;
+ m_flags = 0;
+}
+
+CPolyLine::CPolyLine( const CPolyLine& aCPolyLine)
+{
+ Copy( &aCPolyLine );
+ m_HatchLines = aCPolyLine.m_HatchLines; // vector <> copy
+}
+
+
+// destructor, removes display elements
+//
+CPolyLine::~CPolyLine()
+{
+ UnHatch();
+}
+
+/* Removes corners which create a null segment edge
+ * (i.e. when 2 successive corners are at the same location)
+ * returns the count of removed corners.
+ */
+ int CPolyLine::RemoveNullSegments()
+{
+ int removed = 0;
+ unsigned startcountour = 0;
+
+ for( unsigned icnt = 1; icnt < m_CornersList.GetCornersCount(); icnt ++ )
+ {
+ unsigned last = icnt-1;
+ if( m_CornersList[icnt].end_contour )
+ {
+ last = startcountour;
+ startcountour = icnt+1;
+ }
+
+ if( ( m_CornersList[last].x == m_CornersList[icnt].x ) &&
+ ( m_CornersList[last].y == m_CornersList[icnt].y ) )
+ {
+ DeleteCorner( icnt );
+ icnt--;
+ removed ++;
+ }
+
+ if( m_CornersList[icnt].end_contour )
+ {
+ startcountour = icnt+1;
+ icnt++;
+ }
+ }
+
+ return removed;
+}
+
+
+/* Convert a self-intersecting polygon to one (or more) non self-intersecting polygon(s)
+ * and removes null segments.
+ * param aNewPolygonList = a std::vector<CPolyLine*> reference where to store new CPolyLine
+ * needed by the normalization
+ * return the polygon count (always >= 1, because there is at least one polygon)
+ * There are new polygons only if the polygon count is > 1
+ */
+int CPolyLine::NormalizeAreaOutlines( std::vector<CPolyLine*>* aNewPolygonList )
+{
+ SHAPE_POLY_SET polySet = ConvertPolyListToPolySet( m_CornersList );
+
+ // We are expecting only one main outline, but this main outline can have holes
+ // if holes: combine holes and remove them from the main outline.
+ SHAPE_POLY_SET::POLYGON& outline = polySet.Polygon( 0 );
+ SHAPE_POLY_SET holesBuffer;
+
+ // Move holes stored in outline to holesBuffer:
+ // The first SHAPE_LINE_CHAIN is the main outline, others are holes
+ while( outline.size() > 1 )
+ {
+ holesBuffer.AddOutline( outline.back() );
+ outline.pop_back();
+ }
+
+ polySet.Simplify();
+
+ // If any hole, substract it to main outline
+ if( holesBuffer.OutlineCount() )
+ {
+ holesBuffer.Simplify();
+ polySet.BooleanSubtract( holesBuffer );
+ }
+
+ RemoveAllContours();
+
+ // Note: we can have more than outline, because a self intersecting outline will be
+ // broken to non intersecting polygons, and removing holes can also create a few polygons
+ for( int ii = 0; ii < polySet.OutlineCount(); ii++ )
+ {
+ CPolyLine* polyline = this;
+
+ if( ii > 0 )
+ {
+ polyline = new CPolyLine;
+ polyline->ImportSettings( this );
+ aNewPolygonList->push_back( polyline );
+ }
+
+ SHAPE_POLY_SET pnew;
+ pnew.NewOutline();
+ pnew.Polygon( 0 ) = polySet.CPolygon( ii );
+
+ polyline->m_CornersList = ConvertPolySetToPolyList( pnew );
+ polyline->RemoveNullSegments();
+ }
+
+ return polySet.OutlineCount();
+}
+
+/**
+ * Function ImportSettings
+ * Copy settings (layer, hatch styles) from aPoly
+ */
+void CPolyLine::ImportSettings( const CPolyLine * aPoly )
+{
+ SetLayer( aPoly->GetLayer() );
+ SetHatchStyle( aPoly->GetHatchStyle() );
+ SetHatchPitch( aPoly->GetHatchPitch() );
+}
+
+/* initialize a contour
+ * set layer, hatch style, and starting point
+ */
+void CPolyLine::Start( LAYER_NUM layer, int x, int y, int hatch )
+{
+ m_layer = layer;
+ SetHatchStyle( (enum HATCH_STYLE) hatch );
+ CPolyPt poly_pt( x, y );
+ poly_pt.end_contour = false;
+
+ m_CornersList.Append( poly_pt );
+}
+
+
+// add a corner to unclosed polyline
+//
+void CPolyLine::AppendCorner( int x, int y )
+{
+ UnHatch();
+ CPolyPt poly_pt( x, y );
+ poly_pt.end_contour = false;
+
+ // add entries for new corner
+ m_CornersList.Append( poly_pt );
+}
+
+// move corner of polyline
+//
+void CPolyLine::MoveCorner( int ic, int x, int y )
+{
+ UnHatch();
+ m_CornersList[ic].x = x;
+ m_CornersList[ic].y = y;
+ Hatch();
+}
+
+
+// delete corner and adjust arrays
+//
+void CPolyLine::DeleteCorner( int ic )
+{
+ UnHatch();
+ int icont = GetContour( ic );
+ int iend = GetContourEnd( icont );
+ bool closed = icont < GetContoursCount() - 1 || GetClosed();
+
+ if( !closed )
+ {
+ // open contour, must be last contour
+ m_CornersList.DeleteCorner( ic );
+ }
+ else
+ {
+ // closed contour
+ m_CornersList.DeleteCorner( ic );
+
+ if( ic == iend )
+ m_CornersList[ic - 1].end_contour = true;
+ }
+
+ if( closed && GetContourSize( icont ) < 3 )
+ {
+ // delete the entire contour
+ RemoveContour( icont );
+ }
+}
+
+
+/******************************************/
+void CPolyLine::RemoveContour( int icont )
+/******************************************/
+
+/**
+ * Function RemoveContour
+ * @param icont = contour number to remove
+ * remove a contour only if there is more than 1 contour
+ */
+{
+ UnHatch();
+ int istart = GetContourStart( icont );
+ int iend = GetContourEnd( icont );
+
+ int polycount = GetContoursCount();
+
+ if( icont == 0 && polycount == 1 )
+ {
+ // remove the only contour
+ wxASSERT( 0 );
+ }
+ else
+ {
+ // remove closed contour
+ for( int ic = iend; ic>=istart; ic-- )
+ {
+ m_CornersList.DeleteCorner( ic );
+ }
+ }
+
+ Hatch();
+}
+
+
+CPolyLine* CPolyLine::Chamfer( unsigned int aDistance )
+{
+ // Null segments create serious issues in calculations.
+ // remove them:
+ RemoveNullSegments();
+
+ CPolyLine* newPoly = new CPolyLine;
+
+ if( !aDistance )
+ {
+ newPoly->Copy( this );
+ return newPoly;
+ }
+
+ int polycount = GetContoursCount();
+
+ for( int contour = 0; contour < polycount; contour++ )
+ {
+ unsigned int startIndex = GetContourStart( contour );
+ unsigned int endIndex = GetContourEnd( contour );
+
+ for( unsigned int index = startIndex; index <= endIndex; index++ )
+ {
+ // Current vertex
+ int x1 = m_CornersList[index].x;
+ int y1 = m_CornersList[index].y;
+ double xa, ya; // Previous vertex
+ double xb, yb; // Next vertex
+
+ if( index == startIndex )
+ {
+ xa = m_CornersList[endIndex].x - x1;
+ ya = m_CornersList[endIndex].y - y1;
+ }
+ else
+ {
+ xa = m_CornersList[index - 1].x - x1;
+ ya = m_CornersList[index - 1].y - y1;
+ }
+
+ if( index == endIndex )
+ {
+ xb = m_CornersList[startIndex].x - x1;
+ yb = m_CornersList[startIndex].y - y1;
+ }
+ else
+ {
+ xb = m_CornersList[index + 1].x - x1;
+ yb = m_CornersList[index + 1].y - y1;
+ }
+
+ double lena = hypot( xa, ya );
+ double lenb = hypot( xb, yb );
+ double distance = aDistance;
+
+ // Chamfer one half of an edge at most
+ if( 0.5 * lena < distance )
+ distance = 0.5 * lena;
+
+ if( 0.5 * lenb < distance )
+ distance = 0.5 * lenb;
+
+ int nx1 = KiROUND( distance * xa / lena );
+ int ny1 = KiROUND( distance * ya / lena );
+
+ if( index == startIndex )
+ newPoly->Start( GetLayer(), x1 + nx1, y1 + ny1, GetHatchStyle() );
+ else
+ newPoly->AppendCorner( x1 + nx1, y1 + ny1 );
+
+ int nx2 = KiROUND( distance * xb / lenb );
+ int ny2 = KiROUND( distance * yb / lenb );
+ newPoly->AppendCorner( x1 + nx2, y1 + ny2 );
+ }
+
+ newPoly->CloseLastContour();
+ }
+
+ return newPoly;
+}
+
+
+CPolyLine* CPolyLine::Fillet( unsigned int aRadius, unsigned int aSegments )
+{
+ // Null segments create serious issues in calculations.
+ // remove them:
+ RemoveNullSegments();
+
+ CPolyLine* newPoly = new CPolyLine;
+
+ if( !aRadius )
+ {
+ newPoly->Copy( this );
+ return newPoly;
+ }
+
+ int polycount = GetContoursCount();
+
+ for( int contour = 0; contour < polycount; contour++ )
+ {
+ unsigned int startIndex = GetContourStart( contour );
+ unsigned int endIndex = GetContourEnd( contour );
+
+ for( unsigned int index = startIndex; index <= endIndex; index++ )
+ {
+ // Current vertex
+ int x1 = m_CornersList[index].x;
+ int y1 = m_CornersList[index].y;
+ double xa, ya; // Previous vertex
+ double xb, yb; // Next vertex
+
+ if( index == startIndex )
+ {
+ xa = m_CornersList[endIndex].x - x1;
+ ya = m_CornersList[endIndex].y - y1;
+ }
+ else
+ {
+ xa = m_CornersList[index - 1].x - x1;
+ ya = m_CornersList[index - 1].y - y1;
+ }
+
+ if( index == endIndex )
+ {
+ xb = m_CornersList[startIndex].x - x1;
+ yb = m_CornersList[startIndex].y - y1;
+ }
+ else
+ {
+ xb = m_CornersList[index + 1].x - x1;
+ yb = m_CornersList[index + 1].y - y1;
+ }
+
+ double lena = hypot( xa, ya );
+ double lenb = hypot( xb, yb );
+ double cosine = ( xa * xb + ya * yb ) / ( lena * lenb );
+
+ double radius = aRadius;
+ double denom = sqrt( 2.0 / ( 1 + cosine ) - 1 );
+
+ // Do nothing in case of parallel edges
+ if( !std::isfinite( denom ) )
+ continue;
+
+ // Limit rounding distance to one half of an edge
+ if( 0.5 * lena * denom < radius )
+ radius = 0.5 * lena * denom;
+
+ if( 0.5 * lenb * denom < radius )
+ radius = 0.5 * lenb * denom;
+
+ // Calculate fillet arc absolute center point (xc, yx)
+ double k = radius / sqrt( .5 * ( 1 - cosine ) );
+ double lenab = sqrt( ( xa / lena + xb / lenb ) * ( xa / lena + xb / lenb ) +
+ ( ya / lena + yb / lenb ) * ( ya / lena + yb / lenb ) );
+ double xc = x1 + k * ( xa / lena + xb / lenb ) / lenab;
+ double yc = y1 + k * ( ya / lena + yb / lenb ) / lenab;
+
+ // Calculate arc start and end vectors
+ k = radius / sqrt( 2 / ( 1 + cosine ) - 1 );
+ double xs = x1 + k * xa / lena - xc;
+ double ys = y1 + k * ya / lena - yc;
+ double xe = x1 + k * xb / lenb - xc;
+ double ye = y1 + k * yb / lenb - yc;
+
+ // Cosine of arc angle
+ double argument = ( xs * xe + ys * ye ) / ( radius * radius );
+
+ if( argument < -1 ) // Just in case...
+ argument = -1;
+ else if( argument > 1 )
+ argument = 1;
+
+ double arcAngle = acos( argument );
+
+ // Calculate the number of segments
+ double tempSegments = (double) aSegments * ( arcAngle / ( 2 * M_PI ) );
+
+ if( tempSegments - (int) tempSegments > 0 )
+ tempSegments++;
+
+ unsigned int segments = (unsigned int) tempSegments;
+
+ double deltaAngle = arcAngle / segments;
+ double startAngle = atan2( -ys, xs );
+
+ // Flip arc for inner corners
+ if( xa * yb - ya * xb <= 0 )
+ deltaAngle *= -1;
+
+ double nx = xc + xs;
+ double ny = yc + ys;
+
+ if( index == startIndex )
+ newPoly->Start( GetLayer(), KiROUND( nx ), KiROUND( ny ), GetHatchStyle() );
+ else
+ newPoly->AppendCorner( KiROUND( nx ), KiROUND( ny ) );
+
+ for( unsigned int j = 0; j < segments; j++ )
+ {
+ nx = xc + cos( startAngle + (j + 1) * deltaAngle ) * radius;
+ ny = yc - sin( startAngle + (j + 1) * deltaAngle ) * radius;
+ newPoly->AppendCorner( KiROUND( nx ), KiROUND( ny ) );
+ }
+ }
+
+ newPoly->CloseLastContour();
+ }
+
+ return newPoly;
+}
+
+
+/******************************************/
+void CPolyLine::RemoveAllContours( void )
+/******************************************/
+
+/**
+ * function RemoveAllContours
+ * removes all corners from the list.
+ * Others params are not changed
+ */
+{
+ m_CornersList.RemoveAllContours();
+}
+
+
+/**
+ * Function InsertCorner
+ * insert a new corner between two existing corners
+ * @param ic = index for the insertion point: the corner is inserted AFTER ic
+ * @param x, y = coordinates corner to insert
+ */
+void CPolyLine::InsertCorner( int ic, int x, int y )
+{
+ UnHatch();
+
+ if( (unsigned) (ic) >= m_CornersList.GetCornersCount() )
+ {
+ m_CornersList.Append( CPolyPt( x, y ) );
+ }
+ else
+ {
+ m_CornersList.InsertCorner(ic, CPolyPt( x, y ) );
+ }
+
+ if( (unsigned) (ic + 1) < m_CornersList.GetCornersCount() )
+ {
+ if( m_CornersList[ic].end_contour )
+ {
+ m_CornersList[ic + 1].end_contour = true;
+ m_CornersList[ic].end_contour = false;
+ }
+ }
+
+ Hatch();
+}
+
+
+// undraw polyline by removing all graphic elements from display list
+void CPolyLine::UnHatch()
+{
+ m_HatchLines.clear();
+}
+
+
+const EDA_RECT CPolyLine::GetBoundingBox()
+{
+ int xmin = INT_MAX;
+ int ymin = INT_MAX;
+ int xmax = INT_MIN;
+ int ymax = INT_MIN;
+
+ for( unsigned i = 0; i< m_CornersList.GetCornersCount(); i++ )
+ {
+ xmin = std::min( xmin, m_CornersList[i].x );
+ xmax = std::max( xmax, m_CornersList[i].x );
+ ymin = std::min( ymin, m_CornersList[i].y );
+ ymax = std::max( ymax, m_CornersList[i].y );
+ }
+
+ EDA_RECT r;
+ r.SetOrigin( wxPoint( xmin, ymin ) );
+ r.SetEnd( wxPoint( xmax, ymax ) );
+
+ return r;
+}
+
+
+const EDA_RECT CPolyLine::GetBoundingBox( int icont )
+{
+ int xmin = INT_MAX;
+ int ymin = INT_MAX;
+ int xmax = INT_MIN;
+ int ymax = INT_MIN;
+ int istart = GetContourStart( icont );
+ int iend = GetContourEnd( icont );
+
+ for( int i = istart; i<=iend; i++ )
+ {
+ xmin = std::min( xmin, m_CornersList[i].x );
+ xmax = std::max( xmax, m_CornersList[i].x );
+ ymin = std::min( ymin, m_CornersList[i].y );
+ ymax = std::max( ymax, m_CornersList[i].y );
+ }
+
+ EDA_RECT r;
+ r.SetOrigin( wxPoint( xmin, ymin ) );
+ r.SetEnd( wxPoint( xmax, ymax ) );
+
+ return r;
+}
+
+
+int CPolyLine::GetContoursCount() const
+{
+ return m_CornersList.GetContoursCount();
+}
+
+
+
+int CPOLYGONS_LIST::GetContoursCount() const
+{
+ if( !m_cornersList.size() )
+ return 0;
+
+ // count the number of corners flagged end_contour
+ int ncont = 0;
+
+ for( unsigned ic = 0; ic < m_cornersList.size(); ic++ )
+ if( m_cornersList[ic].end_contour )
+ ncont++;
+
+ // The last corner can be not yet flagged end_contour.
+ // It was not counted, but the polygon exists, so count it
+ if( !m_cornersList[m_cornersList.size() - 1].end_contour )
+ ncont++;
+
+ return ncont;
+}
+
+
+int CPolyLine::GetContour( int ic )
+{
+ int ncont = 0;
+
+ for( int i = 0; i<ic; i++ )
+ {
+ if( m_CornersList[i].end_contour )
+ ncont++;
+ }
+
+ return ncont;
+}
+
+
+int CPolyLine::GetContourStart( int icont )
+{
+ if( icont == 0 )
+ return 0;
+
+ int ncont = 0;
+
+ for( unsigned i = 0; i<m_CornersList.GetCornersCount(); i++ )
+ {
+ if( m_CornersList[i].end_contour )
+ {
+ ncont++;
+
+ if( ncont == icont )
+ return i + 1;
+ }
+ }
+
+ wxASSERT( 0 );
+ return 0;
+}
+
+
+int CPolyLine::GetContourEnd( int icont )
+{
+ if( icont < 0 )
+ return 0;
+
+ if( icont == GetContoursCount() - 1 )
+ return m_CornersList.GetCornersCount() - 1;
+
+ int ncont = 0;
+
+ for( unsigned i = 0; i<m_CornersList.GetCornersCount(); i++ )
+ {
+ if( m_CornersList[i].end_contour )
+ {
+ if( ncont == icont )
+ return i;
+
+ ncont++;
+ }
+ }
+
+ wxASSERT( 0 );
+ return 0;
+}
+
+
+int CPolyLine::GetContourSize( int icont )
+{
+ return GetContourEnd( icont ) - GetContourStart( icont ) + 1;
+}
+
+
+bool CPolyLine::GetClosed()
+{
+ if( m_CornersList.GetCornersCount() == 0 )
+ return false;
+ else
+ return m_CornersList[m_CornersList.GetCornersCount() - 1].end_contour;
+}
+
+
+// Creates hatch lines inside the outline of the complex polygon
+//
+// sort function used in ::Hatch to sort points by descending wxPoint.x values
+bool sort_ends_by_descending_X( const wxPoint& ref, const wxPoint& tst )
+{
+ return tst.x < ref.x;
+}
+
+
+void CPolyLine::Hatch()
+{
+ m_HatchLines.clear();
+
+ if( m_hatchStyle == NO_HATCH || m_hatchPitch == 0 )
+ return;
+
+ if( !GetClosed() ) // If not closed, the poly is beeing created and not finalised. Not not hatch
+ return;
+
+ // define range for hatch lines
+ int min_x = m_CornersList[0].x;
+ int max_x = m_CornersList[0].x;
+ int min_y = m_CornersList[0].y;
+ int max_y = m_CornersList[0].y;
+
+ for( unsigned ic = 1; ic < m_CornersList.GetCornersCount(); ic++ )
+ {
+ if( m_CornersList[ic].x < min_x )
+ min_x = m_CornersList[ic].x;
+
+ if( m_CornersList[ic].x > max_x )
+ max_x = m_CornersList[ic].x;
+
+ if( m_CornersList[ic].y < min_y )
+ min_y = m_CornersList[ic].y;
+
+ if( m_CornersList[ic].y > max_y )
+ max_y = m_CornersList[ic].y;
+ }
+
+ // Calculate spacing between 2 hatch lines
+ int spacing;
+
+ if( m_hatchStyle == DIAGONAL_EDGE )
+ spacing = m_hatchPitch;
+ else
+ spacing = m_hatchPitch * 2;
+
+ // set the "length" of hatch lines (the lenght on horizontal axis)
+ double hatch_line_len = m_hatchPitch;
+
+ // To have a better look, give a slope depending on the layer
+ LAYER_NUM layer = GetLayer();
+ int slope_flag = (layer & 1) ? 1 : -1; // 1 or -1
+ double slope = 0.707106 * slope_flag; // 45 degrees slope
+ int max_a, min_a;
+
+ if( slope_flag == 1 )
+ {
+ max_a = KiROUND( max_y - slope * min_x );
+ min_a = KiROUND( min_y - slope * max_x );
+ }
+ else
+ {
+ max_a = KiROUND( max_y - slope * max_x );
+ min_a = KiROUND( min_y - slope * min_x );
+ }
+
+ min_a = (min_a / spacing) * spacing;
+
+ // calculate an offset depending on layer number,
+ // for a better look of hatches on a multilayer board
+ int offset = (layer * 7) / 8;
+ min_a += offset;
+
+ // now calculate and draw hatch lines
+ int nc = m_CornersList.GetCornersCount();
+
+ // loop through hatch lines
+ #define MAXPTS 200 // Usually we store only few values per one hatch line
+ // depending on the compexity of the zone outline
+
+ static std::vector <wxPoint> pointbuffer;
+ pointbuffer.clear();
+ pointbuffer.reserve( MAXPTS + 2 );
+
+ for( int a = min_a; a < max_a; a += spacing )
+ {
+ // get intersection points for this hatch line
+
+ // Note: because we should have an even number of intersections with the
+ // current hatch line and the zone outline (a closed polygon,
+ // or a set of closed polygons), if an odd count is found
+ // we skip this line (should not occur)
+ pointbuffer.clear();
+ int i_start_contour = 0;
+
+ for( int ic = 0; ic<nc; ic++ )
+ {
+ double x, y, x2, y2;
+ int ok;
+
+ if( m_CornersList[ic].end_contour ||
+ ( ic == (int) (m_CornersList.GetCornersCount() - 1) ) )
+ {
+ ok = FindLineSegmentIntersection( a, slope,
+ m_CornersList[ic].x, m_CornersList[ic].y,
+ m_CornersList[i_start_contour].x,
+ m_CornersList[i_start_contour].y,
+ &x, &y, &x2, &y2 );
+ i_start_contour = ic + 1;
+ }
+ else
+ {
+ ok = FindLineSegmentIntersection( a, slope,
+ m_CornersList[ic].x, m_CornersList[ic].y,
+ m_CornersList[ic + 1].x, m_CornersList[ic + 1].y,
+ &x, &y, &x2, &y2 );
+ }
+
+ if( ok )
+ {
+ wxPoint point( KiROUND( x ), KiROUND( y ) );
+ pointbuffer.push_back( point );
+ }
+
+ if( ok == 2 )
+ {
+ wxPoint point( KiROUND( x2 ), KiROUND( y2 ) );
+ pointbuffer.push_back( point );
+ }
+
+ if( pointbuffer.size() >= MAXPTS ) // overflow
+ {
+ wxASSERT( 0 );
+ break;
+ }
+ }
+
+ // ensure we have found an even intersection points count
+ // because intersections are the ends of segments
+ // inside the polygon(s) and a segment has 2 ends.
+ // if not, this is a strange case (a bug ?) so skip this hatch
+ if( pointbuffer.size() % 2 != 0 )
+ continue;
+
+ // sort points in order of descending x (if more than 2) to
+ // ensure the starting point and the ending point of the same segment
+ // are stored one just after the other.
+ if( pointbuffer.size() > 2 )
+ sort( pointbuffer.begin(), pointbuffer.end(), sort_ends_by_descending_X );
+
+ // creates lines or short segments inside the complex polygon
+ for( unsigned ip = 0; ip < pointbuffer.size(); ip += 2 )
+ {
+ double dx = pointbuffer[ip + 1].x - pointbuffer[ip].x;
+
+ // Push only one line for diagonal hatch,
+ // or for small lines < twice the line len
+ // else push 2 small lines
+ if( m_hatchStyle == DIAGONAL_FULL || fabs( dx ) < 2 * hatch_line_len )
+ {
+ m_HatchLines.push_back( CSegment( pointbuffer[ip], pointbuffer[ip + 1] ) );
+ }
+ else
+ {
+ double dy = pointbuffer[ip + 1].y - pointbuffer[ip].y;
+ double slope = dy / dx;
+
+ if( dx > 0 )
+ dx = hatch_line_len;
+ else
+ dx = -hatch_line_len;
+
+ double x1 = pointbuffer[ip].x + dx;
+ double x2 = pointbuffer[ip + 1].x - dx;
+ double y1 = pointbuffer[ip].y + dx * slope;
+ double y2 = pointbuffer[ip + 1].y - dx * slope;
+
+ m_HatchLines.push_back( CSegment( pointbuffer[ip].x,
+ pointbuffer[ip].y,
+ KiROUND( x1 ), KiROUND( y1 ) ) );
+
+ m_HatchLines.push_back( CSegment( pointbuffer[ip + 1].x,
+ pointbuffer[ip + 1].y,
+ KiROUND( x2 ), KiROUND( y2 ) ) );
+ }
+ }
+ }
+}
+
+
+// test to see if a point is inside polyline
+//
+bool CPolyLine::TestPointInside( int px, int py )
+{
+ if( !GetClosed() )
+ {
+ wxASSERT( 0 );
+ }
+
+ // Test all polygons.
+ // Since the first is the main outline, and other are holes,
+ // if the tested point is inside only one contour, it is inside the whole polygon
+ // (in fact inside the main outline, and outside all holes).
+ // if inside 2 contours (the main outline + an hole), it is outside the poly.
+ int polycount = GetContoursCount();
+ bool inside = false;
+
+ for( int icont = 0; icont < polycount; icont++ )
+ {
+ int istart = GetContourStart( icont );
+ int iend = GetContourEnd( icont );
+
+ // test point inside the current polygon
+ if( TestPointInsidePolygon( m_CornersList, istart, iend, px, py ) )
+ inside = not inside;
+ }
+
+ return inside;
+}
+
+
+// copy data from another CPolyLine, but don't draw it
+void CPolyLine::Copy( const CPolyLine* src )
+{
+ UnHatch();
+ m_layer = src->m_layer;
+ m_hatchStyle = src->m_hatchStyle;
+ m_hatchPitch = src->m_hatchPitch;
+ m_flags = src->m_flags;
+ m_CornersList.RemoveAllContours();
+ m_CornersList.Append( src->m_CornersList );
+}
+
+
+/*
+ * return true if the corner aCornerIdx is on a hole inside the main outline
+ * and false if it is on the main outline
+ */
+bool CPolyLine::IsCutoutContour( int aCornerIdx )
+{
+ int ncont = GetContour( aCornerIdx );
+
+ if( ncont == 0 ) // the first contour is the main outline, not an hole
+ return false;
+
+ return true;
+}
+
+
+void CPolyLine::MoveOrigin( int x_off, int y_off )
+{
+ UnHatch();
+
+ for( int ic = 0; ic < GetCornersCount(); ic++ )
+ {
+ SetX( ic, GetX( ic ) + x_off );
+ SetY( ic, GetY( ic ) + y_off );
+ }
+
+ Hatch();
+}
+
+/*
+ * AppendArc:
+ * adds segments to current contour to approximate the given arc
+ */
+void CPolyLine::AppendArc( int xi, int yi, int xf, int yf, int xc, int yc, int num )
+{
+ // get radius
+ double radius = ::Distance( xi, yi, xf, yf );
+
+ // get angles of start pint and end point
+ double th_i = atan2( (double) (yi - yc), (double) (xi - xc) );
+ double th_f = atan2( (double) (yf - yc), (double) (xf - xc) );
+ double th_d = (th_f - th_i) / (num - 1);
+ double theta = th_i;
+
+ // generate arc
+ for( int ic = 0; ic < num; ic++ )
+ {
+ int x = xc + KiROUND( radius * cos( theta ) );
+ int y = yc + KiROUND( radius * sin( theta ) );
+ AppendCorner( x, y );
+ theta += th_d;
+ }
+
+ CloseLastContour();
+}
+
+
+// Bezier Support
+void CPolyLine::AppendBezier( int x1, int y1, int x2, int y2, int x3, int y3 )
+{
+ std::vector<wxPoint> bezier_points;
+
+ bezier_points = Bezier2Poly( x1, y1, x2, y2, x3, y3 );
+
+ for( unsigned int i = 0; i < bezier_points.size(); i++ )
+ AppendCorner( bezier_points[i].x, bezier_points[i].y );
+}
+
+
+void CPolyLine::AppendBezier( int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4 )
+{
+ std::vector<wxPoint> bezier_points;
+
+ bezier_points = Bezier2Poly( x1, y1, x2, y2, x3, y3, x4, y4 );
+
+ for( unsigned int i = 0; i < bezier_points.size(); i++ )
+ AppendCorner( bezier_points[i].x, bezier_points[i].y );
+}
+
+
+/*
+ * Function Distance
+ * Calculates the distance between a segment and a polygon (with holes):
+ * param aStart is the starting point of the segment.
+ * param aEnd is the ending point of the segment.
+ * param aWidth is the width of the segment.
+ * return distance between the segment and outline.
+ * 0 if segment intersects or is inside
+ */
+int CPolyLine::Distance( wxPoint aStart, wxPoint aEnd, int aWidth )
+{
+ // We calculate the min dist between the segment and each outline segment
+ // However, if the segment to test is inside the outline, and does not cross
+ // any edge, it can be seen outside the polygon.
+ // Therefore test if a segment end is inside ( testing only one end is enough )
+ if( TestPointInside( aStart.x, aStart.y ) )
+ return 0;
+
+ int distance = INT_MAX;
+ int polycount = GetContoursCount();
+
+ for( int icont = 0; icont < polycount; icont++ )
+ {
+ int ic_start = GetContourStart( icont );
+ int ic_end = GetContourEnd( icont );
+
+ // now test spacing between area outline and segment
+ for( int ic2 = ic_start; ic2 <= ic_end; ic2++ )
+ {
+ int bx1 = GetX( ic2 );
+ int by1 = GetY( ic2 );
+ int bx2, by2;
+
+ if( ic2 == ic_end )
+ {
+ bx2 = GetX( ic_start );
+ by2 = GetY( ic_start );
+ }
+ else
+ {
+ bx2 = GetX( ic2 + 1 );
+ by2 = GetY( ic2 + 1 );
+ }
+
+ int d = GetClearanceBetweenSegments( bx1, by1, bx2, by2, 0,
+ aStart.x, aStart.y, aEnd.x, aEnd.y,
+ aWidth,
+ 1, // min clearance, should be > 0
+ NULL, NULL );
+
+ if( distance > d )
+ distance = d;
+
+ if( distance <= 0 )
+ return 0;
+ }
+ }
+
+ return distance;
+}
+
+
+/*
+ * Function Distance
+ * Calculates the distance between a point and polygon (with holes):
+ * param aPoint is the coordinate of the point.
+ * return distance between the point and outline.
+ * 0 if the point is inside
+ */
+int CPolyLine::Distance( const wxPoint& aPoint )
+{
+ // We calculate the dist between the point and each outline segment
+ // If the point is inside the outline, the dist is 0.
+ if( TestPointInside( aPoint.x, aPoint.y ) )
+ return 0;
+
+ int distance = INT_MAX;
+ int polycount = GetContoursCount();
+
+ for( int icont = 0; icont < polycount; icont++ )
+ {
+ int ic_start = GetContourStart( icont );
+ int ic_end = GetContourEnd( icont );
+
+ // now test spacing between area outline and segment
+ for( int ic2 = ic_start; ic2 <= ic_end; ic2++ )
+ {
+ int bx1 = GetX( ic2 );
+ int by1 = GetY( ic2 );
+ int bx2, by2;
+
+ if( ic2 == ic_end )
+ {
+ bx2 = GetX( ic_start );
+ by2 = GetY( ic_start );
+ }
+ else
+ {
+ bx2 = GetX( ic2 + 1 );
+ by2 = GetY( ic2 + 1 );
+ }
+
+ int d = KiROUND( GetPointToLineSegmentDistance( aPoint.x, aPoint.y,
+ bx1, by1, bx2, by2 ) );
+
+ if( distance > d )
+ distance = d;
+
+ if( distance <= 0 )
+ return 0;
+ }
+ }
+
+ return distance;
+}
+
+
+/* test is the point aPos is near (< aDistMax ) a vertex
+ * return int = the index of the first corner of the vertex, or -1 if not found.
+ */
+int CPolyLine::HitTestForEdge( const wxPoint& aPos, int aDistMax ) const
+{
+ unsigned lim = m_CornersList.GetCornersCount();
+ int corner = -1; // Set to not found
+ unsigned first_corner_pos = 0;
+
+ for( unsigned item_pos = 0; item_pos < lim; item_pos++ )
+ {
+ unsigned end_segm = item_pos + 1;
+
+ /* the last corner of the current outline is tested
+ * the last segment of the current outline starts at current corner, and ends
+ * at the first corner of the outline
+ */
+ if( m_CornersList.IsEndContour ( item_pos ) || end_segm >= lim )
+ {
+ unsigned tmp = first_corner_pos;
+ first_corner_pos = end_segm; // first_corner_pos is now the beginning of the next outline
+ end_segm = tmp; // end_segm is the beginning of the current outline
+ }
+
+ // test the dist between segment and ref point
+ int dist = KiROUND( GetPointToLineSegmentDistance(
+ aPos.x, aPos.y,
+ m_CornersList.GetX( item_pos ),
+ m_CornersList.GetY( item_pos ),
+ m_CornersList.GetX( end_segm ),
+ m_CornersList.GetY( end_segm ) ) );
+
+ if( dist < aDistMax )
+ {
+ corner = item_pos;
+ aDistMax = dist;
+ }
+ }
+
+ return corner;
+}
+
+/* test is the point aPos is near (< aDistMax ) a corner
+ * return int = the index of corner of the, or -1 if not found.
+ */
+int CPolyLine::HitTestForCorner( const wxPoint& aPos, int aDistMax ) const
+{
+ int corner = -1; // Set to not found
+ wxPoint delta;
+ unsigned lim = m_CornersList.GetCornersCount();
+
+ for( unsigned item_pos = 0; item_pos < lim; item_pos++ )
+ {
+ delta.x = aPos.x - m_CornersList.GetX( item_pos );
+ delta.y = aPos.y - m_CornersList.GetY( item_pos );
+
+ // Calculate a distance:
+ int dist = std::max( abs( delta.x ), abs( delta.y ) );
+
+ if( dist < aDistMax ) // this corner is a candidate:
+ {
+ corner = item_pos;
+ aDistMax = dist;
+ }
+ }
+
+ return corner;
+}
+
+
+/**
+ * Function IsPolygonSelfIntersecting
+ * Test a CPolyLine for self-intersection of vertex (all contours).
+ *
+ * @return :
+ * false if no intersecting sides
+ * true if intersecting sides
+ * When a CPolyLine is self intersectic, it need to be normalized.
+ * (converted to non intersecting polygons)
+ */
+bool CPolyLine::IsPolygonSelfIntersecting()
+{
+ // first, check for sides intersecting other sides
+ int n_cont = GetContoursCount();
+
+ // make bounding rect for each contour
+ std::vector<EDA_RECT> cr;
+ cr.reserve( n_cont );
+
+ for( int icont = 0; icont<n_cont; icont++ )
+ cr.push_back( GetBoundingBox( icont ) );
+
+ for( int icont = 0; icont<n_cont; icont++ )
+ {
+ int is_start = GetContourStart( icont );
+ int is_end = GetContourEnd( icont );
+
+ for( int is = is_start; is<=is_end; is++ )
+ {
+ int is_prev = is - 1;
+
+ if( is_prev < is_start )
+ is_prev = is_end;
+
+ int is_next = is + 1;
+
+ if( is_next > is_end )
+ is_next = is_start;
+
+ int x1i = GetX( is );
+ int y1i = GetY( is );
+ int x1f = GetX( is_next );
+ int y1f = GetY( is_next );
+
+ // check for intersection with any other sides
+ for( int icont2 = icont; icont2 < n_cont; icont2++ )
+ {
+ if( !cr[icont].Intersects( cr[icont2] ) )
+ {
+ // rectangles don't overlap, do nothing
+ }
+ else
+ {
+ int is2_start = GetContourStart( icont2 );
+ int is2_end = GetContourEnd( icont2 );
+
+ for( int is2 = is2_start; is2<=is2_end; is2++ )
+ {
+ int is2_prev = is2 - 1;
+
+ if( is2_prev < is2_start )
+ is2_prev = is2_end;
+
+ int is2_next = is2 + 1;
+
+ if( is2_next > is2_end )
+ is2_next = is2_start;
+
+ if( icont != icont2
+ || ( is2 != is && is2 != is_prev && is2 != is_next &&
+ is != is2_prev && is != is2_next )
+ )
+ {
+ int x2i = GetX( is2 );
+ int y2i = GetY( is2 );
+ int x2f = GetX( is2_next );
+ int y2f = GetY( is2_next );
+ int ret = FindSegmentIntersections( x1i, y1i, x1f, y1f,
+ x2i, y2i, x2f, y2f );
+ if( ret )
+ {
+ // intersection between non-adjacent sides
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+const SHAPE_POLY_SET ConvertPolyListToPolySet( const CPOLYGONS_LIST& aList )
+{
+ SHAPE_POLY_SET rv;
+
+ unsigned corners_count = aList.GetCornersCount();
+
+ // Enter main outline: this is the first contour
+ unsigned ic = 0;
+
+ if( !corners_count )
+ return rv;
+
+ int index = 0;
+
+ while( ic < corners_count )
+ {
+ int hole = -1;
+
+ if( index == 0 )
+ {
+ rv.NewOutline();
+ hole = -1;
+ }
+ else
+ {
+ hole = rv.NewHole();
+ }
+
+ while( ic < corners_count )
+ {
+ rv.Append( aList.GetX( ic ), aList.GetY( ic ), 0, hole );
+
+ if( aList.IsEndContour( ic ) )
+ break;
+
+ ic++;
+ }
+ ic++;
+
+ index++;
+ }
+
+ return rv;
+}
+
+
+const CPOLYGONS_LIST ConvertPolySetToPolyList(const SHAPE_POLY_SET& aPolyset)
+{
+ CPOLYGONS_LIST list;
+ CPolyPt corner, firstCorner;
+
+ const SHAPE_POLY_SET::POLYGON& poly = aPolyset.CPolygon( 0 );
+
+ for( unsigned int jj = 0; jj < poly.size() ; jj++ )
+ {
+ const SHAPE_LINE_CHAIN& path = poly[jj];
+
+ for( int i = 0; i < path.PointCount(); i++ )
+ {
+ const VECTOR2I &v = path.CPoint( i );
+
+ corner.x = v.x;
+ corner.y = v.y;
+ corner.end_contour = false;
+
+ if( i == 0 )
+ firstCorner = corner;
+
+ list.AddCorner( corner );
+ }
+
+ firstCorner.end_contour = true;
+ list.AddCorner( firstCorner );
+ }
+
+ return list;
+}
diff --git a/polygon/PolyLine.h b/polygon/PolyLine.h
new file mode 100644
index 0000000..b60cd37
--- /dev/null
+++ b/polygon/PolyLine.h
@@ -0,0 +1,490 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Few parts of this code come from FreePCB, released under the GNU General Public License V2.
+ * (see http://www.freepcb.com/ )
+ *
+ * Copyright (C) 2012-2015 Jean-Pierre Charras, jp.charras at wanadoo.fr
+ * Copyright (C) 2008-2013 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
+ * Copyright (C) 2008-2013 Wayne Stambaugh <stambaughw@verizon.net>
+ * Copyright (C) 2012-2015 KiCad Developers, see CHANGELOG.TXT for contributors.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+
+/**
+ * @file PolyLine.h
+ * @note definition of CPolyLine class
+ */
+
+//
+// A polyline contains one or more contours, where each contour
+// is defined by a list of corners and side-styles
+// There may be multiple contours in a polyline.
+// The last contour may be open or closed, any others must be closed.
+// All of the corners and side-styles are concatenated into 2 arrays,
+// separated by setting the end_contour flag of the last corner of
+// each contour.
+//
+// When used for copper (or technical layers) areas, the first contour is the outer edge
+// of the area, subsequent ones are "holes" in the copper.
+
+#ifndef POLYLINE_H
+#define POLYLINE_H
+
+#include <vector>
+
+#include <wx/gdicmn.h> // for wxPoint definition
+#include <layers_id_colors_and_visibility.h> // for LAYER_NUM definition
+#include <class_eda_rect.h> // for EDA_RECT definition
+
+#include <geometry/shape_poly_set.h> // fixme
+
+class CSegment
+{
+public:
+ wxPoint m_Start;
+ wxPoint m_End;
+
+ CSegment() { };
+ CSegment( const wxPoint& aStart, const wxPoint& aEnd )
+ {
+ m_Start = aStart;
+ m_End = aEnd;
+ }
+
+ CSegment( int x0, int y0, int x1, int y1 )
+ {
+ m_Start.x = x0; m_Start.y = y0;
+ m_End.x = x1; m_End.y = y1;
+ }
+};
+
+class CPolyPt : public wxPoint
+{
+public:
+ CPolyPt( int aX = 0, int aY = 0, bool aEnd = false, int aUtility = 0 ) :
+ wxPoint( aX, aY ), end_contour( aEnd ), m_flags( aUtility )
+ {}
+
+ // / Pure copy constructor is here to dis-ambiguate from the
+ // / specialized CPolyPt( const wxPoint& ) constructor version below.
+ CPolyPt( const CPolyPt& aPt ) :
+ wxPoint( aPt.x, aPt.y ), end_contour( aPt.end_contour ), m_flags( aPt.m_flags )
+ {}
+
+ CPolyPt( const wxPoint& aPoint ) :
+ wxPoint( aPoint ), end_contour( false ), m_flags( 0 )
+ {}
+
+
+ bool end_contour;
+ int m_flags;
+
+ bool operator ==( const CPolyPt& cpt2 ) const
+ { return (x == cpt2.x) && (y == cpt2.y) && (end_contour == cpt2.end_contour); }
+
+ bool operator !=( CPolyPt& cpt2 ) const
+ { return (x != cpt2.x) || (y != cpt2.y) || (end_contour != cpt2.end_contour); }
+};
+
+/**
+ * CPOLYGONS_LIST handle a list of contours (polygons corners).
+ * Each corner is a CPolyPt item.
+ * The last cornet of each contour has its end_contour member = true
+ */
+class CPOLYGONS_LIST
+{
+private:
+ std::vector <CPolyPt> m_cornersList; // array of points for corners
+public:
+ CPOLYGONS_LIST() {};
+
+ CPolyPt& operator [](int aIdx) { return m_cornersList[aIdx]; }
+
+ // Accessor:
+ const std::vector <CPolyPt>& GetList() const {return m_cornersList;}
+
+ int GetX( int ic ) const { return m_cornersList[ic].x; }
+ void SetX( int ic, int aValue ) { m_cornersList[ic].x = aValue; }
+ int GetY( int ic ) const { return m_cornersList[ic].y; }
+ void SetY( int ic, int aValue ) { m_cornersList[ic].y = aValue; }
+
+ bool IsEndContour( int ic ) const
+ {
+ return m_cornersList[ic].end_contour;
+ }
+
+ const wxPoint& GetPos( int ic ) const { return m_cornersList[ic]; }
+ const CPolyPt& GetCorner( int ic ) const { return m_cornersList[ic]; }
+
+ // vector <> methods
+ void reserve( int aSize ) { m_cornersList.reserve( aSize ); }
+
+
+ void RemoveAllContours( void ) { m_cornersList.clear(); }
+ CPolyPt& GetLastCorner() { return m_cornersList.back(); }
+
+ unsigned GetCornersCount() const { return m_cornersList.size(); }
+
+ void DeleteCorner( int aIdx )
+ {
+ m_cornersList.erase( m_cornersList.begin() + aIdx );
+ }
+
+ // used only to erase an entire polygon
+ void DeleteCorners( int aIdFirstCorner, int aIdLastCorner )
+ {
+ m_cornersList.erase( m_cornersList.begin() + aIdFirstCorner,
+ m_cornersList.begin() + aIdLastCorner + 1 );
+ }
+
+ void Append( const CPOLYGONS_LIST& aList )
+ {
+ m_cornersList.insert( m_cornersList.end(),
+ aList.m_cornersList.begin(),
+ aList.m_cornersList.end() );
+ }
+
+ void Append( const CPolyPt& aItem )
+ {
+ m_cornersList.push_back( aItem );
+ }
+
+ void Append( const wxPoint& aItem )
+ {
+ CPolyPt item( aItem );
+
+ m_cornersList.push_back( aItem );
+ }
+
+ void InsertCorner( int aPosition, const CPolyPt& aItem )
+ {
+ m_cornersList.insert( m_cornersList.begin() + aPosition + 1, aItem );
+ }
+
+ /**
+ * function AddCorner
+ * add a corner to the list
+ */
+ void AddCorner( const CPolyPt& aCorner )
+ {
+ m_cornersList.push_back( aCorner );
+ }
+
+ /**
+ * function CloseLastContour
+ * Set the .end_contour member of the last corner in list to true
+ */
+ void CloseLastContour()
+ {
+ if( m_cornersList.size() > 0 )
+ m_cornersList.back().end_contour = true;
+ }
+
+ /**
+ * Function GetContoursCount.
+ * @return the number of polygons stored in list
+ * (number of corners flagged "end_contour"
+ */
+ int GetContoursCount() const;
+};
+
+class CPolyLine
+{
+public:
+ enum HATCH_STYLE { NO_HATCH, DIAGONAL_FULL, DIAGONAL_EDGE }; // hatch styles
+
+ // constructors/destructor
+ CPolyLine();
+ CPolyLine( const CPolyLine& aCPolyLine);
+ ~CPolyLine();
+
+ /**
+ * Function ImportSettings
+ * Copy settings (layer, hatch styles) from aPoly
+ * @param aPoly is the CPolyLine to import settings
+ */
+ void ImportSettings( const CPolyLine* aPoly );
+
+ // functions for modifying the CPolyLine contours
+
+ /* initialize a contour
+ * set layer, hatch style, and starting point
+ */
+ void Start( LAYER_NUM layer, int x, int y, int hatch );
+
+ void AppendCorner( int x, int y );
+ void InsertCorner( int ic, int x, int y );
+
+ /**
+ * Function DeleteCorner
+ * remove the given corner. if it is the last point of a contour
+ * keep the controur closed by modifying the previous corner
+ * @param ic = the index of the corner to delete
+ */
+ void DeleteCorner( int ic );
+ void MoveCorner( int ic, int x, int y );
+
+ /**
+ * function CloseLastContour
+ * Set the .end_contour member of the last corner
+ * of the last contour to true
+ */
+ void CloseLastContour()
+ {
+ m_CornersList.CloseLastContour();
+ }
+
+ void RemoveContour( int icont );
+
+ /**
+ * Function IsPolygonSelfIntersecting
+ * Test a CPolyLine for self-intersection of vertex (all contours).
+ *
+ * @return :
+ * false if no intersecting sides
+ * true if intersecting sides
+ * When a CPolyLine is self intersectic, it need to be normalized.
+ * (converted to non intersecting polygons)
+ */
+ bool IsPolygonSelfIntersecting();
+
+ /**
+ * Function Chamfer
+ * returns a chamfered version of a polygon.
+ * @param aDistance is the chamfering distance.
+ * @return CPolyLine* - Pointer to new polygon.
+ */
+ CPolyLine* Chamfer( unsigned int aDistance );
+
+ /**
+ * Function Fillet
+ * returns a filleted version of a polygon.
+ * @param aRadius is the fillet radius.
+ * @param aSegments is the number of segments / fillet.
+ * @return CPolyLine* - Pointer to new polygon.
+ */
+ CPolyLine* Fillet( unsigned int aRadius, unsigned int aSegments );
+
+ /**
+ * Function RemoveNullSegments
+ * Removes corners which create a null segment edge
+ * (i.e. when 2 successive corners are at the same location)
+ * @return the count of removed corners.
+ */
+ int RemoveNullSegments();
+
+ void RemoveAllContours( void );
+
+ // Remove or create hatch
+ void UnHatch();
+ void Hatch();
+
+ // Transform functions
+ void MoveOrigin( int x_off, int y_off );
+
+ // misc. functions
+ /**
+ * @return the full bounding box of polygons
+ */
+ const EDA_RECT GetBoundingBox();
+
+ /**
+ * @return the bounding box of a given polygon
+ * @param icont = the index of the polygon contour
+ * (0 = main contour, 1 ... n = other contours, usually holes)
+ */
+ const EDA_RECT GetBoundingBox( int icont );
+
+ void Copy( const CPolyLine* src );
+ bool TestPointInside( int x, int y );
+
+ /**
+ * @return true if the corner aCornerIdx is on a hole inside the main outline
+ * and false if it is on the main outline
+ */
+ bool IsCutoutContour( int aCornerIdx );
+
+ /**
+ * Function AppendArc.
+ * Adds segments to current contour to approximate the given arc
+ */
+ void AppendArc( int xi, int yi, int xf, int yf, int xc, int yc, int num );
+
+ // access functions
+ void SetLayer( LAYER_NUM aLayer ) { m_layer = aLayer; }
+ LAYER_NUM GetLayer() const { return m_layer; }
+
+ int GetCornersCount() const
+ {
+ return m_CornersList.GetCornersCount();
+ }
+
+ /**
+ * @return true if the last corner in corners list is flagged end_contour
+ */
+ bool GetClosed();
+
+ /**
+ * Function GetContoursCount.
+ * @return the number of polygons stored in list
+ * (number of corners flagged "end_contour"
+ */
+ int GetContoursCount() const;
+
+ /**
+ * Function GetContour.
+ * @return the contour number containing the corner ic
+ * @param ic = the index of the corner in the corner list
+ */
+ int GetContour( int ic );
+
+ /**
+ * Function GetContourStart.
+ * @return the index of the first corner (in corners list) of a contour
+ * @param icont = the index of the contour
+ */
+ int GetContourStart( int icont );
+
+ /**
+ * Function GetContourEnd.
+ * @return the index of the last corner (in corners list) of a contour
+ * @param icont = the index of the contour
+ */
+ int GetContourEnd( int icont );
+
+ /**
+ * Function GetContourSize.
+ * @return the corners count of a contour
+ * @param icont = the index of the contour
+ */
+ int GetContourSize( int icont );
+
+ int GetX( int ic ) const { return m_CornersList.GetX( ic ); }
+ int GetY( int ic ) const { return m_CornersList.GetY( ic ); }
+
+ /**
+ * Function IsEndContour.
+ * @return true if a corner is flagged end_contour
+ * @param ic= the index (in corners list) of the corner
+ */
+ bool IsEndContour( int ic ) const { return m_CornersList.IsEndContour( ic ); }
+
+ const wxPoint& GetPos( int ic ) const { return m_CornersList.GetPos( ic ); }
+
+ int GetHatchPitch() const { return m_hatchPitch; }
+ static int GetDefaultHatchPitchMils() { return 20; } // default hatch pitch value in mils
+
+ enum HATCH_STYLE GetHatchStyle() const { return m_hatchStyle; }
+ void SetHatch( int aHatchStyle, int aHatchPitch, bool aRebuildHatch )
+ {
+ SetHatchPitch( aHatchPitch );
+ m_hatchStyle = (enum HATCH_STYLE) aHatchStyle;
+
+ if( aRebuildHatch )
+ Hatch();
+ }
+
+ void SetX( int ic, int x )
+ {
+ m_CornersList.SetX( ic, x );
+ }
+
+ void SetY( int ic, int y )
+ {
+ m_CornersList.SetY( ic, y );
+ }
+
+ void SetHatchStyle( enum HATCH_STYLE style )
+ {
+ m_hatchStyle = style;
+ }
+
+ void SetHatchPitch( int pitch ) { m_hatchPitch = pitch; }
+
+ /**
+ * Function NormalizeAreaOutlines
+ * Convert a self-intersecting polygon to one (or more) non self-intersecting polygon(s)
+ * Removes null segments.
+ * @param aNewPolygonList = a std::vector<CPolyLine*> reference where to store new CPolyLine
+ * needed by the normalization
+ * @return the polygon count (always >= 1, because there is at least one polygon)
+ * There are new polygons only if the polygon count is > 1
+ */
+ int NormalizeAreaOutlines( std::vector<CPolyLine*>* aNewPolygonList );
+
+ // Bezier Support
+ void AppendBezier( int x1, int y1, int x2, int y2, int x3, int y3 );
+ void AppendBezier( int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4 );
+
+ /**
+ * Function Distance
+ * Calculates the distance between a point and the zone:
+ * @param aPoint the coordinate of the point.
+ * @return int = distance between the point and outline.
+ * 0 if the point is inside
+ */
+ int Distance( const wxPoint& aPoint );
+
+ /**
+ * Function Distance
+ * Calculates the distance between a segment and the zone:
+ * @param aStart the starting point of the segment.
+ * @param aEnd the ending point of the segment.
+ * @param aWidth the width of the segment.
+ * @return int = distance between the segment and outline.
+ * 0 if segment intersects or is inside
+ */
+ int Distance( wxPoint aStart, wxPoint aEnd, int aWidth );
+
+ /**
+ * Function HitTestForEdge
+ * test is the point aPos is near (< aDistMax ) a vertex
+ * @param aPos = the reference point
+ * @param aDistMax = the max distance between a vertex and the reference point
+ * @return int = the index of the first corner of the vertex, or -1 if not found.
+ */
+ int HitTestForEdge( const wxPoint& aPos, int aDistMax ) const;
+
+ /**
+ * Function HitTestForCorner
+ * test is the point aPos is near (< aDistMax ) a corner
+ * @param aPos = the reference point
+ * @param aDistMax = the max distance between a vertex and the corner
+ * @return int = the index of corner of the, or -1 if not found.
+ */
+ int HitTestForCorner( const wxPoint& aPos, int aDistMax ) const;
+
+private:
+ LAYER_NUM m_layer; // layer to draw on
+ enum HATCH_STYLE m_hatchStyle; // hatch style, see enum above
+ int m_hatchPitch; // for DIAGONAL_EDGE hatched outlines, basic distance between 2 hatch lines
+ // and the len of eacvh segment
+ // for DIAGONAL_FULL, the pitch is twice this value
+ int m_flags; // a flag used in some calculations
+public:
+ CPOLYGONS_LIST m_CornersList; // array of points for corners
+ std::vector <CSegment> m_HatchLines; // hatch lines showing the polygon area
+};
+
+const SHAPE_POLY_SET ConvertPolyListToPolySet( const CPOLYGONS_LIST& aList );
+const CPOLYGONS_LIST ConvertPolySetToPolyList( const SHAPE_POLY_SET& aPolyset );
+
+#endif // #ifndef POLYLINE_H
diff --git a/polygon/SutherlandHodgmanClipPoly.h b/polygon/SutherlandHodgmanClipPoly.h
new file mode 100644
index 0000000..506ee0f
--- /dev/null
+++ b/polygon/SutherlandHodgmanClipPoly.h
@@ -0,0 +1,273 @@
+/********************************************************************************
+* Copyright (C) 2004 Sjaak Priester
+*
+* This is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This file is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with Tinter; if not, write to the Free Software
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+********************************************************************************/
+
+// SutherlandHodgman
+// Class to perform polygon clipping against an upright rectangular boundary window.
+// Implementation of Sutherland-Hodgman algorithm (1974).
+//
+// Version 1.0 (C) 2004, Sjaak Priester, Amsterdam.
+// mailto:sjaak@sjaakpriester.nl
+// http://www.sjaakpriester.nl
+
+#ifndef __SUTHERLAND_HODGMAN_H__
+#define __SUTHERLAND_HODGMAN_H__
+
+
+#include <vector>
+#include <functional>
+
+#ifndef _GDIPLUS_H
+
+// I designed this with GDI+ in mind. However, this particular code doesn't
+// use GDI+ at all, only some of it's variable types.
+// These definitions are substitutes for those of GDI+.
+typedef double REAL;
+class PointF
+{
+public:
+ REAL X;
+ REAL Y;
+
+ PointF() : X( 0 )
+ , Y( 0 ) { }
+ PointF( const PointF& p ) : X( p.X )
+ , Y( p.Y ) { }
+ PointF( REAL x, REAL y ) : X( x )
+ , Y( y ) { }
+ PointF operator+( const PointF& p ) const { return PointF( X + p.X, Y + p.Y ); }
+ PointF operator-( const PointF& p ) const { return PointF( X - p.X, Y - p.Y ); }
+ bool Equals( const PointF& p ) { return (X == p.X) && (Y == p.Y); }
+};
+
+class RectF
+{
+public:
+ REAL X;
+ REAL Y;
+ REAL Width;
+ REAL Height;
+
+ RectF() { X = 0, Y = 0, Height = 0, Width = 0; }
+ RectF( const RectF& r )
+ {
+ X = r.X; Y = r.Y; Height = r.Height, Width = r.Width;
+ }
+
+
+ RectF( REAL x, REAL y, REAL w, REAL h ) : X( x ), Y( y ),Width( w ), Height( h )
+ { }
+ REAL GetLeft() const { return X; }
+ REAL GetTop() const { return Y; }
+ REAL GetRight() const { return X + Width; }
+ REAL GetBottom() const { return Y + Height; }
+};
+
+#endif // _GDIPLUS_H
+
+typedef std::vector<PointF> pointVector;
+typedef std::vector<PointF>::iterator pointIterator;
+typedef std::vector<PointF>::const_iterator cpointIterator;
+
+class SutherlandHodgman
+{
+public:
+
+ // Constructor. Parameter is the boundary rectangle.
+ // SutherlandHodgman expects a 'normalized' boundary rectangle, meaning
+ // that boundaries.GetRight() > boundaries.GetLeft() and
+ // boundaries.GetBottom() > boundaries.GetTop().
+ // In other words: boundary.Width > 0 and boundaries.Height > 0.
+ // If this is violated, nothing will be output.
+ SutherlandHodgman( RectF& boundaries ) :
+ m_stageBottom( m_stageOut, boundaries.GetBottom() )
+ , /* Initialize each stage */ m_stageLeft( m_stageBottom, boundaries.GetLeft() )
+ , /* with its next stage and */ m_stageTop( m_stageLeft, boundaries.GetTop() )
+ , /* the boundary position. */ m_stageRight( m_stageTop, boundaries.GetRight() )
+ {
+ }
+
+
+ void Clip( pointVector& input, pointVector& clipped )
+ {
+ clipped.clear();
+ m_stageOut.SetDestination( &clipped );
+
+ // Clip each input vertex.
+ for( cpointIterator it = input.begin(); it != input.end(); ++it )
+ m_stageRight.HandleVertex( *it );
+
+ // Do the final step.
+ m_stageRight.Finalize();
+ }
+
+
+private:
+
+ // Implementation of a horizontal boundary (top or bottom).
+ // Comp is a std::binary_function object, comparing its two parameters, f.i. std::less.
+
+ template <class Comp>
+
+ class BoundaryHor
+ {
+public:
+ BoundaryHor( REAL y ) : m_Y( y ) { }
+ bool IsInside( const PointF& pnt ) const
+ {
+ return Comp ()( pnt.Y, m_Y );
+ } // return true if pnt.Y is at the inside of the boundary
+ PointF Intersect( const PointF& p0, const PointF& p1 ) const // return intersection point of line p0...p1 with boundary
+ { // assumes p0...p1 is not strictly horizontal
+ PointF d = p1 - p0;
+ REAL xslope = d.X / d.Y;
+
+ PointF r;
+
+ r.Y = m_Y;
+ r.X = p0.X + xslope * (m_Y - p0.Y);
+ return r;
+ }
+
+
+private:
+ REAL m_Y;
+ };
+
+ // Implementation of a vertical boundary (left or right).
+ template <class Comp>
+ class BoundaryVert
+ {
+public:
+ BoundaryVert( REAL x ) : m_X( x )
+ { }
+ bool IsInside( const PointF& pnt ) const
+ {
+ return Comp() ( pnt.X, m_X );
+ }
+ PointF Intersect( const PointF& p0, const PointF& p1 ) const // assumes p0...p1 is not strictly vertical
+ {
+ PointF d = p1 - p0;
+ REAL yslope = d.Y / d.X;
+
+ PointF r;
+
+ r.X = m_X;
+ r.Y = p0.Y + yslope * (m_X - p0.X);
+ return r;
+ }
+
+
+private:
+ REAL m_X;
+ };
+
+ // This template class is the workhorse of the algorithm. It handles the clipping against one boundary.
+ // Boundary is either BoundaryHor or BoundaryVert, Stage is the next ClipStage, or the output stage.
+ template <class Boundary, class Stage>
+ class ClipStage : private Boundary
+ {
+public:
+ ClipStage( Stage& nextStage, REAL position ) :
+ Boundary( position ) , m_NextStage( nextStage ), m_bFirst( true ), m_bPreviousInside( false )
+ { }
+
+ // Function to handle one vertex
+ void HandleVertex( const PointF& pntCurrent )
+ {
+ bool bCurrentInside = this->IsInside( pntCurrent ); // See if vertex is inside the boundary.
+
+ if( m_bFirst ) // If this is the first vertex...
+ {
+ m_pntFirst = pntCurrent; // ... just remember it,...
+
+ m_bFirst = false;
+ }
+ else // Common cases, not the first vertex.
+ {
+ if( bCurrentInside ) // If this vertex is inside...
+ {
+ if( !m_bPreviousInside ) // ... and the previous one was outside
+ m_NextStage.HandleVertex( this->Intersect( m_pntPrevious, pntCurrent ) );
+
+ // ... first output the intersection point.
+
+ m_NextStage.HandleVertex( pntCurrent ); // Output the current vertex.
+ }
+ else if( m_bPreviousInside ) // If this vertex is outside, and the previous one was inside...
+ m_NextStage.HandleVertex( this->Intersect( m_pntPrevious, pntCurrent ) );
+
+ // ... output the intersection point.
+
+ // If neither current vertex nor the previous one are inside, output nothing.
+ }
+ m_pntPrevious = pntCurrent; // Be prepared for next vertex.
+ m_bPreviousInside = bCurrentInside;
+ }
+
+
+ void Finalize()
+ {
+ HandleVertex( m_pntFirst ); // Close the polygon.
+ m_NextStage.Finalize(); // Delegate to the next stage.
+ }
+
+
+private:
+ Stage& m_NextStage; // the next stage
+ bool m_bFirst; // true if no vertices have been handled
+ PointF m_pntFirst; // the first vertex
+ PointF m_pntPrevious; // the previous vertex
+ bool m_bPreviousInside; // true if the previous vertex was inside the Boundary
+ };
+
+ class OutputStage
+ {
+public:
+ OutputStage() : m_pDest( 0 ) { }
+ void SetDestination( pointVector* pDest ) { m_pDest = pDest; }
+ void HandleVertex( const PointF& pnt ) { m_pDest->push_back( pnt ); } // Append the vertex to the output container.
+ void Finalize() { } // Do nothing.
+private:
+ pointVector* m_pDest;
+ };
+
+ // These typedefs define the four boundaries. In keeping up with the GDI/GDI+ interpretation of
+ // rectangles, we include the left and top boundaries, but not the right and bottom boundaries.
+ // In other words: a vertex on the left boundary is considered to be inside, but a vertex
+ // on the right boundary is considered to be outside.
+ typedef BoundaryVert<std::less<REAL> > BoundaryRight;
+ typedef BoundaryHor<std::greater_equal<REAL> > BoundaryTop;
+ typedef BoundaryVert<std::greater_equal<REAL> > BoundaryLeft;
+ typedef BoundaryHor<std::less<REAL> > BoundaryBottom;
+
+ // Next typedefs define the four stages. First template parameter is the boundary,
+ // second template parameter is the next stage.
+ typedef ClipStage<BoundaryBottom, OutputStage> ClipBottom;
+ typedef ClipStage<BoundaryLeft, ClipBottom> ClipLeft;
+ typedef ClipStage<BoundaryTop, ClipLeft> ClipTop;
+ typedef ClipStage<BoundaryRight, ClipTop> ClipRight;
+
+ // Our data members.
+ OutputStage m_stageOut;
+ ClipBottom m_stageBottom;
+ ClipLeft m_stageLeft;
+ ClipTop m_stageTop;
+ ClipRight m_stageRight;
+};
+
+#endif
diff --git a/polygon/clipper.cpp b/polygon/clipper.cpp
new file mode 100644
index 0000000..6d6ce6e
--- /dev/null
+++ b/polygon/clipper.cpp
@@ -0,0 +1,4642 @@
+/*******************************************************************************
+* *
+* Author : Angus Johnson *
+* Version : 6.4.0 *
+* Date : 2 July 2015 *
+* Website : http://www.angusj.com *
+* Copyright : Angus Johnson 2010-2015 *
+* *
+* License: *
+* Use, modification & distribution is subject to Boost Software License Ver 1. *
+* http://www.boost.org/LICENSE_1_0.txt *
+* *
+* Attributions: *
+* The code in this library is an extension of Bala Vatti's clipping algorithm: *
+* "A generic solution to polygon clipping" *
+* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. *
+* http://portal.acm.org/citation.cfm?id=129906 *
+* *
+* Computer graphics and geometric modeling: implementation and algorithms *
+* By Max K. Agoston *
+* Springer; 1 edition (January 4, 2005) *
+* http://books.google.com/books?q=vatti+clipping+agoston *
+* *
+* See also: *
+* "Polygon Offsetting by Computing Winding Numbers" *
+* Paper no. DETC2005-85513 pp. 565-575 *
+* ASME 2005 International Design Engineering Technical Conferences *
+* and Computers and Information in Engineering Conference (IDETC/CIE2005) *
+* September 24-28, 2005 , Long Beach, California, USA *
+* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf *
+* *
+*******************************************************************************/
+
+/*******************************************************************************
+* *
+* This is a translation of the Delphi Clipper library and the naming style *
+* used has retained a Delphi flavour. *
+* *
+*******************************************************************************/
+
+#include "clipper.hpp"
+#include <cmath>
+#include <vector>
+#include <algorithm>
+#include <stdexcept>
+#include <cstring>
+#include <cstdlib>
+#include <ostream>
+#include <functional>
+
+namespace ClipperLib {
+
+static double const pi = 3.141592653589793238;
+static double const two_pi = pi *2;
+static double const def_arc_tolerance = 0.25;
+
+enum Direction { dRightToLeft, dLeftToRight };
+
+static int const Unassigned = -1; //edge not currently 'owning' a solution
+static int const Skip = -2; //edge that would otherwise close a path
+
+#define HORIZONTAL (-1.0E+40)
+#define TOLERANCE (1.0e-20)
+#define NEAR_ZERO(val) (((val) > -TOLERANCE) && ((val) < TOLERANCE))
+
+struct TEdge {
+ IntPoint Bot;
+ IntPoint Curr; //current (updated for every new scanbeam)
+ IntPoint Top;
+ double Dx;
+ PolyType PolyTyp;
+ EdgeSide Side; //side only refers to current side of solution poly
+ int WindDelta; //1 or -1 depending on winding direction
+ int WindCnt;
+ int WindCnt2; //winding count of the opposite polytype
+ int OutIdx;
+ TEdge *Next;
+ TEdge *Prev;
+ TEdge *NextInLML;
+ TEdge *NextInAEL;
+ TEdge *PrevInAEL;
+ TEdge *NextInSEL;
+ TEdge *PrevInSEL;
+};
+
+struct IntersectNode {
+ TEdge *Edge1;
+ TEdge *Edge2;
+ IntPoint Pt;
+};
+
+struct LocalMinimum {
+ cInt Y;
+ TEdge *LeftBound;
+ TEdge *RightBound;
+};
+
+struct OutPt;
+
+//OutRec: contains a path in the clipping solution. Edges in the AEL will
+//carry a pointer to an OutRec when they are part of the clipping solution.
+struct OutRec {
+ int Idx;
+ bool IsHole;
+ bool IsOpen;
+ OutRec *FirstLeft; //see comments in clipper.pas
+ PolyNode *PolyNd;
+ OutPt *Pts;
+ OutPt *BottomPt;
+};
+
+struct OutPt {
+ int Idx;
+ IntPoint Pt;
+ OutPt *Next;
+ OutPt *Prev;
+};
+
+struct Join {
+ OutPt *OutPt1;
+ OutPt *OutPt2;
+ IntPoint OffPt;
+};
+
+struct LocMinSorter
+{
+ inline bool operator()(const LocalMinimum& locMin1, const LocalMinimum& locMin2)
+ {
+ return locMin2.Y < locMin1.Y;
+ }
+};
+
+//------------------------------------------------------------------------------
+//------------------------------------------------------------------------------
+
+inline cInt Round(double val)
+{
+ if ((val < 0)) return static_cast<cInt>(val - 0.5);
+ else return static_cast<cInt>(val + 0.5);
+}
+//------------------------------------------------------------------------------
+
+inline cInt Abs(cInt val)
+{
+ return val < 0 ? -val : val;
+}
+
+//------------------------------------------------------------------------------
+// PolyTree methods ...
+//------------------------------------------------------------------------------
+
+void PolyTree::Clear()
+{
+ for (PolyNodes::size_type i = 0; i < AllNodes.size(); ++i)
+ delete AllNodes[i];
+ AllNodes.resize(0);
+ Childs.resize(0);
+}
+//------------------------------------------------------------------------------
+
+PolyNode* PolyTree::GetFirst() const
+{
+ if (!Childs.empty())
+ return Childs[0];
+ else
+ return 0;
+}
+//------------------------------------------------------------------------------
+
+int PolyTree::Total() const
+{
+ int result = (int)AllNodes.size();
+ //with negative offsets, ignore the hidden outer polygon ...
+ if (result > 0 && Childs[0] != AllNodes[0]) result--;
+ return result;
+}
+
+//------------------------------------------------------------------------------
+// PolyNode methods ...
+//------------------------------------------------------------------------------
+
+PolyNode::PolyNode(): Childs(), Parent(0), Index(0), m_IsOpen(false)
+{
+ // Avoid uninitialized vars
+ m_endtype = etClosedPolygon;
+ m_jointype = jtSquare;
+}
+//------------------------------------------------------------------------------
+
+int PolyNode::ChildCount() const
+{
+ return (int)Childs.size();
+}
+//------------------------------------------------------------------------------
+
+void PolyNode::AddChild(PolyNode& child)
+{
+ unsigned cnt = (unsigned)Childs.size();
+ Childs.push_back(&child);
+ child.Parent = this;
+ child.Index = cnt;
+}
+//------------------------------------------------------------------------------
+
+PolyNode* PolyNode::GetNext() const
+{
+ if (!Childs.empty())
+ return Childs[0];
+ else
+ return GetNextSiblingUp();
+}
+//------------------------------------------------------------------------------
+
+PolyNode* PolyNode::GetNextSiblingUp() const
+{
+ if (!Parent) //protects against PolyTree.GetNextSiblingUp()
+ return 0;
+ else if (Index == Parent->Childs.size() - 1)
+ return Parent->GetNextSiblingUp();
+ else
+ return Parent->Childs[Index + 1];
+}
+//------------------------------------------------------------------------------
+
+bool PolyNode::IsHole() const
+{
+ bool result = true;
+ PolyNode* node = Parent;
+ while (node)
+ {
+ result = !result;
+ node = node->Parent;
+ }
+ return result;
+}
+//------------------------------------------------------------------------------
+
+bool PolyNode::IsOpen() const
+{
+ return m_IsOpen;
+}
+//------------------------------------------------------------------------------
+
+#ifndef use_int32
+
+//------------------------------------------------------------------------------
+// Int128 class (enables safe math on signed 64bit integers)
+// eg Int128 val1((long64)9223372036854775807); //ie 2^63 -1
+// Int128 val2((long64)9223372036854775807);
+// Int128 val3 = val1 * val2;
+// val3.AsString => "85070591730234615847396907784232501249" (8.5e+37)
+//------------------------------------------------------------------------------
+
+class Int128
+{
+ public:
+ ulong64 lo;
+ long64 hi;
+
+ Int128(long64 _lo = 0)
+ {
+ lo = (ulong64)_lo;
+ if (_lo < 0) hi = -1; else hi = 0;
+ }
+
+
+ Int128(const Int128 &val): lo(val.lo), hi(val.hi){}
+
+ Int128(const long64& _hi, const ulong64& _lo): lo(_lo), hi(_hi){}
+
+ Int128& operator = (const long64 &val)
+ {
+ lo = (ulong64)val;
+ if (val < 0) hi = -1; else hi = 0;
+ return *this;
+ }
+
+ bool operator == (const Int128 &val) const
+ {return (hi == val.hi && lo == val.lo);}
+
+ bool operator != (const Int128 &val) const
+ { return !(*this == val);}
+
+ bool operator > (const Int128 &val) const
+ {
+ if (hi != val.hi)
+ return hi > val.hi;
+ else
+ return lo > val.lo;
+ }
+
+ bool operator < (const Int128 &val) const
+ {
+ if (hi != val.hi)
+ return hi < val.hi;
+ else
+ return lo < val.lo;
+ }
+
+ bool operator >= (const Int128 &val) const
+ { return !(*this < val);}
+
+ bool operator <= (const Int128 &val) const
+ { return !(*this > val);}
+
+ Int128& operator += (const Int128 &rhs)
+ {
+ hi += rhs.hi;
+ lo += rhs.lo;
+ if (lo < rhs.lo) hi++;
+ return *this;
+ }
+
+ Int128 operator + (const Int128 &rhs) const
+ {
+ Int128 result(*this);
+ result+= rhs;
+ return result;
+ }
+
+ Int128& operator -= (const Int128 &rhs)
+ {
+ *this += -rhs;
+ return *this;
+ }
+
+ Int128 operator - (const Int128 &rhs) const
+ {
+ Int128 result(*this);
+ result -= rhs;
+ return result;
+ }
+
+ Int128 operator-() const //unary negation
+ {
+ if (lo == 0)
+ return Int128(-hi, 0);
+ else
+ return Int128(~hi, ~lo + 1);
+ }
+
+ operator double() const
+ {
+ const double shift64 = 18446744073709551616.0; //2^64
+ if (hi < 0)
+ {
+ if (lo == 0) return (double)hi * shift64;
+ else return -(double)(~lo + ~hi * shift64);
+ }
+ else
+ return (double)(lo + hi * shift64);
+ }
+
+};
+//------------------------------------------------------------------------------
+
+Int128 Int128Mul (long64 lhs, long64 rhs)
+{
+ bool negate = (lhs < 0) != (rhs < 0);
+
+ if (lhs < 0) lhs = -lhs;
+ ulong64 int1Hi = ulong64(lhs) >> 32;
+ ulong64 int1Lo = ulong64(lhs & 0xFFFFFFFF);
+
+ if (rhs < 0) rhs = -rhs;
+ ulong64 int2Hi = ulong64(rhs) >> 32;
+ ulong64 int2Lo = ulong64(rhs & 0xFFFFFFFF);
+
+ //nb: see comments in clipper.pas
+ ulong64 a = int1Hi * int2Hi;
+ ulong64 b = int1Lo * int2Lo;
+ ulong64 c = int1Hi * int2Lo + int1Lo * int2Hi;
+
+ Int128 tmp;
+ tmp.hi = long64(a + (c >> 32));
+ tmp.lo = long64(c << 32);
+ tmp.lo += long64(b);
+ if (tmp.lo < b) tmp.hi++;
+ if (negate) tmp = -tmp;
+ return tmp;
+};
+#endif
+
+//------------------------------------------------------------------------------
+// Miscellaneous global functions
+//------------------------------------------------------------------------------
+
+bool Orientation(const Path &poly)
+{
+ return Area(poly) >= 0;
+}
+//------------------------------------------------------------------------------
+
+double Area(const Path &poly)
+{
+ int size = (int)poly.size();
+ if (size < 3) return 0;
+
+ double a = 0;
+ for (int i = 0, j = size -1; i < size; ++i)
+ {
+ a += ((double)poly[j].X + poly[i].X) * ((double)poly[j].Y - poly[i].Y);
+ j = i;
+ }
+ return -a * 0.5;
+}
+//------------------------------------------------------------------------------
+
+double Area(const OutPt *op)
+{
+ const OutPt *startOp = op;
+ if (!op) return 0;
+ double a = 0;
+ do {
+ a += (double)(op->Prev->Pt.X + op->Pt.X) * (double)(op->Prev->Pt.Y - op->Pt.Y);
+ op = op->Next;
+ } while (op != startOp);
+ return a * 0.5;
+}
+//------------------------------------------------------------------------------
+
+double Area(const OutRec &outRec)
+{
+ return Area(outRec.Pts);
+}
+//------------------------------------------------------------------------------
+
+bool PointIsVertex(const IntPoint &Pt, OutPt *pp)
+{
+ OutPt *pp2 = pp;
+ do
+ {
+ if (pp2->Pt == Pt) return true;
+ pp2 = pp2->Next;
+ }
+ while (pp2 != pp);
+ return false;
+}
+//------------------------------------------------------------------------------
+
+//See "The Point in Polygon Problem for Arbitrary Polygons" by Hormann & Agathos
+//http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.88.5498&rep=rep1&type=pdf
+int PointInPolygon(const IntPoint &pt, const Path &path)
+{
+ //returns 0 if false, +1 if true, -1 if pt ON polygon boundary
+ int result = 0;
+ size_t cnt = path.size();
+ if (cnt < 3) return 0;
+ IntPoint ip = path[0];
+ for(size_t i = 1; i <= cnt; ++i)
+ {
+ IntPoint ipNext = (i == cnt ? path[0] : path[i]);
+ if (ipNext.Y == pt.Y)
+ {
+ if ((ipNext.X == pt.X) || (ip.Y == pt.Y &&
+ ((ipNext.X > pt.X) == (ip.X < pt.X)))) return -1;
+ }
+ if ((ip.Y < pt.Y) != (ipNext.Y < pt.Y))
+ {
+ if (ip.X >= pt.X)
+ {
+ if (ipNext.X > pt.X) result = 1 - result;
+ else
+ {
+ double d = (double)(ip.X - pt.X) * (ipNext.Y - pt.Y) -
+ (double)(ipNext.X - pt.X) * (ip.Y - pt.Y);
+ if (!d) return -1;
+ if ((d > 0) == (ipNext.Y > ip.Y)) result = 1 - result;
+ }
+ } else
+ {
+ if (ipNext.X > pt.X)
+ {
+ double d = (double)(ip.X - pt.X) * (ipNext.Y - pt.Y) -
+ (double)(ipNext.X - pt.X) * (ip.Y - pt.Y);
+ if (!d) return -1;
+ if ((d > 0) == (ipNext.Y > ip.Y)) result = 1 - result;
+ }
+ }
+ }
+ ip = ipNext;
+ }
+ return result;
+}
+//------------------------------------------------------------------------------
+
+int PointInPolygon (const IntPoint &pt, OutPt *op)
+{
+ //returns 0 if false, +1 if true, -1 if pt ON polygon boundary
+ int result = 0;
+ OutPt* startOp = op;
+ for(;;)
+ {
+ if (op->Next->Pt.Y == pt.Y)
+ {
+ if ((op->Next->Pt.X == pt.X) || (op->Pt.Y == pt.Y &&
+ ((op->Next->Pt.X > pt.X) == (op->Pt.X < pt.X)))) return -1;
+ }
+ if ((op->Pt.Y < pt.Y) != (op->Next->Pt.Y < pt.Y))
+ {
+ if (op->Pt.X >= pt.X)
+ {
+ if (op->Next->Pt.X > pt.X) result = 1 - result;
+ else
+ {
+ double d = (double)(op->Pt.X - pt.X) * (op->Next->Pt.Y - pt.Y) -
+ (double)(op->Next->Pt.X - pt.X) * (op->Pt.Y - pt.Y);
+ if (!d) return -1;
+ if ((d > 0) == (op->Next->Pt.Y > op->Pt.Y)) result = 1 - result;
+ }
+ } else
+ {
+ if (op->Next->Pt.X > pt.X)
+ {
+ double d = (double)(op->Pt.X - pt.X) * (op->Next->Pt.Y - pt.Y) -
+ (double)(op->Next->Pt.X - pt.X) * (op->Pt.Y - pt.Y);
+ if (!d) return -1;
+ if ((d > 0) == (op->Next->Pt.Y > op->Pt.Y)) result = 1 - result;
+ }
+ }
+ }
+ op = op->Next;
+ if (startOp == op) break;
+ }
+ return result;
+}
+//------------------------------------------------------------------------------
+
+bool Poly2ContainsPoly1(OutPt *OutPt1, OutPt *OutPt2)
+{
+ OutPt* op = OutPt1;
+ do
+ {
+ //nb: PointInPolygon returns 0 if false, +1 if true, -1 if pt on polygon
+ int res = PointInPolygon(op->Pt, OutPt2);
+ if (res >= 0) return res > 0;
+ op = op->Next;
+ }
+ while (op != OutPt1);
+ return true;
+}
+//----------------------------------------------------------------------
+
+bool SlopesEqual(const TEdge &e1, const TEdge &e2, bool UseFullInt64Range)
+{
+#ifndef use_int32
+ if (UseFullInt64Range)
+ return Int128Mul(e1.Top.Y - e1.Bot.Y, e2.Top.X - e2.Bot.X) ==
+ Int128Mul(e1.Top.X - e1.Bot.X, e2.Top.Y - e2.Bot.Y);
+ else
+#endif
+ return (e1.Top.Y - e1.Bot.Y) * (e2.Top.X - e2.Bot.X) ==
+ (e1.Top.X - e1.Bot.X) * (e2.Top.Y - e2.Bot.Y);
+}
+//------------------------------------------------------------------------------
+
+bool SlopesEqual(const IntPoint pt1, const IntPoint pt2,
+ const IntPoint pt3, bool UseFullInt64Range)
+{
+#ifndef use_int32
+ if (UseFullInt64Range)
+ return Int128Mul(pt1.Y-pt2.Y, pt2.X-pt3.X) == Int128Mul(pt1.X-pt2.X, pt2.Y-pt3.Y);
+ else
+#endif
+ return (pt1.Y-pt2.Y)*(pt2.X-pt3.X) == (pt1.X-pt2.X)*(pt2.Y-pt3.Y);
+}
+//------------------------------------------------------------------------------
+
+bool SlopesEqual(const IntPoint pt1, const IntPoint pt2,
+ const IntPoint pt3, const IntPoint pt4, bool UseFullInt64Range)
+{
+#ifndef use_int32
+ if (UseFullInt64Range)
+ return Int128Mul(pt1.Y-pt2.Y, pt3.X-pt4.X) == Int128Mul(pt1.X-pt2.X, pt3.Y-pt4.Y);
+ else
+#endif
+ return (pt1.Y-pt2.Y)*(pt3.X-pt4.X) == (pt1.X-pt2.X)*(pt3.Y-pt4.Y);
+}
+//------------------------------------------------------------------------------
+
+inline bool IsHorizontal(TEdge &e)
+{
+ return e.Dx == HORIZONTAL;
+}
+//------------------------------------------------------------------------------
+
+inline double GetDx(const IntPoint pt1, const IntPoint pt2)
+{
+ return (pt1.Y == pt2.Y) ?
+ HORIZONTAL : (double)(pt2.X - pt1.X) / (pt2.Y - pt1.Y);
+}
+//---------------------------------------------------------------------------
+
+inline void SetDx(TEdge &e)
+{
+ cInt dy = (e.Top.Y - e.Bot.Y);
+ if (dy == 0) e.Dx = HORIZONTAL;
+ else e.Dx = (double)(e.Top.X - e.Bot.X) / dy;
+}
+//---------------------------------------------------------------------------
+
+inline void SwapSides(TEdge &Edge1, TEdge &Edge2)
+{
+ EdgeSide Side = Edge1.Side;
+ Edge1.Side = Edge2.Side;
+ Edge2.Side = Side;
+}
+//------------------------------------------------------------------------------
+
+inline void SwapPolyIndexes(TEdge &Edge1, TEdge &Edge2)
+{
+ int OutIdx = Edge1.OutIdx;
+ Edge1.OutIdx = Edge2.OutIdx;
+ Edge2.OutIdx = OutIdx;
+}
+//------------------------------------------------------------------------------
+
+inline cInt TopX(TEdge &edge, const cInt currentY)
+{
+ return ( currentY == edge.Top.Y ) ?
+ edge.Top.X : edge.Bot.X + Round(edge.Dx *(currentY - edge.Bot.Y));
+}
+//------------------------------------------------------------------------------
+
+void IntersectPoint(TEdge &Edge1, TEdge &Edge2, IntPoint &ip)
+{
+#ifdef use_xyz
+ ip.Z = 0;
+#endif
+
+ double b1, b2;
+ if (Edge1.Dx == Edge2.Dx)
+ {
+ ip.Y = Edge1.Curr.Y;
+ ip.X = TopX(Edge1, ip.Y);
+ return;
+ }
+ else if (Edge1.Dx == 0)
+ {
+ ip.X = Edge1.Bot.X;
+ if (IsHorizontal(Edge2))
+ ip.Y = Edge2.Bot.Y;
+ else
+ {
+ b2 = Edge2.Bot.Y - (Edge2.Bot.X / Edge2.Dx);
+ ip.Y = Round(ip.X / Edge2.Dx + b2);
+ }
+ }
+ else if (Edge2.Dx == 0)
+ {
+ ip.X = Edge2.Bot.X;
+ if (IsHorizontal(Edge1))
+ ip.Y = Edge1.Bot.Y;
+ else
+ {
+ b1 = Edge1.Bot.Y - (Edge1.Bot.X / Edge1.Dx);
+ ip.Y = Round(ip.X / Edge1.Dx + b1);
+ }
+ }
+ else
+ {
+ b1 = Edge1.Bot.X - Edge1.Bot.Y * Edge1.Dx;
+ b2 = Edge2.Bot.X - Edge2.Bot.Y * Edge2.Dx;
+ double q = (b2-b1) / (Edge1.Dx - Edge2.Dx);
+ ip.Y = Round(q);
+ if (std::fabs(Edge1.Dx) < std::fabs(Edge2.Dx))
+ ip.X = Round(Edge1.Dx * q + b1);
+ else
+ ip.X = Round(Edge2.Dx * q + b2);
+ }
+
+ if (ip.Y < Edge1.Top.Y || ip.Y < Edge2.Top.Y)
+ {
+ if (Edge1.Top.Y > Edge2.Top.Y)
+ ip.Y = Edge1.Top.Y;
+ else
+ ip.Y = Edge2.Top.Y;
+ if (std::fabs(Edge1.Dx) < std::fabs(Edge2.Dx))
+ ip.X = TopX(Edge1, ip.Y);
+ else
+ ip.X = TopX(Edge2, ip.Y);
+ }
+ //finally, don't allow 'ip' to be BELOW curr.Y (ie bottom of scanbeam) ...
+ if (ip.Y > Edge1.Curr.Y)
+ {
+ ip.Y = Edge1.Curr.Y;
+ //use the more vertical edge to derive X ...
+ if (std::fabs(Edge1.Dx) > std::fabs(Edge2.Dx))
+ ip.X = TopX(Edge2, ip.Y); else
+ ip.X = TopX(Edge1, ip.Y);
+ }
+}
+//------------------------------------------------------------------------------
+
+void ReversePolyPtLinks(OutPt *pp)
+{
+ if (!pp) return;
+ OutPt *pp1, *pp2;
+ pp1 = pp;
+ do {
+ pp2 = pp1->Next;
+ pp1->Next = pp1->Prev;
+ pp1->Prev = pp2;
+ pp1 = pp2;
+ } while( pp1 != pp );
+}
+//------------------------------------------------------------------------------
+
+void DisposeOutPts(OutPt*& pp)
+{
+ if (pp == 0) return;
+ pp->Prev->Next = 0;
+ while( pp )
+ {
+ OutPt *tmpPp = pp;
+ pp = pp->Next;
+ delete tmpPp;
+ }
+}
+//------------------------------------------------------------------------------
+
+inline void InitEdge(TEdge* e, TEdge* eNext, TEdge* ePrev, const IntPoint& Pt)
+{
+ std::memset(e, 0, sizeof(TEdge));
+ e->Next = eNext;
+ e->Prev = ePrev;
+ e->Curr = Pt;
+ e->OutIdx = Unassigned;
+}
+//------------------------------------------------------------------------------
+
+void InitEdge2(TEdge& e, PolyType Pt)
+{
+ if (e.Curr.Y >= e.Next->Curr.Y)
+ {
+ e.Bot = e.Curr;
+ e.Top = e.Next->Curr;
+ } else
+ {
+ e.Top = e.Curr;
+ e.Bot = e.Next->Curr;
+ }
+ SetDx(e);
+ e.PolyTyp = Pt;
+}
+//------------------------------------------------------------------------------
+
+TEdge* RemoveEdge(TEdge* e)
+{
+ //removes e from double_linked_list (but without removing from memory)
+ e->Prev->Next = e->Next;
+ e->Next->Prev = e->Prev;
+ TEdge* result = e->Next;
+ e->Prev = 0; //flag as removed (see ClipperBase.Clear)
+ return result;
+}
+//------------------------------------------------------------------------------
+
+inline void ReverseHorizontal(TEdge &e)
+{
+ //swap horizontal edges' Top and Bottom x's so they follow the natural
+ //progression of the bounds - ie so their xbots will align with the
+ //adjoining lower edge. [Helpful in the ProcessHorizontal() method.]
+ std::swap(e.Top.X, e.Bot.X);
+#ifdef use_xyz
+ std::swap(e.Top.Z, e.Bot.Z);
+#endif
+}
+//------------------------------------------------------------------------------
+
+void SwapPoints(IntPoint &pt1, IntPoint &pt2)
+{
+ IntPoint tmp = pt1;
+ pt1 = pt2;
+ pt2 = tmp;
+}
+//------------------------------------------------------------------------------
+
+bool GetOverlapSegment(IntPoint pt1a, IntPoint pt1b, IntPoint pt2a,
+ IntPoint pt2b, IntPoint &pt1, IntPoint &pt2)
+{
+ //precondition: segments are Collinear.
+ if (Abs(pt1a.X - pt1b.X) > Abs(pt1a.Y - pt1b.Y))
+ {
+ if (pt1a.X > pt1b.X) SwapPoints(pt1a, pt1b);
+ if (pt2a.X > pt2b.X) SwapPoints(pt2a, pt2b);
+ if (pt1a.X > pt2a.X) pt1 = pt1a; else pt1 = pt2a;
+ if (pt1b.X < pt2b.X) pt2 = pt1b; else pt2 = pt2b;
+ return pt1.X < pt2.X;
+ } else
+ {
+ if (pt1a.Y < pt1b.Y) SwapPoints(pt1a, pt1b);
+ if (pt2a.Y < pt2b.Y) SwapPoints(pt2a, pt2b);
+ if (pt1a.Y < pt2a.Y) pt1 = pt1a; else pt1 = pt2a;
+ if (pt1b.Y > pt2b.Y) pt2 = pt1b; else pt2 = pt2b;
+ return pt1.Y > pt2.Y;
+ }
+}
+//------------------------------------------------------------------------------
+
+bool FirstIsBottomPt(const OutPt* btmPt1, const OutPt* btmPt2)
+{
+ OutPt *p = btmPt1->Prev;
+ while ((p->Pt == btmPt1->Pt) && (p != btmPt1)) p = p->Prev;
+ double dx1p = std::fabs(GetDx(btmPt1->Pt, p->Pt));
+ p = btmPt1->Next;
+ while ((p->Pt == btmPt1->Pt) && (p != btmPt1)) p = p->Next;
+ double dx1n = std::fabs(GetDx(btmPt1->Pt, p->Pt));
+
+ p = btmPt2->Prev;
+ while ((p->Pt == btmPt2->Pt) && (p != btmPt2)) p = p->Prev;
+ double dx2p = std::fabs(GetDx(btmPt2->Pt, p->Pt));
+ p = btmPt2->Next;
+ while ((p->Pt == btmPt2->Pt) && (p != btmPt2)) p = p->Next;
+ double dx2n = std::fabs(GetDx(btmPt2->Pt, p->Pt));
+
+ if (std::max(dx1p, dx1n) == std::max(dx2p, dx2n) &&
+ std::min(dx1p, dx1n) == std::min(dx2p, dx2n))
+ return Area(btmPt1) > 0; //if otherwise identical use orientation
+ else
+ return (dx1p >= dx2p && dx1p >= dx2n) || (dx1n >= dx2p && dx1n >= dx2n);
+}
+//------------------------------------------------------------------------------
+
+OutPt* GetBottomPt(OutPt *pp)
+{
+ OutPt* dups = 0;
+ OutPt* p = pp->Next;
+ while (p != pp)
+ {
+ if (p->Pt.Y > pp->Pt.Y)
+ {
+ pp = p;
+ dups = 0;
+ }
+ else if (p->Pt.Y == pp->Pt.Y && p->Pt.X <= pp->Pt.X)
+ {
+ if (p->Pt.X < pp->Pt.X)
+ {
+ dups = 0;
+ pp = p;
+ } else
+ {
+ if (p->Next != pp && p->Prev != pp) dups = p;
+ }
+ }
+ p = p->Next;
+ }
+ if (dups)
+ {
+ //there appears to be at least 2 vertices at BottomPt so ...
+ while (dups != p)
+ {
+ if (!FirstIsBottomPt(p, dups)) pp = dups;
+ dups = dups->Next;
+ while (dups->Pt != pp->Pt) dups = dups->Next;
+ }
+ }
+ return pp;
+}
+//------------------------------------------------------------------------------
+
+bool Pt2IsBetweenPt1AndPt3(const IntPoint pt1,
+ const IntPoint pt2, const IntPoint pt3)
+{
+ if ((pt1 == pt3) || (pt1 == pt2) || (pt3 == pt2))
+ return false;
+ else if (pt1.X != pt3.X)
+ return (pt2.X > pt1.X) == (pt2.X < pt3.X);
+ else
+ return (pt2.Y > pt1.Y) == (pt2.Y < pt3.Y);
+}
+//------------------------------------------------------------------------------
+
+bool HorzSegmentsOverlap(cInt seg1a, cInt seg1b, cInt seg2a, cInt seg2b)
+{
+ if (seg1a > seg1b) std::swap(seg1a, seg1b);
+ if (seg2a > seg2b) std::swap(seg2a, seg2b);
+ return (seg1a < seg2b) && (seg2a < seg1b);
+}
+
+//------------------------------------------------------------------------------
+// ClipperBase class methods ...
+//------------------------------------------------------------------------------
+
+ClipperBase::ClipperBase() //constructor
+{
+ m_CurrentLM = m_MinimaList.begin(); //begin() == end() here
+ m_UseFullRange = false;
+
+ // Avoid uninitialized vars
+ m_PreserveCollinear = false;
+ m_HasOpenPaths = false;
+ m_ActiveEdges = NULL;
+}
+//------------------------------------------------------------------------------
+
+ClipperBase::~ClipperBase() //destructor
+{
+ Clear();
+}
+//------------------------------------------------------------------------------
+
+void RangeTest(const IntPoint& Pt, bool& useFullRange)
+{
+ if (useFullRange)
+ {
+ if (Pt.X > hiRange || Pt.Y > hiRange || -Pt.X > hiRange || -Pt.Y > hiRange)
+ throw clipperException("Coordinate outside allowed range");
+ }
+ else if (Pt.X > loRange|| Pt.Y > loRange || -Pt.X > loRange || -Pt.Y > loRange)
+ {
+ useFullRange = true;
+ RangeTest(Pt, useFullRange);
+ }
+}
+//------------------------------------------------------------------------------
+
+TEdge* FindNextLocMin(TEdge* E)
+{
+ for (;;)
+ {
+ while (E->Bot != E->Prev->Bot || E->Curr == E->Top) E = E->Next;
+ if (!IsHorizontal(*E) && !IsHorizontal(*E->Prev)) break;
+ while (IsHorizontal(*E->Prev)) E = E->Prev;
+ TEdge* E2 = E;
+ while (IsHorizontal(*E)) E = E->Next;
+ if (E->Top.Y == E->Prev->Bot.Y) continue; //ie just an intermediate horz.
+ if (E2->Prev->Bot.X < E->Bot.X) E = E2;
+ break;
+ }
+ return E;
+}
+//------------------------------------------------------------------------------
+
+TEdge* ClipperBase::ProcessBound(TEdge* E, bool NextIsForward)
+{
+ TEdge *Result = E;
+ TEdge *Horz = 0;
+
+ if (E->OutIdx == Skip)
+ {
+ //if edges still remain in the current bound beyond the skip edge then
+ //create another LocMin and call ProcessBound once more
+ if (NextIsForward)
+ {
+ while (E->Top.Y == E->Next->Bot.Y) E = E->Next;
+ //don't include top horizontals when parsing a bound a second time,
+ //they will be contained in the opposite bound ...
+ while (E != Result && IsHorizontal(*E)) E = E->Prev;
+ }
+ else
+ {
+ while (E->Top.Y == E->Prev->Bot.Y) E = E->Prev;
+ while (E != Result && IsHorizontal(*E)) E = E->Next;
+ }
+
+ if (E == Result)
+ {
+ if (NextIsForward) Result = E->Next;
+ else Result = E->Prev;
+ }
+ else
+ {
+ //there are more edges in the bound beyond result starting with E
+ if (NextIsForward)
+ E = Result->Next;
+ else
+ E = Result->Prev;
+ MinimaList::value_type locMin;
+ locMin.Y = E->Bot.Y;
+ locMin.LeftBound = 0;
+ locMin.RightBound = E;
+ E->WindDelta = 0;
+ Result = ProcessBound(E, NextIsForward);
+ m_MinimaList.push_back(locMin);
+ }
+ return Result;
+ }
+
+ TEdge *EStart;
+
+ if (IsHorizontal(*E))
+ {
+ //We need to be careful with open paths because this may not be a
+ //true local minima (ie E may be following a skip edge).
+ //Also, consecutive horz. edges may start heading left before going right.
+ if (NextIsForward)
+ EStart = E->Prev;
+ else
+ EStart = E->Next;
+ if (IsHorizontal(*EStart)) //ie an adjoining horizontal skip edge
+ {
+ if (EStart->Bot.X != E->Bot.X && EStart->Top.X != E->Bot.X)
+ ReverseHorizontal(*E);
+ }
+ else if (EStart->Bot.X != E->Bot.X)
+ ReverseHorizontal(*E);
+ }
+
+ EStart = E;
+ if (NextIsForward)
+ {
+ while (Result->Top.Y == Result->Next->Bot.Y && Result->Next->OutIdx != Skip)
+ Result = Result->Next;
+ if (IsHorizontal(*Result) && Result->Next->OutIdx != Skip)
+ {
+ //nb: at the top of a bound, horizontals are added to the bound
+ //only when the preceding edge attaches to the horizontal's left vertex
+ //unless a Skip edge is encountered when that becomes the top divide
+ Horz = Result;
+ while (IsHorizontal(*Horz->Prev)) Horz = Horz->Prev;
+ if (Horz->Prev->Top.X > Result->Next->Top.X) Result = Horz->Prev;
+ }
+ while (E != Result)
+ {
+ E->NextInLML = E->Next;
+ if (IsHorizontal(*E) && E != EStart &&
+ E->Bot.X != E->Prev->Top.X) ReverseHorizontal(*E);
+ E = E->Next;
+ }
+ if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Prev->Top.X)
+ ReverseHorizontal(*E);
+ Result = Result->Next; //move to the edge just beyond current bound
+ } else
+ {
+ while (Result->Top.Y == Result->Prev->Bot.Y && Result->Prev->OutIdx != Skip)
+ Result = Result->Prev;
+ if (IsHorizontal(*Result) && Result->Prev->OutIdx != Skip)
+ {
+ Horz = Result;
+ while (IsHorizontal(*Horz->Next)) Horz = Horz->Next;
+ if (Horz->Next->Top.X == Result->Prev->Top.X ||
+ Horz->Next->Top.X > Result->Prev->Top.X) Result = Horz->Next;
+ }
+
+ while (E != Result)
+ {
+ E->NextInLML = E->Prev;
+ if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Next->Top.X)
+ ReverseHorizontal(*E);
+ E = E->Prev;
+ }
+ if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Next->Top.X)
+ ReverseHorizontal(*E);
+ Result = Result->Prev; //move to the edge just beyond current bound
+ }
+
+ return Result;
+}
+//------------------------------------------------------------------------------
+
+bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed)
+{
+#ifdef use_lines
+ if (!Closed && PolyTyp == ptClip)
+ throw clipperException("AddPath: Open paths must be subject.");
+#else
+ if (!Closed)
+ throw clipperException("AddPath: Open paths have been disabled.");
+#endif
+
+ int highI = (int)pg.size() -1;
+ if (Closed) while (highI > 0 && (pg[highI] == pg[0])) --highI;
+ while (highI > 0 && (pg[highI] == pg[highI -1])) --highI;
+ if ((Closed && highI < 2) || (!Closed && highI < 1)) return false;
+
+ //create a new edge array ...
+ TEdge *edges = new TEdge [highI +1];
+
+ bool IsFlat = true;
+ //1. Basic (first) edge initialization ...
+ try
+ {
+ edges[1].Curr = pg[1];
+ RangeTest(pg[0], m_UseFullRange);
+ RangeTest(pg[highI], m_UseFullRange);
+ InitEdge(&edges[0], &edges[1], &edges[highI], pg[0]);
+ InitEdge(&edges[highI], &edges[0], &edges[highI-1], pg[highI]);
+ for (int i = highI - 1; i >= 1; --i)
+ {
+ RangeTest(pg[i], m_UseFullRange);
+ InitEdge(&edges[i], &edges[i+1], &edges[i-1], pg[i]);
+ }
+ }
+ catch(...)
+ {
+ delete [] edges;
+ throw; //range test fails
+ }
+ TEdge *eStart = &edges[0];
+
+ //2. Remove duplicate vertices, and (when closed) collinear edges ...
+ TEdge *E = eStart, *eLoopStop = eStart;
+ for (;;)
+ {
+ //nb: allows matching start and end points when not Closed ...
+ if (E->Curr == E->Next->Curr && (Closed || E->Next != eStart))
+ {
+ if (E == E->Next) break;
+ if (E == eStart) eStart = E->Next;
+ E = RemoveEdge(E);
+ eLoopStop = E;
+ continue;
+ }
+ if (E->Prev == E->Next)
+ break; //only two vertices
+ else if (Closed &&
+ SlopesEqual(E->Prev->Curr, E->Curr, E->Next->Curr, m_UseFullRange) &&
+ (!m_PreserveCollinear ||
+ !Pt2IsBetweenPt1AndPt3(E->Prev->Curr, E->Curr, E->Next->Curr)))
+ {
+ //Collinear edges are allowed for open paths but in closed paths
+ //the default is to merge adjacent collinear edges into a single edge.
+ //However, if the PreserveCollinear property is enabled, only overlapping
+ //collinear edges (ie spikes) will be removed from closed paths.
+ if (E == eStart) eStart = E->Next;
+ E = RemoveEdge(E);
+ E = E->Prev;
+ eLoopStop = E;
+ continue;
+ }
+ E = E->Next;
+ if ((E == eLoopStop) || (!Closed && E->Next == eStart)) break;
+ }
+
+ if ((!Closed && (E == E->Next)) || (Closed && (E->Prev == E->Next)))
+ {
+ delete [] edges;
+ return false;
+ }
+
+ if (!Closed)
+ {
+ m_HasOpenPaths = true;
+ eStart->Prev->OutIdx = Skip;
+ }
+
+ //3. Do second stage of edge initialization ...
+ E = eStart;
+ do
+ {
+ InitEdge2(*E, PolyTyp);
+ E = E->Next;
+ if (IsFlat && E->Curr.Y != eStart->Curr.Y) IsFlat = false;
+ }
+ while (E != eStart);
+
+ //4. Finally, add edge bounds to LocalMinima list ...
+
+ //Totally flat paths must be handled differently when adding them
+ //to LocalMinima list to avoid endless loops etc ...
+ if (IsFlat)
+ {
+ if (Closed)
+ {
+ delete [] edges;
+ return false;
+ }
+ E->Prev->OutIdx = Skip;
+ MinimaList::value_type locMin;
+ locMin.Y = E->Bot.Y;
+ locMin.LeftBound = 0;
+ locMin.RightBound = E;
+ locMin.RightBound->Side = esRight;
+ locMin.RightBound->WindDelta = 0;
+ for (;;)
+ {
+ if (E->Bot.X != E->Prev->Top.X) ReverseHorizontal(*E);
+ if (E->Next->OutIdx == Skip) break;
+ E->NextInLML = E->Next;
+ E = E->Next;
+ }
+ m_MinimaList.push_back(locMin);
+ m_edges.push_back(edges);
+ return true;
+ }
+
+ m_edges.push_back(edges);
+ bool leftBoundIsForward;
+ TEdge* EMin = 0;
+
+ //workaround to avoid an endless loop in the while loop below when
+ //open paths have matching start and end points ...
+ if (E->Prev->Bot == E->Prev->Top) E = E->Next;
+
+ for (;;)
+ {
+ E = FindNextLocMin(E);
+ if (E == EMin) break;
+ else if (!EMin) EMin = E;
+
+ //E and E.Prev now share a local minima (left aligned if horizontal).
+ //Compare their slopes to find which starts which bound ...
+ MinimaList::value_type locMin;
+ locMin.Y = E->Bot.Y;
+ if (E->Dx < E->Prev->Dx)
+ {
+ locMin.LeftBound = E->Prev;
+ locMin.RightBound = E;
+ leftBoundIsForward = false; //Q.nextInLML = Q.prev
+ } else
+ {
+ locMin.LeftBound = E;
+ locMin.RightBound = E->Prev;
+ leftBoundIsForward = true; //Q.nextInLML = Q.next
+ }
+
+ if (!Closed) locMin.LeftBound->WindDelta = 0;
+ else if (locMin.LeftBound->Next == locMin.RightBound)
+ locMin.LeftBound->WindDelta = -1;
+ else locMin.LeftBound->WindDelta = 1;
+ locMin.RightBound->WindDelta = -locMin.LeftBound->WindDelta;
+
+ E = ProcessBound(locMin.LeftBound, leftBoundIsForward);
+ if (E->OutIdx == Skip) E = ProcessBound(E, leftBoundIsForward);
+
+ TEdge* E2 = ProcessBound(locMin.RightBound, !leftBoundIsForward);
+ if (E2->OutIdx == Skip) E2 = ProcessBound(E2, !leftBoundIsForward);
+
+ if (locMin.LeftBound->OutIdx == Skip)
+ locMin.LeftBound = 0;
+ else if (locMin.RightBound->OutIdx == Skip)
+ locMin.RightBound = 0;
+ m_MinimaList.push_back(locMin);
+ if (!leftBoundIsForward) E = E2;
+ }
+ return true;
+}
+//------------------------------------------------------------------------------
+
+bool ClipperBase::AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed)
+{
+ bool result = false;
+ for (Paths::size_type i = 0; i < ppg.size(); ++i)
+ if (AddPath(ppg[i], PolyTyp, Closed)) result = true;
+ return result;
+}
+//------------------------------------------------------------------------------
+
+void ClipperBase::Clear()
+{
+ DisposeLocalMinimaList();
+ for (EdgeList::size_type i = 0; i < m_edges.size(); ++i)
+ {
+ TEdge* edges = m_edges[i];
+ delete [] edges;
+ }
+ m_edges.clear();
+ m_UseFullRange = false;
+ m_HasOpenPaths = false;
+}
+//------------------------------------------------------------------------------
+
+void ClipperBase::Reset()
+{
+ m_CurrentLM = m_MinimaList.begin();
+ if (m_CurrentLM == m_MinimaList.end()) return; //ie nothing to process
+ std::sort(m_MinimaList.begin(), m_MinimaList.end(), LocMinSorter());
+
+ m_Scanbeam = ScanbeamList(); //clears/resets priority_queue
+ //reset all edges ...
+ for (MinimaList::iterator lm = m_MinimaList.begin(); lm != m_MinimaList.end(); ++lm)
+ {
+ InsertScanbeam(lm->Y);
+ TEdge* e = lm->LeftBound;
+ if (e)
+ {
+ e->Curr = e->Bot;
+ e->Side = esLeft;
+ e->OutIdx = Unassigned;
+ }
+
+ e = lm->RightBound;
+ if (e)
+ {
+ e->Curr = e->Bot;
+ e->Side = esRight;
+ e->OutIdx = Unassigned;
+ }
+ }
+ m_ActiveEdges = 0;
+ m_CurrentLM = m_MinimaList.begin();
+}
+//------------------------------------------------------------------------------
+
+void ClipperBase::DisposeLocalMinimaList()
+{
+ m_MinimaList.clear();
+ m_CurrentLM = m_MinimaList.begin();
+}
+//------------------------------------------------------------------------------
+
+bool ClipperBase::PopLocalMinima(cInt Y, const LocalMinimum *&locMin)
+{
+ if (m_CurrentLM == m_MinimaList.end() || (*m_CurrentLM).Y != Y) return false;
+ locMin = &(*m_CurrentLM);
+ ++m_CurrentLM;
+ return true;
+}
+//------------------------------------------------------------------------------
+
+IntRect ClipperBase::GetBounds()
+{
+ IntRect result;
+ MinimaList::iterator lm = m_MinimaList.begin();
+ if (lm == m_MinimaList.end())
+ {
+ result.left = result.top = result.right = result.bottom = 0;
+ return result;
+ }
+ result.left = lm->LeftBound->Bot.X;
+ result.top = lm->LeftBound->Bot.Y;
+ result.right = lm->LeftBound->Bot.X;
+ result.bottom = lm->LeftBound->Bot.Y;
+ while (lm != m_MinimaList.end())
+ {
+ //todo - needs fixing for open paths
+ result.bottom = std::max(result.bottom, lm->LeftBound->Bot.Y);
+ TEdge* e = lm->LeftBound;
+ for (;;) {
+ TEdge* bottomE = e;
+ while (e->NextInLML)
+ {
+ if (e->Bot.X < result.left) result.left = e->Bot.X;
+ if (e->Bot.X > result.right) result.right = e->Bot.X;
+ e = e->NextInLML;
+ }
+ result.left = std::min(result.left, e->Bot.X);
+ result.right = std::max(result.right, e->Bot.X);
+ result.left = std::min(result.left, e->Top.X);
+ result.right = std::max(result.right, e->Top.X);
+ result.top = std::min(result.top, e->Top.Y);
+ if (bottomE == lm->LeftBound) e = lm->RightBound;
+ else break;
+ }
+ ++lm;
+ }
+ return result;
+}
+//------------------------------------------------------------------------------
+
+void ClipperBase::InsertScanbeam(const cInt Y)
+{
+ m_Scanbeam.push(Y);
+}
+//------------------------------------------------------------------------------
+
+bool ClipperBase::PopScanbeam(cInt &Y)
+{
+ if (m_Scanbeam.empty()) return false;
+ Y = m_Scanbeam.top();
+ m_Scanbeam.pop();
+ while (!m_Scanbeam.empty() && Y == m_Scanbeam.top()) { m_Scanbeam.pop(); } // Pop duplicates.
+ return true;
+}
+//------------------------------------------------------------------------------
+
+void ClipperBase::DisposeAllOutRecs(){
+ for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i)
+ DisposeOutRec(i);
+ m_PolyOuts.clear();
+}
+//------------------------------------------------------------------------------
+
+void ClipperBase::DisposeOutRec(PolyOutList::size_type index)
+{
+ OutRec *outRec = m_PolyOuts[index];
+ if (outRec->Pts) DisposeOutPts(outRec->Pts);
+ delete outRec;
+ m_PolyOuts[index] = 0;
+}
+//------------------------------------------------------------------------------
+
+void ClipperBase::DeleteFromAEL(TEdge *e)
+{
+ TEdge* AelPrev = e->PrevInAEL;
+ TEdge* AelNext = e->NextInAEL;
+ if (!AelPrev && !AelNext && (e != m_ActiveEdges)) return; //already deleted
+ if (AelPrev) AelPrev->NextInAEL = AelNext;
+ else m_ActiveEdges = AelNext;
+ if (AelNext) AelNext->PrevInAEL = AelPrev;
+ e->NextInAEL = 0;
+ e->PrevInAEL = 0;
+}
+//------------------------------------------------------------------------------
+
+OutRec* ClipperBase::CreateOutRec()
+{
+ OutRec* result = new OutRec;
+ result->IsHole = false;
+ result->IsOpen = false;
+ result->FirstLeft = 0;
+ result->Pts = 0;
+ result->BottomPt = 0;
+ result->PolyNd = 0;
+ m_PolyOuts.push_back(result);
+ result->Idx = (int)m_PolyOuts.size() - 1;
+ return result;
+}
+//------------------------------------------------------------------------------
+
+void ClipperBase::SwapPositionsInAEL(TEdge *Edge1, TEdge *Edge2)
+{
+ //check that one or other edge hasn't already been removed from AEL ...
+ if (Edge1->NextInAEL == Edge1->PrevInAEL ||
+ Edge2->NextInAEL == Edge2->PrevInAEL) return;
+
+ if (Edge1->NextInAEL == Edge2)
+ {
+ TEdge* Next = Edge2->NextInAEL;
+ if (Next) Next->PrevInAEL = Edge1;
+ TEdge* Prev = Edge1->PrevInAEL;
+ if (Prev) Prev->NextInAEL = Edge2;
+ Edge2->PrevInAEL = Prev;
+ Edge2->NextInAEL = Edge1;
+ Edge1->PrevInAEL = Edge2;
+ Edge1->NextInAEL = Next;
+ }
+ else if (Edge2->NextInAEL == Edge1)
+ {
+ TEdge* Next = Edge1->NextInAEL;
+ if (Next) Next->PrevInAEL = Edge2;
+ TEdge* Prev = Edge2->PrevInAEL;
+ if (Prev) Prev->NextInAEL = Edge1;
+ Edge1->PrevInAEL = Prev;
+ Edge1->NextInAEL = Edge2;
+ Edge2->PrevInAEL = Edge1;
+ Edge2->NextInAEL = Next;
+ }
+ else
+ {
+ TEdge* Next = Edge1->NextInAEL;
+ TEdge* Prev = Edge1->PrevInAEL;
+ Edge1->NextInAEL = Edge2->NextInAEL;
+ if (Edge1->NextInAEL) Edge1->NextInAEL->PrevInAEL = Edge1;
+ Edge1->PrevInAEL = Edge2->PrevInAEL;
+ if (Edge1->PrevInAEL) Edge1->PrevInAEL->NextInAEL = Edge1;
+ Edge2->NextInAEL = Next;
+ if (Edge2->NextInAEL) Edge2->NextInAEL->PrevInAEL = Edge2;
+ Edge2->PrevInAEL = Prev;
+ if (Edge2->PrevInAEL) Edge2->PrevInAEL->NextInAEL = Edge2;
+ }
+
+ if (!Edge1->PrevInAEL) m_ActiveEdges = Edge1;
+ else if (!Edge2->PrevInAEL) m_ActiveEdges = Edge2;
+}
+//------------------------------------------------------------------------------
+
+void ClipperBase::UpdateEdgeIntoAEL(TEdge *&e)
+{
+ if (!e->NextInLML)
+ throw clipperException("UpdateEdgeIntoAEL: invalid call");
+
+ e->NextInLML->OutIdx = e->OutIdx;
+ TEdge* AelPrev = e->PrevInAEL;
+ TEdge* AelNext = e->NextInAEL;
+ if (AelPrev) AelPrev->NextInAEL = e->NextInLML;
+ else m_ActiveEdges = e->NextInLML;
+ if (AelNext) AelNext->PrevInAEL = e->NextInLML;
+ e->NextInLML->Side = e->Side;
+ e->NextInLML->WindDelta = e->WindDelta;
+ e->NextInLML->WindCnt = e->WindCnt;
+ e->NextInLML->WindCnt2 = e->WindCnt2;
+ e = e->NextInLML;
+ e->Curr = e->Bot;
+ e->PrevInAEL = AelPrev;
+ e->NextInAEL = AelNext;
+ if (!IsHorizontal(*e)) InsertScanbeam(e->Top.Y);
+}
+//------------------------------------------------------------------------------
+
+bool ClipperBase::LocalMinimaPending()
+{
+ return (m_CurrentLM != m_MinimaList.end());
+}
+
+//------------------------------------------------------------------------------
+// TClipper methods ...
+//------------------------------------------------------------------------------
+
+Clipper::Clipper(int initOptions) : ClipperBase() //constructor
+{
+ m_ExecuteLocked = false;
+ m_UseFullRange = false;
+ m_ReverseOutput = ((initOptions & ioReverseSolution) != 0);
+ m_StrictSimple = ((initOptions & ioStrictlySimple) != 0);
+ m_PreserveCollinear = ((initOptions & ioPreserveCollinear) != 0);
+ m_HasOpenPaths = false;
+#ifdef use_xyz
+ m_ZFill = 0;
+#endif
+
+ // Avoid uninitialized vars
+ m_ClipType = ctIntersection;
+ m_SortedEdges = NULL;
+ m_ClipFillType = pftEvenOdd;
+ m_SubjFillType = pftEvenOdd;
+ m_UsingPolyTree = true;
+}
+//------------------------------------------------------------------------------
+
+#ifdef use_xyz
+void Clipper::ZFillFunction(ZFillCallback zFillFunc)
+{
+ m_ZFill = zFillFunc;
+}
+//------------------------------------------------------------------------------
+#endif
+
+bool Clipper::Execute(ClipType clipType, Paths &solution, PolyFillType fillType)
+{
+ return Execute(clipType, solution, fillType, fillType);
+}
+//------------------------------------------------------------------------------
+
+bool Clipper::Execute(ClipType clipType, PolyTree &polytree, PolyFillType fillType)
+{
+ return Execute(clipType, polytree, fillType, fillType);
+}
+//------------------------------------------------------------------------------
+
+bool Clipper::Execute(ClipType clipType, Paths &solution,
+ PolyFillType subjFillType, PolyFillType clipFillType)
+{
+ if( m_ExecuteLocked ) return false;
+ if (m_HasOpenPaths)
+ throw clipperException("Error: PolyTree struct is needed for open path clipping.");
+ m_ExecuteLocked = true;
+ solution.resize(0);
+ m_SubjFillType = subjFillType;
+ m_ClipFillType = clipFillType;
+ m_ClipType = clipType;
+ m_UsingPolyTree = false;
+ bool succeeded = ExecuteInternal();
+ if (succeeded) BuildResult(solution);
+ DisposeAllOutRecs();
+ m_ExecuteLocked = false;
+ return succeeded;
+}
+//------------------------------------------------------------------------------
+
+bool Clipper::Execute(ClipType clipType, PolyTree& polytree,
+ PolyFillType subjFillType, PolyFillType clipFillType)
+{
+ if( m_ExecuteLocked ) return false;
+ m_ExecuteLocked = true;
+ m_SubjFillType = subjFillType;
+ m_ClipFillType = clipFillType;
+ m_ClipType = clipType;
+ m_UsingPolyTree = true;
+ bool succeeded = ExecuteInternal();
+ if (succeeded) BuildResult2(polytree);
+ DisposeAllOutRecs();
+ m_ExecuteLocked = false;
+ return succeeded;
+}
+//------------------------------------------------------------------------------
+
+void Clipper::FixHoleLinkage(OutRec &outrec)
+{
+ //skip OutRecs that (a) contain outermost polygons or
+ //(b) already have the correct owner/child linkage ...
+ if (!outrec.FirstLeft ||
+ (outrec.IsHole != outrec.FirstLeft->IsHole &&
+ outrec.FirstLeft->Pts)) return;
+
+ OutRec* orfl = outrec.FirstLeft;
+ while (orfl && ((orfl->IsHole == outrec.IsHole) || !orfl->Pts))
+ orfl = orfl->FirstLeft;
+ outrec.FirstLeft = orfl;
+}
+//------------------------------------------------------------------------------
+
+bool Clipper::ExecuteInternal()
+{
+ bool succeeded = true;
+ try {
+ Reset();
+ m_Maxima = MaximaList();
+ m_SortedEdges = 0;
+
+ succeeded = true;
+ cInt botY, topY;
+ if (!PopScanbeam(botY)) return false;
+ InsertLocalMinimaIntoAEL(botY);
+ while (PopScanbeam(topY) || LocalMinimaPending())
+ {
+ ProcessHorizontals();
+ ClearGhostJoins();
+ if (!ProcessIntersections(topY))
+ {
+ succeeded = false;
+ break;
+ }
+ ProcessEdgesAtTopOfScanbeam(topY);
+ botY = topY;
+ InsertLocalMinimaIntoAEL(botY);
+ }
+ }
+ catch(...)
+ {
+ succeeded = false;
+ }
+
+ if (succeeded)
+ {
+ //fix orientations ...
+ for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i)
+ {
+ OutRec *outRec = m_PolyOuts[i];
+ if (!outRec->Pts || outRec->IsOpen) continue;
+ if ((outRec->IsHole ^ m_ReverseOutput) == (Area(*outRec) > 0))
+ ReversePolyPtLinks(outRec->Pts);
+ }
+
+ if (!m_Joins.empty()) JoinCommonEdges();
+
+ //unfortunately FixupOutPolygon() must be done after JoinCommonEdges()
+ for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i)
+ {
+ OutRec *outRec = m_PolyOuts[i];
+ if (!outRec->Pts) continue;
+ if (outRec->IsOpen)
+ FixupOutPolyline(*outRec);
+ else
+ FixupOutPolygon(*outRec);
+ }
+
+ if (m_StrictSimple) DoSimplePolygons();
+ }
+
+ ClearJoins();
+ ClearGhostJoins();
+ return succeeded;
+}
+//------------------------------------------------------------------------------
+
+void Clipper::SetWindingCount(TEdge &edge)
+{
+ TEdge *e = edge.PrevInAEL;
+ //find the edge of the same polytype that immediately preceeds 'edge' in AEL
+ while (e && ((e->PolyTyp != edge.PolyTyp) || (e->WindDelta == 0))) e = e->PrevInAEL;
+ if (!e)
+ {
+ if (edge.WindDelta == 0)
+ {
+ PolyFillType pft = (edge.PolyTyp == ptSubject ? m_SubjFillType : m_ClipFillType);
+ edge.WindCnt = (pft == pftNegative ? -1 : 1);
+ }
+ else
+ edge.WindCnt = edge.WindDelta;
+ edge.WindCnt2 = 0;
+ e = m_ActiveEdges; //ie get ready to calc WindCnt2
+ }
+ else if (edge.WindDelta == 0 && m_ClipType != ctUnion)
+ {
+ edge.WindCnt = 1;
+ edge.WindCnt2 = e->WindCnt2;
+ e = e->NextInAEL; //ie get ready to calc WindCnt2
+ }
+ else if (IsEvenOddFillType(edge))
+ {
+ //EvenOdd filling ...
+ if (edge.WindDelta == 0)
+ {
+ //are we inside a subj polygon ...
+ bool Inside = true;
+ TEdge *e2 = e->PrevInAEL;
+ while (e2)
+ {
+ if (e2->PolyTyp == e->PolyTyp && e2->WindDelta != 0)
+ Inside = !Inside;
+ e2 = e2->PrevInAEL;
+ }
+ edge.WindCnt = (Inside ? 0 : 1);
+ }
+ else
+ {
+ edge.WindCnt = edge.WindDelta;
+ }
+ edge.WindCnt2 = e->WindCnt2;
+ e = e->NextInAEL; //ie get ready to calc WindCnt2
+ }
+ else
+ {
+ //nonZero, Positive or Negative filling ...
+ if (e->WindCnt * e->WindDelta < 0)
+ {
+ //prev edge is 'decreasing' WindCount (WC) toward zero
+ //so we're outside the previous polygon ...
+ if (Abs(e->WindCnt) > 1)
+ {
+ //outside prev poly but still inside another.
+ //when reversing direction of prev poly use the same WC
+ if (e->WindDelta * edge.WindDelta < 0) edge.WindCnt = e->WindCnt;
+ //otherwise continue to 'decrease' WC ...
+ else edge.WindCnt = e->WindCnt + edge.WindDelta;
+ }
+ else
+ //now outside all polys of same polytype so set own WC ...
+ edge.WindCnt = (edge.WindDelta == 0 ? 1 : edge.WindDelta);
+ } else
+ {
+ //prev edge is 'increasing' WindCount (WC) away from zero
+ //so we're inside the previous polygon ...
+ if (edge.WindDelta == 0)
+ edge.WindCnt = (e->WindCnt < 0 ? e->WindCnt - 1 : e->WindCnt + 1);
+ //if wind direction is reversing prev then use same WC
+ else if (e->WindDelta * edge.WindDelta < 0) edge.WindCnt = e->WindCnt;
+ //otherwise add to WC ...
+ else edge.WindCnt = e->WindCnt + edge.WindDelta;
+ }
+ edge.WindCnt2 = e->WindCnt2;
+ e = e->NextInAEL; //ie get ready to calc WindCnt2
+ }
+
+ //update WindCnt2 ...
+ if (IsEvenOddAltFillType(edge))
+ {
+ //EvenOdd filling ...
+ while (e != &edge)
+ {
+ if (e->WindDelta != 0)
+ edge.WindCnt2 = (edge.WindCnt2 == 0 ? 1 : 0);
+ e = e->NextInAEL;
+ }
+ } else
+ {
+ //nonZero, Positive or Negative filling ...
+ while ( e != &edge )
+ {
+ edge.WindCnt2 += e->WindDelta;
+ e = e->NextInAEL;
+ }
+ }
+}
+//------------------------------------------------------------------------------
+
+bool Clipper::IsEvenOddFillType(const TEdge& edge) const
+{
+ if (edge.PolyTyp == ptSubject)
+ return m_SubjFillType == pftEvenOdd; else
+ return m_ClipFillType == pftEvenOdd;
+}
+//------------------------------------------------------------------------------
+
+bool Clipper::IsEvenOddAltFillType(const TEdge& edge) const
+{
+ if (edge.PolyTyp == ptSubject)
+ return m_ClipFillType == pftEvenOdd; else
+ return m_SubjFillType == pftEvenOdd;
+}
+//------------------------------------------------------------------------------
+
+bool Clipper::IsContributing(const TEdge& edge) const
+{
+ PolyFillType pft, pft2;
+ if (edge.PolyTyp == ptSubject)
+ {
+ pft = m_SubjFillType;
+ pft2 = m_ClipFillType;
+ } else
+ {
+ pft = m_ClipFillType;
+ pft2 = m_SubjFillType;
+ }
+
+ switch(pft)
+ {
+ case pftEvenOdd:
+ //return false if a subj line has been flagged as inside a subj polygon
+ if (edge.WindDelta == 0 && edge.WindCnt != 1) return false;
+ break;
+ case pftNonZero:
+ if (Abs(edge.WindCnt) != 1) return false;
+ break;
+ case pftPositive:
+ if (edge.WindCnt != 1) return false;
+ break;
+ default: //pftNegative
+ if (edge.WindCnt != -1) return false;
+ }
+
+ switch(m_ClipType)
+ {
+ case ctIntersection:
+ switch(pft2)
+ {
+ case pftEvenOdd:
+ case pftNonZero:
+ return (edge.WindCnt2 != 0);
+ case pftPositive:
+ return (edge.WindCnt2 > 0);
+ default:
+ return (edge.WindCnt2 < 0);
+ }
+ break;
+ case ctUnion:
+ switch(pft2)
+ {
+ case pftEvenOdd:
+ case pftNonZero:
+ return (edge.WindCnt2 == 0);
+ case pftPositive:
+ return (edge.WindCnt2 <= 0);
+ default:
+ return (edge.WindCnt2 >= 0);
+ }
+ break;
+ case ctDifference:
+ if (edge.PolyTyp == ptSubject)
+ switch(pft2)
+ {
+ case pftEvenOdd:
+ case pftNonZero:
+ return (edge.WindCnt2 == 0);
+ case pftPositive:
+ return (edge.WindCnt2 <= 0);
+ default:
+ return (edge.WindCnt2 >= 0);
+ }
+ else
+ switch(pft2)
+ {
+ case pftEvenOdd:
+ case pftNonZero:
+ return (edge.WindCnt2 != 0);
+ case pftPositive:
+ return (edge.WindCnt2 > 0);
+ default:
+ return (edge.WindCnt2 < 0);
+ }
+ break;
+ case ctXor:
+ if (edge.WindDelta == 0) //XOr always contributing unless open
+ switch(pft2)
+ {
+ case pftEvenOdd:
+ case pftNonZero:
+ return (edge.WindCnt2 == 0);
+ case pftPositive:
+ return (edge.WindCnt2 <= 0);
+ default:
+ return (edge.WindCnt2 >= 0);
+ }
+ else
+ return true;
+ break;
+ default:
+ return true;
+ }
+}
+//------------------------------------------------------------------------------
+
+OutPt* Clipper::AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &Pt)
+{
+ OutPt* result;
+ TEdge *e, *prevE;
+ if (IsHorizontal(*e2) || ( e1->Dx > e2->Dx ))
+ {
+ result = AddOutPt(e1, Pt);
+ e2->OutIdx = e1->OutIdx;
+ e1->Side = esLeft;
+ e2->Side = esRight;
+ e = e1;
+ if (e->PrevInAEL == e2)
+ prevE = e2->PrevInAEL;
+ else
+ prevE = e->PrevInAEL;
+ } else
+ {
+ result = AddOutPt(e2, Pt);
+ e1->OutIdx = e2->OutIdx;
+ e1->Side = esRight;
+ e2->Side = esLeft;
+ e = e2;
+ if (e->PrevInAEL == e1)
+ prevE = e1->PrevInAEL;
+ else
+ prevE = e->PrevInAEL;
+ }
+
+ if (prevE && prevE->OutIdx >= 0)
+ {
+ cInt xPrev = TopX(*prevE, Pt.Y);
+ cInt xE = TopX(*e, Pt.Y);
+ if (xPrev == xE && (e->WindDelta != 0) && (prevE->WindDelta != 0) &&
+ SlopesEqual(IntPoint(xPrev, Pt.Y), prevE->Top, IntPoint(xE, Pt.Y), e->Top, m_UseFullRange))
+ {
+ OutPt* outPt = AddOutPt(prevE, Pt);
+ AddJoin(result, outPt, e->Top);
+ }
+ }
+ return result;
+}
+//------------------------------------------------------------------------------
+
+void Clipper::AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &Pt)
+{
+ AddOutPt( e1, Pt );
+ if (e2->WindDelta == 0) AddOutPt(e2, Pt);
+ if( e1->OutIdx == e2->OutIdx )
+ {
+ e1->OutIdx = Unassigned;
+ e2->OutIdx = Unassigned;
+ }
+ else if (e1->OutIdx < e2->OutIdx)
+ AppendPolygon(e1, e2);
+ else
+ AppendPolygon(e2, e1);
+}
+//------------------------------------------------------------------------------
+
+void Clipper::AddEdgeToSEL(TEdge *edge)
+{
+ //SEL pointers in PEdge are reused to build a list of horizontal edges.
+ //However, we don't need to worry about order with horizontal edge processing.
+ if( !m_SortedEdges )
+ {
+ m_SortedEdges = edge;
+ edge->PrevInSEL = 0;
+ edge->NextInSEL = 0;
+ }
+ else
+ {
+ edge->NextInSEL = m_SortedEdges;
+ edge->PrevInSEL = 0;
+ m_SortedEdges->PrevInSEL = edge;
+ m_SortedEdges = edge;
+ }
+}
+//------------------------------------------------------------------------------
+
+bool Clipper::PopEdgeFromSEL(TEdge *&edge)
+{
+ if (!m_SortedEdges) return false;
+ edge = m_SortedEdges;
+ DeleteFromSEL(m_SortedEdges);
+ return true;
+}
+//------------------------------------------------------------------------------
+
+void Clipper::CopyAELToSEL()
+{
+ TEdge* e = m_ActiveEdges;
+ m_SortedEdges = e;
+ while ( e )
+ {
+ e->PrevInSEL = e->PrevInAEL;
+ e->NextInSEL = e->NextInAEL;
+ e = e->NextInAEL;
+ }
+}
+//------------------------------------------------------------------------------
+
+void Clipper::AddJoin(OutPt *op1, OutPt *op2, const IntPoint OffPt)
+{
+ Join* j = new Join;
+ j->OutPt1 = op1;
+ j->OutPt2 = op2;
+ j->OffPt = OffPt;
+ m_Joins.push_back(j);
+}
+//------------------------------------------------------------------------------
+
+void Clipper::ClearJoins()
+{
+ for (JoinList::size_type i = 0; i < m_Joins.size(); i++)
+ delete m_Joins[i];
+ m_Joins.resize(0);
+}
+//------------------------------------------------------------------------------
+
+void Clipper::ClearGhostJoins()
+{
+ for (JoinList::size_type i = 0; i < m_GhostJoins.size(); i++)
+ delete m_GhostJoins[i];
+ m_GhostJoins.resize(0);
+}
+//------------------------------------------------------------------------------
+
+void Clipper::AddGhostJoin(OutPt *op, const IntPoint OffPt)
+{
+ Join* j = new Join;
+ j->OutPt1 = op;
+ j->OutPt2 = 0;
+ j->OffPt = OffPt;
+ m_GhostJoins.push_back(j);
+}
+//------------------------------------------------------------------------------
+
+void Clipper::InsertLocalMinimaIntoAEL(const cInt botY)
+{
+ const LocalMinimum *lm;
+ while (PopLocalMinima(botY, lm))
+ {
+ TEdge* lb = lm->LeftBound;
+ TEdge* rb = lm->RightBound;
+
+ OutPt *Op1 = 0;
+ if (!lb)
+ {
+ //nb: don't insert LB into either AEL or SEL
+ InsertEdgeIntoAEL(rb, 0);
+ SetWindingCount(*rb);
+ if (IsContributing(*rb))
+ Op1 = AddOutPt(rb, rb->Bot);
+ }
+ else if (!rb)
+ {
+ InsertEdgeIntoAEL(lb, 0);
+ SetWindingCount(*lb);
+ if (IsContributing(*lb))
+ Op1 = AddOutPt(lb, lb->Bot);
+ InsertScanbeam(lb->Top.Y);
+ }
+ else
+ {
+ InsertEdgeIntoAEL(lb, 0);
+ InsertEdgeIntoAEL(rb, lb);
+ SetWindingCount( *lb );
+ rb->WindCnt = lb->WindCnt;
+ rb->WindCnt2 = lb->WindCnt2;
+ if (IsContributing(*lb))
+ Op1 = AddLocalMinPoly(lb, rb, lb->Bot);
+ InsertScanbeam(lb->Top.Y);
+ }
+
+ if (rb)
+ {
+ if (IsHorizontal(*rb))
+ {
+ AddEdgeToSEL(rb);
+ if (rb->NextInLML)
+ InsertScanbeam(rb->NextInLML->Top.Y);
+ }
+ else InsertScanbeam( rb->Top.Y );
+ }
+
+ if (!lb || !rb) continue;
+
+ //if any output polygons share an edge, they'll need joining later ...
+ if (Op1 && IsHorizontal(*rb) &&
+ m_GhostJoins.size() > 0 && (rb->WindDelta != 0))
+ {
+ for (JoinList::size_type i = 0; i < m_GhostJoins.size(); ++i)
+ {
+ Join* jr = m_GhostJoins[i];
+ //if the horizontal Rb and a 'ghost' horizontal overlap, then convert
+ //the 'ghost' join to a real join ready for later ...
+ if (HorzSegmentsOverlap(jr->OutPt1->Pt.X, jr->OffPt.X, rb->Bot.X, rb->Top.X))
+ AddJoin(jr->OutPt1, Op1, jr->OffPt);
+ }
+ }
+
+ if (lb->OutIdx >= 0 && lb->PrevInAEL &&
+ lb->PrevInAEL->Curr.X == lb->Bot.X &&
+ lb->PrevInAEL->OutIdx >= 0 &&
+ SlopesEqual(lb->PrevInAEL->Bot, lb->PrevInAEL->Top, lb->Curr, lb->Top, m_UseFullRange) &&
+ (lb->WindDelta != 0) && (lb->PrevInAEL->WindDelta != 0))
+ {
+ OutPt *Op2 = AddOutPt(lb->PrevInAEL, lb->Bot);
+ AddJoin(Op1, Op2, lb->Top);
+ }
+
+ if(lb->NextInAEL != rb)
+ {
+
+ if (rb->OutIdx >= 0 && rb->PrevInAEL->OutIdx >= 0 &&
+ SlopesEqual(rb->PrevInAEL->Curr, rb->PrevInAEL->Top, rb->Curr, rb->Top, m_UseFullRange) &&
+ (rb->WindDelta != 0) && (rb->PrevInAEL->WindDelta != 0))
+ {
+ OutPt *Op2 = AddOutPt(rb->PrevInAEL, rb->Bot);
+ AddJoin(Op1, Op2, rb->Top);
+ }
+
+ TEdge* e = lb->NextInAEL;
+ if (e)
+ {
+ while( e != rb )
+ {
+ //nb: For calculating winding counts etc, IntersectEdges() assumes
+ //that param1 will be to the Right of param2 ABOVE the intersection ...
+ IntersectEdges(rb , e , lb->Curr); //order important here
+ e = e->NextInAEL;
+ }
+ }
+ }
+
+ }
+}
+//------------------------------------------------------------------------------
+
+void Clipper::DeleteFromSEL(TEdge *e)
+{
+ TEdge* SelPrev = e->PrevInSEL;
+ TEdge* SelNext = e->NextInSEL;
+ if( !SelPrev && !SelNext && (e != m_SortedEdges) ) return; //already deleted
+ if( SelPrev ) SelPrev->NextInSEL = SelNext;
+ else m_SortedEdges = SelNext;
+ if( SelNext ) SelNext->PrevInSEL = SelPrev;
+ e->NextInSEL = 0;
+ e->PrevInSEL = 0;
+}
+//------------------------------------------------------------------------------
+
+#ifdef use_xyz
+void Clipper::SetZ(IntPoint& pt, TEdge& e1, TEdge& e2)
+{
+ if (pt.Z != 0 || !m_ZFill) return;
+ else if (pt == e1.Bot) pt.Z = e1.Bot.Z;
+ else if (pt == e1.Top) pt.Z = e1.Top.Z;
+ else if (pt == e2.Bot) pt.Z = e2.Bot.Z;
+ else if (pt == e2.Top) pt.Z = e2.Top.Z;
+ else (*m_ZFill)(e1.Bot, e1.Top, e2.Bot, e2.Top, pt);
+}
+//------------------------------------------------------------------------------
+#endif
+
+void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &Pt)
+{
+ bool e1Contributing = ( e1->OutIdx >= 0 );
+ bool e2Contributing = ( e2->OutIdx >= 0 );
+
+#ifdef use_xyz
+ SetZ(Pt, *e1, *e2);
+#endif
+
+#ifdef use_lines
+ //if either edge is on an OPEN path ...
+ if (e1->WindDelta == 0 || e2->WindDelta == 0)
+ {
+ //ignore subject-subject open path intersections UNLESS they
+ //are both open paths, AND they are both 'contributing maximas' ...
+ if (e1->WindDelta == 0 && e2->WindDelta == 0) return;
+
+ //if intersecting a subj line with a subj poly ...
+ else if (e1->PolyTyp == e2->PolyTyp &&
+ e1->WindDelta != e2->WindDelta && m_ClipType == ctUnion)
+ {
+ if (e1->WindDelta == 0)
+ {
+ if (e2Contributing)
+ {
+ AddOutPt(e1, Pt);
+ if (e1Contributing) e1->OutIdx = Unassigned;
+ }
+ }
+ else
+ {
+ if (e1Contributing)
+ {
+ AddOutPt(e2, Pt);
+ if (e2Contributing) e2->OutIdx = Unassigned;
+ }
+ }
+ }
+ else if (e1->PolyTyp != e2->PolyTyp)
+ {
+ //toggle subj open path OutIdx on/off when Abs(clip.WndCnt) == 1 ...
+ if ((e1->WindDelta == 0) && abs(e2->WindCnt) == 1 &&
+ (m_ClipType != ctUnion || e2->WindCnt2 == 0))
+ {
+ AddOutPt(e1, Pt);
+ if (e1Contributing) e1->OutIdx = Unassigned;
+ }
+ else if ((e2->WindDelta == 0) && (abs(e1->WindCnt) == 1) &&
+ (m_ClipType != ctUnion || e1->WindCnt2 == 0))
+ {
+ AddOutPt(e2, Pt);
+ if (e2Contributing) e2->OutIdx = Unassigned;
+ }
+ }
+ return;
+ }
+#endif
+
+ //update winding counts...
+ //assumes that e1 will be to the Right of e2 ABOVE the intersection
+ if ( e1->PolyTyp == e2->PolyTyp )
+ {
+ if ( IsEvenOddFillType( *e1) )
+ {
+ int oldE1WindCnt = e1->WindCnt;
+ e1->WindCnt = e2->WindCnt;
+ e2->WindCnt = oldE1WindCnt;
+ } else
+ {
+ if (e1->WindCnt + e2->WindDelta == 0 ) e1->WindCnt = -e1->WindCnt;
+ else e1->WindCnt += e2->WindDelta;
+ if ( e2->WindCnt - e1->WindDelta == 0 ) e2->WindCnt = -e2->WindCnt;
+ else e2->WindCnt -= e1->WindDelta;
+ }
+ } else
+ {
+ if (!IsEvenOddFillType(*e2)) e1->WindCnt2 += e2->WindDelta;
+ else e1->WindCnt2 = ( e1->WindCnt2 == 0 ) ? 1 : 0;
+ if (!IsEvenOddFillType(*e1)) e2->WindCnt2 -= e1->WindDelta;
+ else e2->WindCnt2 = ( e2->WindCnt2 == 0 ) ? 1 : 0;
+ }
+
+ PolyFillType e1FillType, e2FillType, e1FillType2, e2FillType2;
+ if (e1->PolyTyp == ptSubject)
+ {
+ e1FillType = m_SubjFillType;
+ e1FillType2 = m_ClipFillType;
+ } else
+ {
+ e1FillType = m_ClipFillType;
+ e1FillType2 = m_SubjFillType;
+ }
+ if (e2->PolyTyp == ptSubject)
+ {
+ e2FillType = m_SubjFillType;
+ e2FillType2 = m_ClipFillType;
+ } else
+ {
+ e2FillType = m_ClipFillType;
+ e2FillType2 = m_SubjFillType;
+ }
+
+ cInt e1Wc, e2Wc;
+ switch (e1FillType)
+ {
+ case pftPositive: e1Wc = e1->WindCnt; break;
+ case pftNegative: e1Wc = -e1->WindCnt; break;
+ default: e1Wc = Abs(e1->WindCnt);
+ }
+ switch(e2FillType)
+ {
+ case pftPositive: e2Wc = e2->WindCnt; break;
+ case pftNegative: e2Wc = -e2->WindCnt; break;
+ default: e2Wc = Abs(e2->WindCnt);
+ }
+
+ if ( e1Contributing && e2Contributing )
+ {
+ if ((e1Wc != 0 && e1Wc != 1) || (e2Wc != 0 && e2Wc != 1) ||
+ (e1->PolyTyp != e2->PolyTyp && m_ClipType != ctXor) )
+ {
+ AddLocalMaxPoly(e1, e2, Pt);
+ }
+ else
+ {
+ AddOutPt(e1, Pt);
+ AddOutPt(e2, Pt);
+ SwapSides( *e1 , *e2 );
+ SwapPolyIndexes( *e1 , *e2 );
+ }
+ }
+ else if ( e1Contributing )
+ {
+ if (e2Wc == 0 || e2Wc == 1)
+ {
+ AddOutPt(e1, Pt);
+ SwapSides(*e1, *e2);
+ SwapPolyIndexes(*e1, *e2);
+ }
+ }
+ else if ( e2Contributing )
+ {
+ if (e1Wc == 0 || e1Wc == 1)
+ {
+ AddOutPt(e2, Pt);
+ SwapSides(*e1, *e2);
+ SwapPolyIndexes(*e1, *e2);
+ }
+ }
+ else if ( (e1Wc == 0 || e1Wc == 1) && (e2Wc == 0 || e2Wc == 1))
+ {
+ //neither edge is currently contributing ...
+
+ cInt e1Wc2, e2Wc2;
+ switch (e1FillType2)
+ {
+ case pftPositive: e1Wc2 = e1->WindCnt2; break;
+ case pftNegative : e1Wc2 = -e1->WindCnt2; break;
+ default: e1Wc2 = Abs(e1->WindCnt2);
+ }
+ switch (e2FillType2)
+ {
+ case pftPositive: e2Wc2 = e2->WindCnt2; break;
+ case pftNegative: e2Wc2 = -e2->WindCnt2; break;
+ default: e2Wc2 = Abs(e2->WindCnt2);
+ }
+
+ if (e1->PolyTyp != e2->PolyTyp)
+ {
+ AddLocalMinPoly(e1, e2, Pt);
+ }
+ else if (e1Wc == 1 && e2Wc == 1)
+ switch( m_ClipType ) {
+ case ctIntersection:
+ if (e1Wc2 > 0 && e2Wc2 > 0)
+ AddLocalMinPoly(e1, e2, Pt);
+ break;
+ case ctUnion:
+ if ( e1Wc2 <= 0 && e2Wc2 <= 0 )
+ AddLocalMinPoly(e1, e2, Pt);
+ break;
+ case ctDifference:
+ if (((e1->PolyTyp == ptClip) && (e1Wc2 > 0) && (e2Wc2 > 0)) ||
+ ((e1->PolyTyp == ptSubject) && (e1Wc2 <= 0) && (e2Wc2 <= 0)))
+ AddLocalMinPoly(e1, e2, Pt);
+ break;
+ case ctXor:
+ AddLocalMinPoly(e1, e2, Pt);
+ }
+ else
+ SwapSides( *e1, *e2 );
+ }
+}
+//------------------------------------------------------------------------------
+
+void Clipper::SetHoleState(TEdge *e, OutRec *outrec)
+{
+ TEdge *e2 = e->PrevInAEL;
+ TEdge *eTmp = 0;
+ while (e2)
+ {
+ if (e2->OutIdx >= 0 && e2->WindDelta != 0)
+ {
+ if (!eTmp) eTmp = e2;
+ else if (eTmp->OutIdx == e2->OutIdx) eTmp = 0;
+ }
+ e2 = e2->PrevInAEL;
+ }
+ if (!eTmp)
+ {
+ outrec->FirstLeft = 0;
+ outrec->IsHole = false;
+ }
+ else
+ {
+ outrec->FirstLeft = m_PolyOuts[eTmp->OutIdx];
+ outrec->IsHole = !outrec->FirstLeft->IsHole;
+ }
+}
+//------------------------------------------------------------------------------
+
+OutRec* GetLowermostRec(OutRec *outRec1, OutRec *outRec2)
+{
+ //work out which polygon fragment has the correct hole state ...
+ if (!outRec1->BottomPt)
+ outRec1->BottomPt = GetBottomPt(outRec1->Pts);
+ if (!outRec2->BottomPt)
+ outRec2->BottomPt = GetBottomPt(outRec2->Pts);
+ OutPt *OutPt1 = outRec1->BottomPt;
+ OutPt *OutPt2 = outRec2->BottomPt;
+ if (OutPt1->Pt.Y > OutPt2->Pt.Y) return outRec1;
+ else if (OutPt1->Pt.Y < OutPt2->Pt.Y) return outRec2;
+ else if (OutPt1->Pt.X < OutPt2->Pt.X) return outRec1;
+ else if (OutPt1->Pt.X > OutPt2->Pt.X) return outRec2;
+ else if (OutPt1->Next == OutPt1) return outRec2;
+ else if (OutPt2->Next == OutPt2) return outRec1;
+ else if (FirstIsBottomPt(OutPt1, OutPt2)) return outRec1;
+ else return outRec2;
+}
+//------------------------------------------------------------------------------
+
+bool OutRec1RightOfOutRec2(OutRec* outRec1, OutRec* outRec2)
+{
+ do
+ {
+ outRec1 = outRec1->FirstLeft;
+ if (outRec1 == outRec2) return true;
+ } while (outRec1);
+ return false;
+}
+//------------------------------------------------------------------------------
+
+OutRec* Clipper::GetOutRec(int Idx)
+{
+ OutRec* outrec = m_PolyOuts[Idx];
+ while (outrec != m_PolyOuts[outrec->Idx])
+ outrec = m_PolyOuts[outrec->Idx];
+ return outrec;
+}
+//------------------------------------------------------------------------------
+
+void Clipper::AppendPolygon(TEdge *e1, TEdge *e2)
+{
+ //get the start and ends of both output polygons ...
+ OutRec *outRec1 = m_PolyOuts[e1->OutIdx];
+ OutRec *outRec2 = m_PolyOuts[e2->OutIdx];
+
+ OutRec *holeStateRec;
+ if (OutRec1RightOfOutRec2(outRec1, outRec2))
+ holeStateRec = outRec2;
+ else if (OutRec1RightOfOutRec2(outRec2, outRec1))
+ holeStateRec = outRec1;
+ else
+ holeStateRec = GetLowermostRec(outRec1, outRec2);
+
+ //get the start and ends of both output polygons and
+ //join e2 poly onto e1 poly and delete pointers to e2 ...
+
+ OutPt* p1_lft = outRec1->Pts;
+ OutPt* p1_rt = p1_lft->Prev;
+ OutPt* p2_lft = outRec2->Pts;
+ OutPt* p2_rt = p2_lft->Prev;
+
+ //join e2 poly onto e1 poly and delete pointers to e2 ...
+ if( e1->Side == esLeft )
+ {
+ if( e2->Side == esLeft )
+ {
+ //z y x a b c
+ ReversePolyPtLinks(p2_lft);
+ p2_lft->Next = p1_lft;
+ p1_lft->Prev = p2_lft;
+ p1_rt->Next = p2_rt;
+ p2_rt->Prev = p1_rt;
+ outRec1->Pts = p2_rt;
+ } else
+ {
+ //x y z a b c
+ p2_rt->Next = p1_lft;
+ p1_lft->Prev = p2_rt;
+ p2_lft->Prev = p1_rt;
+ p1_rt->Next = p2_lft;
+ outRec1->Pts = p2_lft;
+ }
+ } else
+ {
+ if( e2->Side == esRight )
+ {
+ //a b c z y x
+ ReversePolyPtLinks(p2_lft);
+ p1_rt->Next = p2_rt;
+ p2_rt->Prev = p1_rt;
+ p2_lft->Next = p1_lft;
+ p1_lft->Prev = p2_lft;
+ } else
+ {
+ //a b c x y z
+ p1_rt->Next = p2_lft;
+ p2_lft->Prev = p1_rt;
+ p1_lft->Prev = p2_rt;
+ p2_rt->Next = p1_lft;
+ }
+ }
+
+ outRec1->BottomPt = 0;
+ if (holeStateRec == outRec2)
+ {
+ if (outRec2->FirstLeft != outRec1)
+ outRec1->FirstLeft = outRec2->FirstLeft;
+ outRec1->IsHole = outRec2->IsHole;
+ }
+ outRec2->Pts = 0;
+ outRec2->BottomPt = 0;
+ outRec2->FirstLeft = outRec1;
+
+ int OKIdx = e1->OutIdx;
+ int ObsoleteIdx = e2->OutIdx;
+
+ e1->OutIdx = Unassigned; //nb: safe because we only get here via AddLocalMaxPoly
+ e2->OutIdx = Unassigned;
+
+ TEdge* e = m_ActiveEdges;
+ while( e )
+ {
+ if( e->OutIdx == ObsoleteIdx )
+ {
+ e->OutIdx = OKIdx;
+ e->Side = e1->Side;
+ break;
+ }
+ e = e->NextInAEL;
+ }
+
+ outRec2->Idx = outRec1->Idx;
+}
+//------------------------------------------------------------------------------
+
+OutPt* Clipper::AddOutPt(TEdge *e, const IntPoint &pt)
+{
+ if( e->OutIdx < 0 )
+ {
+ OutRec *outRec = CreateOutRec();
+ outRec->IsOpen = (e->WindDelta == 0);
+ OutPt* newOp = new OutPt;
+ outRec->Pts = newOp;
+ newOp->Idx = outRec->Idx;
+ newOp->Pt = pt;
+ newOp->Next = newOp;
+ newOp->Prev = newOp;
+ if (!outRec->IsOpen)
+ SetHoleState(e, outRec);
+ e->OutIdx = outRec->Idx;
+ return newOp;
+ } else
+ {
+ OutRec *outRec = m_PolyOuts[e->OutIdx];
+ //OutRec.Pts is the 'Left-most' point & OutRec.Pts.Prev is the 'Right-most'
+ OutPt* op = outRec->Pts;
+
+ bool ToFront = (e->Side == esLeft);
+ if (ToFront && (pt == op->Pt)) return op;
+ else if (!ToFront && (pt == op->Prev->Pt)) return op->Prev;
+
+ OutPt* newOp = new OutPt;
+ newOp->Idx = outRec->Idx;
+ newOp->Pt = pt;
+ newOp->Next = op;
+ newOp->Prev = op->Prev;
+ newOp->Prev->Next = newOp;
+ op->Prev = newOp;
+ if (ToFront) outRec->Pts = newOp;
+ return newOp;
+ }
+}
+//------------------------------------------------------------------------------
+
+OutPt* Clipper::GetLastOutPt(TEdge *e)
+{
+ OutRec *outRec = m_PolyOuts[e->OutIdx];
+ if (e->Side == esLeft)
+ return outRec->Pts;
+ else
+ return outRec->Pts->Prev;
+}
+//------------------------------------------------------------------------------
+
+void Clipper::ProcessHorizontals()
+{
+ TEdge* horzEdge;
+ while (PopEdgeFromSEL(horzEdge))
+ ProcessHorizontal(horzEdge);
+}
+//------------------------------------------------------------------------------
+
+inline bool IsMinima(TEdge *e)
+{
+ return e && (e->Prev->NextInLML != e) && (e->Next->NextInLML != e);
+}
+//------------------------------------------------------------------------------
+
+inline bool IsMaxima(TEdge *e, const cInt Y)
+{
+ return e && e->Top.Y == Y && !e->NextInLML;
+}
+//------------------------------------------------------------------------------
+
+inline bool IsIntermediate(TEdge *e, const cInt Y)
+{
+ return e->Top.Y == Y && e->NextInLML;
+}
+//------------------------------------------------------------------------------
+
+TEdge *GetMaximaPair(TEdge *e)
+{
+ if ((e->Next->Top == e->Top) && !e->Next->NextInLML)
+ return e->Next;
+ else if ((e->Prev->Top == e->Top) && !e->Prev->NextInLML)
+ return e->Prev;
+ else return 0;
+}
+//------------------------------------------------------------------------------
+
+TEdge *GetMaximaPairEx(TEdge *e)
+{
+ //as GetMaximaPair() but returns 0 if MaxPair isn't in AEL (unless it's horizontal)
+ TEdge* result = GetMaximaPair(e);
+ if (result && (result->OutIdx == Skip ||
+ (result->NextInAEL == result->PrevInAEL && !IsHorizontal(*result)))) return 0;
+ return result;
+}
+//------------------------------------------------------------------------------
+
+void Clipper::SwapPositionsInSEL(TEdge *Edge1, TEdge *Edge2)
+{
+ if( !( Edge1->NextInSEL ) && !( Edge1->PrevInSEL ) ) return;
+ if( !( Edge2->NextInSEL ) && !( Edge2->PrevInSEL ) ) return;
+
+ if( Edge1->NextInSEL == Edge2 )
+ {
+ TEdge* Next = Edge2->NextInSEL;
+ if( Next ) Next->PrevInSEL = Edge1;
+ TEdge* Prev = Edge1->PrevInSEL;
+ if( Prev ) Prev->NextInSEL = Edge2;
+ Edge2->PrevInSEL = Prev;
+ Edge2->NextInSEL = Edge1;
+ Edge1->PrevInSEL = Edge2;
+ Edge1->NextInSEL = Next;
+ }
+ else if( Edge2->NextInSEL == Edge1 )
+ {
+ TEdge* Next = Edge1->NextInSEL;
+ if( Next ) Next->PrevInSEL = Edge2;
+ TEdge* Prev = Edge2->PrevInSEL;
+ if( Prev ) Prev->NextInSEL = Edge1;
+ Edge1->PrevInSEL = Prev;
+ Edge1->NextInSEL = Edge2;
+ Edge2->PrevInSEL = Edge1;
+ Edge2->NextInSEL = Next;
+ }
+ else
+ {
+ TEdge* Next = Edge1->NextInSEL;
+ TEdge* Prev = Edge1->PrevInSEL;
+ Edge1->NextInSEL = Edge2->NextInSEL;
+ if( Edge1->NextInSEL ) Edge1->NextInSEL->PrevInSEL = Edge1;
+ Edge1->PrevInSEL = Edge2->PrevInSEL;
+ if( Edge1->PrevInSEL ) Edge1->PrevInSEL->NextInSEL = Edge1;
+ Edge2->NextInSEL = Next;
+ if( Edge2->NextInSEL ) Edge2->NextInSEL->PrevInSEL = Edge2;
+ Edge2->PrevInSEL = Prev;
+ if( Edge2->PrevInSEL ) Edge2->PrevInSEL->NextInSEL = Edge2;
+ }
+
+ if( !Edge1->PrevInSEL ) m_SortedEdges = Edge1;
+ else if( !Edge2->PrevInSEL ) m_SortedEdges = Edge2;
+}
+//------------------------------------------------------------------------------
+
+TEdge* GetNextInAEL(TEdge *e, Direction dir)
+{
+ return dir == dLeftToRight ? e->NextInAEL : e->PrevInAEL;
+}
+//------------------------------------------------------------------------------
+
+void GetHorzDirection(TEdge& HorzEdge, Direction& Dir, cInt& Left, cInt& Right)
+{
+ if (HorzEdge.Bot.X < HorzEdge.Top.X)
+ {
+ Left = HorzEdge.Bot.X;
+ Right = HorzEdge.Top.X;
+ Dir = dLeftToRight;
+ } else
+ {
+ Left = HorzEdge.Top.X;
+ Right = HorzEdge.Bot.X;
+ Dir = dRightToLeft;
+ }
+}
+//------------------------------------------------------------------------
+
+/*******************************************************************************
+* Notes: Horizontal edges (HEs) at scanline intersections (ie at the Top or *
+* Bottom of a scanbeam) are processed as if layered. The order in which HEs *
+* are processed doesn't matter. HEs intersect with other HE Bot.Xs only [#] *
+* (or they could intersect with Top.Xs only, ie EITHER Bot.Xs OR Top.Xs), *
+* and with other non-horizontal edges [*]. Once these intersections are *
+* processed, intermediate HEs then 'promote' the Edge above (NextInLML) into *
+* the AEL. These 'promoted' edges may in turn intersect [%] with other HEs. *
+*******************************************************************************/
+
+void Clipper::ProcessHorizontal(TEdge *horzEdge)
+{
+ Direction dir;
+ cInt horzLeft, horzRight;
+ bool IsOpen = (horzEdge->WindDelta == 0);
+
+ GetHorzDirection(*horzEdge, dir, horzLeft, horzRight);
+
+ TEdge* eLastHorz = horzEdge, *eMaxPair = 0;
+ while (eLastHorz->NextInLML && IsHorizontal(*eLastHorz->NextInLML))
+ eLastHorz = eLastHorz->NextInLML;
+ if (!eLastHorz->NextInLML)
+ eMaxPair = GetMaximaPair(eLastHorz);
+
+ MaximaList::const_iterator maxIt;
+ MaximaList::const_reverse_iterator maxRit;
+ if (m_Maxima.size() > 0)
+ {
+ //get the first maxima in range (X) ...
+ if (dir == dLeftToRight)
+ {
+ maxIt = m_Maxima.begin();
+ while (maxIt != m_Maxima.end() && *maxIt <= horzEdge->Bot.X) maxIt++;
+ if (maxIt != m_Maxima.end() && *maxIt >= eLastHorz->Top.X)
+ maxIt = m_Maxima.end();
+ }
+ else
+ {
+ maxRit = m_Maxima.rbegin();
+ while (maxRit != m_Maxima.rend() && *maxRit > horzEdge->Bot.X) maxRit++;
+ if (maxRit != m_Maxima.rend() && *maxRit <= eLastHorz->Top.X)
+ maxRit = m_Maxima.rend();
+ }
+ }
+
+ OutPt* op1 = 0;
+
+ for (;;) //loop through consec. horizontal edges
+ {
+
+ bool IsLastHorz = (horzEdge == eLastHorz);
+ TEdge* e = GetNextInAEL(horzEdge, dir);
+ while(e)
+ {
+
+ //this code block inserts extra coords into horizontal edges (in output
+ //polygons) whereever maxima touch these horizontal edges. This helps
+ //'simplifying' polygons (ie if the Simplify property is set).
+ if (m_Maxima.size() > 0)
+ {
+ if (dir == dLeftToRight)
+ {
+ while (maxIt != m_Maxima.end() && *maxIt < e->Curr.X)
+ {
+ if (horzEdge->OutIdx >= 0 && !IsOpen)
+ AddOutPt(horzEdge, IntPoint(*maxIt, horzEdge->Bot.Y));
+ maxIt++;
+ }
+ }
+ else
+ {
+ while (maxRit != m_Maxima.rend() && *maxRit > e->Curr.X)
+ {
+ if (horzEdge->OutIdx >= 0 && !IsOpen)
+ AddOutPt(horzEdge, IntPoint(*maxRit, horzEdge->Bot.Y));
+ maxRit++;
+ }
+ }
+ };
+
+ if ((dir == dLeftToRight && e->Curr.X > horzRight) ||
+ (dir == dRightToLeft && e->Curr.X < horzLeft)) break;
+
+ //Also break if we've got to the end of an intermediate horizontal edge ...
+ //nb: Smaller Dx's are to the right of larger Dx's ABOVE the horizontal.
+ if (e->Curr.X == horzEdge->Top.X && horzEdge->NextInLML &&
+ e->Dx < horzEdge->NextInLML->Dx) break;
+
+ if (horzEdge->OutIdx >= 0 && !IsOpen) //note: may be done multiple times
+ {
+ op1 = AddOutPt(horzEdge, e->Curr);
+ TEdge* eNextHorz = m_SortedEdges;
+ while (eNextHorz)
+ {
+ if (eNextHorz->OutIdx >= 0 &&
+ HorzSegmentsOverlap(horzEdge->Bot.X,
+ horzEdge->Top.X, eNextHorz->Bot.X, eNextHorz->Top.X))
+ {
+ OutPt* op2 = GetLastOutPt(eNextHorz);
+ AddJoin(op2, op1, eNextHorz->Top);
+ }
+ eNextHorz = eNextHorz->NextInSEL;
+ }
+ AddGhostJoin(op1, horzEdge->Bot);
+ }
+
+ //OK, so far we're still in range of the horizontal Edge but make sure
+ //we're at the last of consec. horizontals when matching with eMaxPair
+ if(e == eMaxPair && IsLastHorz)
+ {
+ if (horzEdge->OutIdx >= 0)
+ AddLocalMaxPoly(horzEdge, eMaxPair, horzEdge->Top);
+ DeleteFromAEL(horzEdge);
+ DeleteFromAEL(eMaxPair);
+ return;
+ }
+
+ if(dir == dLeftToRight)
+ {
+ IntPoint Pt = IntPoint(e->Curr.X, horzEdge->Curr.Y);
+ IntersectEdges(horzEdge, e, Pt);
+ }
+ else
+ {
+ IntPoint Pt = IntPoint(e->Curr.X, horzEdge->Curr.Y);
+ IntersectEdges( e, horzEdge, Pt);
+ }
+ TEdge* eNext = GetNextInAEL(e, dir);
+ SwapPositionsInAEL( horzEdge, e );
+ e = eNext;
+ } //end while(e)
+
+ //Break out of loop if HorzEdge.NextInLML is not also horizontal ...
+ if (!horzEdge->NextInLML || !IsHorizontal(*horzEdge->NextInLML)) break;
+
+ UpdateEdgeIntoAEL(horzEdge);
+ if (horzEdge->OutIdx >= 0) AddOutPt(horzEdge, horzEdge->Bot);
+ GetHorzDirection(*horzEdge, dir, horzLeft, horzRight);
+
+ } //end for (;;)
+
+ if (horzEdge->OutIdx >= 0 && !op1)
+ {
+ op1 = GetLastOutPt(horzEdge);
+ TEdge* eNextHorz = m_SortedEdges;
+ while (eNextHorz)
+ {
+ if (eNextHorz->OutIdx >= 0 &&
+ HorzSegmentsOverlap(horzEdge->Bot.X,
+ horzEdge->Top.X, eNextHorz->Bot.X, eNextHorz->Top.X))
+ {
+ OutPt* op2 = GetLastOutPt(eNextHorz);
+ AddJoin(op2, op1, eNextHorz->Top);
+ }
+ eNextHorz = eNextHorz->NextInSEL;
+ }
+ AddGhostJoin(op1, horzEdge->Top);
+ }
+
+ if (horzEdge->NextInLML)
+ {
+ if(horzEdge->OutIdx >= 0)
+ {
+ op1 = AddOutPt( horzEdge, horzEdge->Top);
+ UpdateEdgeIntoAEL(horzEdge);
+ if (horzEdge->WindDelta == 0) return;
+ //nb: HorzEdge is no longer horizontal here
+ TEdge* ePrev = horzEdge->PrevInAEL;
+ TEdge* eNext = horzEdge->NextInAEL;
+ if (ePrev && ePrev->Curr.X == horzEdge->Bot.X &&
+ ePrev->Curr.Y == horzEdge->Bot.Y && ePrev->WindDelta != 0 &&
+ (ePrev->OutIdx >= 0 && ePrev->Curr.Y > ePrev->Top.Y &&
+ SlopesEqual(*horzEdge, *ePrev, m_UseFullRange)))
+ {
+ OutPt* op2 = AddOutPt(ePrev, horzEdge->Bot);
+ AddJoin(op1, op2, horzEdge->Top);
+ }
+ else if (eNext && eNext->Curr.X == horzEdge->Bot.X &&
+ eNext->Curr.Y == horzEdge->Bot.Y && eNext->WindDelta != 0 &&
+ eNext->OutIdx >= 0 && eNext->Curr.Y > eNext->Top.Y &&
+ SlopesEqual(*horzEdge, *eNext, m_UseFullRange))
+ {
+ OutPt* op2 = AddOutPt(eNext, horzEdge->Bot);
+ AddJoin(op1, op2, horzEdge->Top);
+ }
+ }
+ else
+ UpdateEdgeIntoAEL(horzEdge);
+ }
+ else
+ {
+ if (horzEdge->OutIdx >= 0) AddOutPt(horzEdge, horzEdge->Top);
+ DeleteFromAEL(horzEdge);
+ }
+}
+//------------------------------------------------------------------------------
+
+bool Clipper::ProcessIntersections(const cInt topY)
+{
+ if( !m_ActiveEdges ) return true;
+ try {
+ BuildIntersectList(topY);
+ size_t IlSize = m_IntersectList.size();
+ if (IlSize == 0) return true;
+ if (IlSize == 1 || FixupIntersectionOrder()) ProcessIntersectList();
+ else return false;
+ }
+ catch(...)
+ {
+ m_SortedEdges = 0;
+ DisposeIntersectNodes();
+ throw clipperException("ProcessIntersections error");
+ }
+ m_SortedEdges = 0;
+ return true;
+}
+//------------------------------------------------------------------------------
+
+void Clipper::DisposeIntersectNodes()
+{
+ for (size_t i = 0; i < m_IntersectList.size(); ++i )
+ delete m_IntersectList[i];
+ m_IntersectList.clear();
+}
+//------------------------------------------------------------------------------
+
+void Clipper::BuildIntersectList(const cInt topY)
+{
+ if ( !m_ActiveEdges ) return;
+
+ //prepare for sorting ...
+ TEdge* e = m_ActiveEdges;
+ m_SortedEdges = e;
+ while( e )
+ {
+ e->PrevInSEL = e->PrevInAEL;
+ e->NextInSEL = e->NextInAEL;
+ e->Curr.X = TopX( *e, topY );
+ e = e->NextInAEL;
+ }
+
+ //bubblesort ...
+ bool isModified;
+ do
+ {
+ isModified = false;
+ e = m_SortedEdges;
+ while( e->NextInSEL )
+ {
+ TEdge *eNext = e->NextInSEL;
+ IntPoint Pt;
+ if(e->Curr.X > eNext->Curr.X)
+ {
+ IntersectPoint(*e, *eNext, Pt);
+ if (Pt.Y < topY) Pt = IntPoint(TopX(*e, topY), topY);
+ IntersectNode * newNode = new IntersectNode;
+ newNode->Edge1 = e;
+ newNode->Edge2 = eNext;
+ newNode->Pt = Pt;
+ m_IntersectList.push_back(newNode);
+
+ SwapPositionsInSEL(e, eNext);
+ isModified = true;
+ }
+ else
+ e = eNext;
+ }
+ if( e->PrevInSEL ) e->PrevInSEL->NextInSEL = 0;
+ else break;
+ }
+ while ( isModified );
+ m_SortedEdges = 0; //important
+}
+//------------------------------------------------------------------------------
+
+
+void Clipper::ProcessIntersectList()
+{
+ for (size_t i = 0; i < m_IntersectList.size(); ++i)
+ {
+ IntersectNode* iNode = m_IntersectList[i];
+ {
+ IntersectEdges( iNode->Edge1, iNode->Edge2, iNode->Pt);
+ SwapPositionsInAEL( iNode->Edge1 , iNode->Edge2 );
+ }
+ delete iNode;
+ }
+ m_IntersectList.clear();
+}
+//------------------------------------------------------------------------------
+
+bool IntersectListSort(IntersectNode* node1, IntersectNode* node2)
+{
+ return node2->Pt.Y < node1->Pt.Y;
+}
+//------------------------------------------------------------------------------
+
+inline bool EdgesAdjacent(const IntersectNode &inode)
+{
+ return (inode.Edge1->NextInSEL == inode.Edge2) ||
+ (inode.Edge1->PrevInSEL == inode.Edge2);
+}
+//------------------------------------------------------------------------------
+
+bool Clipper::FixupIntersectionOrder()
+{
+ //pre-condition: intersections are sorted Bottom-most first.
+ //Now it's crucial that intersections are made only between adjacent edges,
+ //so to ensure this the order of intersections may need adjusting ...
+ CopyAELToSEL();
+ std::sort(m_IntersectList.begin(), m_IntersectList.end(), IntersectListSort);
+ size_t cnt = m_IntersectList.size();
+ for (size_t i = 0; i < cnt; ++i)
+ {
+ if (!EdgesAdjacent(*m_IntersectList[i]))
+ {
+ size_t j = i + 1;
+ while (j < cnt && !EdgesAdjacent(*m_IntersectList[j])) j++;
+ if (j == cnt) return false;
+ std::swap(m_IntersectList[i], m_IntersectList[j]);
+ }
+ SwapPositionsInSEL(m_IntersectList[i]->Edge1, m_IntersectList[i]->Edge2);
+ }
+ return true;
+}
+//------------------------------------------------------------------------------
+
+void Clipper::DoMaxima(TEdge *e)
+{
+ TEdge* eMaxPair = GetMaximaPairEx(e);
+ if (!eMaxPair)
+ {
+ if (e->OutIdx >= 0)
+ AddOutPt(e, e->Top);
+ DeleteFromAEL(e);
+ return;
+ }
+
+ TEdge* eNext = e->NextInAEL;
+ while(eNext && eNext != eMaxPair)
+ {
+ IntersectEdges(e, eNext, e->Top);
+ SwapPositionsInAEL(e, eNext);
+ eNext = e->NextInAEL;
+ }
+
+ if(e->OutIdx == Unassigned && eMaxPair->OutIdx == Unassigned)
+ {
+ DeleteFromAEL(e);
+ DeleteFromAEL(eMaxPair);
+ }
+ else if( e->OutIdx >= 0 && eMaxPair->OutIdx >= 0 )
+ {
+ if (e->OutIdx >= 0) AddLocalMaxPoly(e, eMaxPair, e->Top);
+ DeleteFromAEL(e);
+ DeleteFromAEL(eMaxPair);
+ }
+#ifdef use_lines
+ else if (e->WindDelta == 0)
+ {
+ if (e->OutIdx >= 0)
+ {
+ AddOutPt(e, e->Top);
+ e->OutIdx = Unassigned;
+ }
+ DeleteFromAEL(e);
+
+ if (eMaxPair->OutIdx >= 0)
+ {
+ AddOutPt(eMaxPair, e->Top);
+ eMaxPair->OutIdx = Unassigned;
+ }
+ DeleteFromAEL(eMaxPair);
+ }
+#endif
+ else throw clipperException("DoMaxima error");
+}
+//------------------------------------------------------------------------------
+
+void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY)
+{
+ TEdge* e = m_ActiveEdges;
+ while( e )
+ {
+ //1. process maxima, treating them as if they're 'bent' horizontal edges,
+ // but exclude maxima with horizontal edges. nb: e can't be a horizontal.
+ bool IsMaximaEdge = IsMaxima(e, topY);
+
+ if(IsMaximaEdge)
+ {
+ TEdge* eMaxPair = GetMaximaPairEx(e);
+ IsMaximaEdge = (!eMaxPair || !IsHorizontal(*eMaxPair));
+ }
+
+ if(IsMaximaEdge)
+ {
+ if (m_StrictSimple) m_Maxima.push_back(e->Top.X);
+ TEdge* ePrev = e->PrevInAEL;
+ DoMaxima(e);
+ if( !ePrev ) e = m_ActiveEdges;
+ else e = ePrev->NextInAEL;
+ }
+ else
+ {
+ //2. promote horizontal edges, otherwise update Curr.X and Curr.Y ...
+ if (IsIntermediate(e, topY) && IsHorizontal(*e->NextInLML))
+ {
+ UpdateEdgeIntoAEL(e);
+ if (e->OutIdx >= 0)
+ AddOutPt(e, e->Bot);
+ AddEdgeToSEL(e);
+ }
+ else
+ {
+ e->Curr.X = TopX( *e, topY );
+ e->Curr.Y = topY;
+ }
+
+ //When StrictlySimple and 'e' is being touched by another edge, then
+ //make sure both edges have a vertex here ...
+ if (m_StrictSimple)
+ {
+ TEdge* ePrev = e->PrevInAEL;
+ if ((e->OutIdx >= 0) && (e->WindDelta != 0) && ePrev && (ePrev->OutIdx >= 0) &&
+ (ePrev->Curr.X == e->Curr.X) && (ePrev->WindDelta != 0))
+ {
+ IntPoint pt = e->Curr;
+#ifdef use_xyz
+ SetZ(pt, *ePrev, *e);
+#endif
+ OutPt* op = AddOutPt(ePrev, pt);
+ OutPt* op2 = AddOutPt(e, pt);
+ AddJoin(op, op2, pt); //StrictlySimple (type-3) join
+ }
+ }
+
+ e = e->NextInAEL;
+ }
+ }
+
+ //3. Process horizontals at the Top of the scanbeam ...
+ m_Maxima.sort();
+ ProcessHorizontals();
+ m_Maxima.clear();
+
+ //4. Promote intermediate vertices ...
+ e = m_ActiveEdges;
+ while(e)
+ {
+ if(IsIntermediate(e, topY))
+ {
+ OutPt* op = 0;
+ if( e->OutIdx >= 0 )
+ op = AddOutPt(e, e->Top);
+ UpdateEdgeIntoAEL(e);
+
+ //if output polygons share an edge, they'll need joining later ...
+ TEdge* ePrev = e->PrevInAEL;
+ TEdge* eNext = e->NextInAEL;
+ if (ePrev && ePrev->Curr.X == e->Bot.X &&
+ ePrev->Curr.Y == e->Bot.Y && op &&
+ ePrev->OutIdx >= 0 && ePrev->Curr.Y > ePrev->Top.Y &&
+ SlopesEqual(e->Curr, e->Top, ePrev->Curr, ePrev->Top, m_UseFullRange) &&
+ (e->WindDelta != 0) && (ePrev->WindDelta != 0))
+ {
+ OutPt* op2 = AddOutPt(ePrev, e->Bot);
+ AddJoin(op, op2, e->Top);
+ }
+ else if (eNext && eNext->Curr.X == e->Bot.X &&
+ eNext->Curr.Y == e->Bot.Y && op &&
+ eNext->OutIdx >= 0 && eNext->Curr.Y > eNext->Top.Y &&
+ SlopesEqual(e->Curr, e->Top, eNext->Curr, eNext->Top, m_UseFullRange) &&
+ (e->WindDelta != 0) && (eNext->WindDelta != 0))
+ {
+ OutPt* op2 = AddOutPt(eNext, e->Bot);
+ AddJoin(op, op2, e->Top);
+ }
+ }
+ e = e->NextInAEL;
+ }
+}
+//------------------------------------------------------------------------------
+
+void Clipper::FixupOutPolyline(OutRec &outrec)
+{
+ OutPt *pp = outrec.Pts;
+ OutPt *lastPP = pp->Prev;
+ while (pp != lastPP)
+ {
+ pp = pp->Next;
+ if (pp->Pt == pp->Prev->Pt)
+ {
+ if (pp == lastPP) lastPP = pp->Prev;
+ OutPt *tmpPP = pp->Prev;
+ tmpPP->Next = pp->Next;
+ pp->Next->Prev = tmpPP;
+ delete pp;
+ pp = tmpPP;
+ }
+ }
+
+ if (pp == pp->Prev)
+ {
+ DisposeOutPts(pp);
+ outrec.Pts = 0;
+ return;
+ }
+}
+//------------------------------------------------------------------------------
+
+void Clipper::FixupOutPolygon(OutRec &outrec)
+{
+ //FixupOutPolygon() - removes duplicate points and simplifies consecutive
+ //parallel edges by removing the middle vertex.
+ OutPt *lastOK = 0;
+ outrec.BottomPt = 0;
+ OutPt *pp = outrec.Pts;
+ bool preserveCol = m_PreserveCollinear || m_StrictSimple;
+
+ for (;;)
+ {
+ if (pp->Prev == pp || pp->Prev == pp->Next)
+ {
+ DisposeOutPts(pp);
+ outrec.Pts = 0;
+ return;
+ }
+
+ //test for duplicate points and collinear edges ...
+ if ((pp->Pt == pp->Next->Pt) || (pp->Pt == pp->Prev->Pt) ||
+ (SlopesEqual(pp->Prev->Pt, pp->Pt, pp->Next->Pt, m_UseFullRange) &&
+ (!preserveCol || !Pt2IsBetweenPt1AndPt3(pp->Prev->Pt, pp->Pt, pp->Next->Pt))))
+ {
+ lastOK = 0;
+ OutPt *tmp = pp;
+ pp->Prev->Next = pp->Next;
+ pp->Next->Prev = pp->Prev;
+ pp = pp->Prev;
+ delete tmp;
+ }
+ else if (pp == lastOK) break;
+ else
+ {
+ if (!lastOK) lastOK = pp;
+ pp = pp->Next;
+ }
+ }
+ outrec.Pts = pp;
+}
+//------------------------------------------------------------------------------
+
+int PointCount(OutPt *Pts)
+{
+ if (!Pts) return 0;
+ int result = 0;
+ OutPt* p = Pts;
+ do
+ {
+ result++;
+ p = p->Next;
+ }
+ while (p != Pts);
+ return result;
+}
+//------------------------------------------------------------------------------
+
+void Clipper::BuildResult(Paths &polys)
+{
+ polys.reserve(m_PolyOuts.size());
+ for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i)
+ {
+ if (!m_PolyOuts[i]->Pts) continue;
+ Path pg;
+ OutPt* p = m_PolyOuts[i]->Pts->Prev;
+ int cnt = PointCount(p);
+ if (cnt < 2) continue;
+ pg.reserve(cnt);
+ for (int i = 0; i < cnt; ++i)
+ {
+ pg.push_back(p->Pt);
+ p = p->Prev;
+ }
+ polys.push_back(pg);
+ }
+}
+//------------------------------------------------------------------------------
+
+void Clipper::BuildResult2(PolyTree& polytree)
+{
+ polytree.Clear();
+ polytree.AllNodes.reserve(m_PolyOuts.size());
+ //add each output polygon/contour to polytree ...
+ for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); i++)
+ {
+ OutRec* outRec = m_PolyOuts[i];
+ int cnt = PointCount(outRec->Pts);
+ if ((outRec->IsOpen && cnt < 2) || (!outRec->IsOpen && cnt < 3)) continue;
+ FixHoleLinkage(*outRec);
+ PolyNode* pn = new PolyNode();
+ //nb: polytree takes ownership of all the PolyNodes
+ polytree.AllNodes.push_back(pn);
+ outRec->PolyNd = pn;
+ pn->Parent = 0;
+ pn->Index = 0;
+ pn->Contour.reserve(cnt);
+ OutPt *op = outRec->Pts->Prev;
+ for (int j = 0; j < cnt; j++)
+ {
+ pn->Contour.push_back(op->Pt);
+ op = op->Prev;
+ }
+ }
+
+ //fixup PolyNode links etc ...
+ polytree.Childs.reserve(m_PolyOuts.size());
+ for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); i++)
+ {
+ OutRec* outRec = m_PolyOuts[i];
+ if (!outRec->PolyNd) continue;
+ if (outRec->IsOpen)
+ {
+ outRec->PolyNd->m_IsOpen = true;
+ polytree.AddChild(*outRec->PolyNd);
+ }
+ else if (outRec->FirstLeft && outRec->FirstLeft->PolyNd)
+ outRec->FirstLeft->PolyNd->AddChild(*outRec->PolyNd);
+ else
+ polytree.AddChild(*outRec->PolyNd);
+ }
+}
+//------------------------------------------------------------------------------
+
+void SwapIntersectNodes(IntersectNode &int1, IntersectNode &int2)
+{
+ //just swap the contents (because fIntersectNodes is a single-linked-list)
+ IntersectNode inode = int1; //gets a copy of Int1
+ int1.Edge1 = int2.Edge1;
+ int1.Edge2 = int2.Edge2;
+ int1.Pt = int2.Pt;
+ int2.Edge1 = inode.Edge1;
+ int2.Edge2 = inode.Edge2;
+ int2.Pt = inode.Pt;
+}
+//------------------------------------------------------------------------------
+
+inline bool E2InsertsBeforeE1(TEdge &e1, TEdge &e2)
+{
+ if (e2.Curr.X == e1.Curr.X)
+ {
+ if (e2.Top.Y > e1.Top.Y)
+ return e2.Top.X < TopX(e1, e2.Top.Y);
+ else return e1.Top.X > TopX(e2, e1.Top.Y);
+ }
+ else return e2.Curr.X < e1.Curr.X;
+}
+//------------------------------------------------------------------------------
+
+bool GetOverlap(const cInt a1, const cInt a2, const cInt b1, const cInt b2,
+ cInt& Left, cInt& Right)
+{
+ if (a1 < a2)
+ {
+ if (b1 < b2) {Left = std::max(a1,b1); Right = std::min(a2,b2);}
+ else {Left = std::max(a1,b2); Right = std::min(a2,b1);}
+ }
+ else
+ {
+ if (b1 < b2) {Left = std::max(a2,b1); Right = std::min(a1,b2);}
+ else {Left = std::max(a2,b2); Right = std::min(a1,b1);}
+ }
+ return Left < Right;
+}
+//------------------------------------------------------------------------------
+
+inline void UpdateOutPtIdxs(OutRec& outrec)
+{
+ OutPt* op = outrec.Pts;
+ do
+ {
+ op->Idx = outrec.Idx;
+ op = op->Prev;
+ }
+ while(op != outrec.Pts);
+}
+//------------------------------------------------------------------------------
+
+void Clipper::InsertEdgeIntoAEL(TEdge *edge, TEdge* startEdge)
+{
+ if(!m_ActiveEdges)
+ {
+ edge->PrevInAEL = 0;
+ edge->NextInAEL = 0;
+ m_ActiveEdges = edge;
+ }
+ else if(!startEdge && E2InsertsBeforeE1(*m_ActiveEdges, *edge))
+ {
+ edge->PrevInAEL = 0;
+ edge->NextInAEL = m_ActiveEdges;
+ m_ActiveEdges->PrevInAEL = edge;
+ m_ActiveEdges = edge;
+ }
+ else
+ {
+ if(!startEdge) startEdge = m_ActiveEdges;
+ while(startEdge->NextInAEL &&
+ !E2InsertsBeforeE1(*startEdge->NextInAEL , *edge))
+ startEdge = startEdge->NextInAEL;
+ edge->NextInAEL = startEdge->NextInAEL;
+ if(startEdge->NextInAEL) startEdge->NextInAEL->PrevInAEL = edge;
+ edge->PrevInAEL = startEdge;
+ startEdge->NextInAEL = edge;
+ }
+}
+//----------------------------------------------------------------------
+
+OutPt* DupOutPt(OutPt* outPt, bool InsertAfter)
+{
+ OutPt* result = new OutPt;
+ result->Pt = outPt->Pt;
+ result->Idx = outPt->Idx;
+ if (InsertAfter)
+ {
+ result->Next = outPt->Next;
+ result->Prev = outPt;
+ outPt->Next->Prev = result;
+ outPt->Next = result;
+ }
+ else
+ {
+ result->Prev = outPt->Prev;
+ result->Next = outPt;
+ outPt->Prev->Next = result;
+ outPt->Prev = result;
+ }
+ return result;
+}
+//------------------------------------------------------------------------------
+
+bool JoinHorz(OutPt* op1, OutPt* op1b, OutPt* op2, OutPt* op2b,
+ const IntPoint Pt, bool DiscardLeft)
+{
+ Direction Dir1 = (op1->Pt.X > op1b->Pt.X ? dRightToLeft : dLeftToRight);
+ Direction Dir2 = (op2->Pt.X > op2b->Pt.X ? dRightToLeft : dLeftToRight);
+ if (Dir1 == Dir2) return false;
+
+ //When DiscardLeft, we want Op1b to be on the Left of Op1, otherwise we
+ //want Op1b to be on the Right. (And likewise with Op2 and Op2b.)
+ //So, to facilitate this while inserting Op1b and Op2b ...
+ //when DiscardLeft, make sure we're AT or RIGHT of Pt before adding Op1b,
+ //otherwise make sure we're AT or LEFT of Pt. (Likewise with Op2b.)
+ if (Dir1 == dLeftToRight)
+ {
+ while (op1->Next->Pt.X <= Pt.X &&
+ op1->Next->Pt.X >= op1->Pt.X && op1->Next->Pt.Y == Pt.Y)
+ op1 = op1->Next;
+ if (DiscardLeft && (op1->Pt.X != Pt.X)) op1 = op1->Next;
+ op1b = DupOutPt(op1, !DiscardLeft);
+ if (op1b->Pt != Pt)
+ {
+ op1 = op1b;
+ op1->Pt = Pt;
+ op1b = DupOutPt(op1, !DiscardLeft);
+ }
+ }
+ else
+ {
+ while (op1->Next->Pt.X >= Pt.X &&
+ op1->Next->Pt.X <= op1->Pt.X && op1->Next->Pt.Y == Pt.Y)
+ op1 = op1->Next;
+ if (!DiscardLeft && (op1->Pt.X != Pt.X)) op1 = op1->Next;
+ op1b = DupOutPt(op1, DiscardLeft);
+ if (op1b->Pt != Pt)
+ {
+ op1 = op1b;
+ op1->Pt = Pt;
+ op1b = DupOutPt(op1, DiscardLeft);
+ }
+ }
+
+ if (Dir2 == dLeftToRight)
+ {
+ while (op2->Next->Pt.X <= Pt.X &&
+ op2->Next->Pt.X >= op2->Pt.X && op2->Next->Pt.Y == Pt.Y)
+ op2 = op2->Next;
+ if (DiscardLeft && (op2->Pt.X != Pt.X)) op2 = op2->Next;
+ op2b = DupOutPt(op2, !DiscardLeft);
+ if (op2b->Pt != Pt)
+ {
+ op2 = op2b;
+ op2->Pt = Pt;
+ op2b = DupOutPt(op2, !DiscardLeft);
+ };
+ } else
+ {
+ while (op2->Next->Pt.X >= Pt.X &&
+ op2->Next->Pt.X <= op2->Pt.X && op2->Next->Pt.Y == Pt.Y)
+ op2 = op2->Next;
+ if (!DiscardLeft && (op2->Pt.X != Pt.X)) op2 = op2->Next;
+ op2b = DupOutPt(op2, DiscardLeft);
+ if (op2b->Pt != Pt)
+ {
+ op2 = op2b;
+ op2->Pt = Pt;
+ op2b = DupOutPt(op2, DiscardLeft);
+ };
+ };
+
+ if ((Dir1 == dLeftToRight) == DiscardLeft)
+ {
+ op1->Prev = op2;
+ op2->Next = op1;
+ op1b->Next = op2b;
+ op2b->Prev = op1b;
+ }
+ else
+ {
+ op1->Next = op2;
+ op2->Prev = op1;
+ op1b->Prev = op2b;
+ op2b->Next = op1b;
+ }
+ return true;
+}
+//------------------------------------------------------------------------------
+
+bool Clipper::JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2)
+{
+ OutPt *op1 = j->OutPt1, *op1b;
+ OutPt *op2 = j->OutPt2, *op2b;
+
+ //There are 3 kinds of joins for output polygons ...
+ //1. Horizontal joins where Join.OutPt1 & Join.OutPt2 are vertices anywhere
+ //along (horizontal) collinear edges (& Join.OffPt is on the same horizontal).
+ //2. Non-horizontal joins where Join.OutPt1 & Join.OutPt2 are at the same
+ //location at the Bottom of the overlapping segment (& Join.OffPt is above).
+ //3. StrictSimple joins where edges touch but are not collinear and where
+ //Join.OutPt1, Join.OutPt2 & Join.OffPt all share the same point.
+ bool isHorizontal = (j->OutPt1->Pt.Y == j->OffPt.Y);
+
+ if (isHorizontal && (j->OffPt == j->OutPt1->Pt) &&
+ (j->OffPt == j->OutPt2->Pt))
+ {
+ //Strictly Simple join ...
+ if (outRec1 != outRec2) return false;
+ op1b = j->OutPt1->Next;
+ while (op1b != op1 && (op1b->Pt == j->OffPt))
+ op1b = op1b->Next;
+ bool reverse1 = (op1b->Pt.Y > j->OffPt.Y);
+ op2b = j->OutPt2->Next;
+ while (op2b != op2 && (op2b->Pt == j->OffPt))
+ op2b = op2b->Next;
+ bool reverse2 = (op2b->Pt.Y > j->OffPt.Y);
+ if (reverse1 == reverse2) return false;
+ if (reverse1)
+ {
+ op1b = DupOutPt(op1, false);
+ op2b = DupOutPt(op2, true);
+ op1->Prev = op2;
+ op2->Next = op1;
+ op1b->Next = op2b;
+ op2b->Prev = op1b;
+ j->OutPt1 = op1;
+ j->OutPt2 = op1b;
+ return true;
+ } else
+ {
+ op1b = DupOutPt(op1, true);
+ op2b = DupOutPt(op2, false);
+ op1->Next = op2;
+ op2->Prev = op1;
+ op1b->Prev = op2b;
+ op2b->Next = op1b;
+ j->OutPt1 = op1;
+ j->OutPt2 = op1b;
+ return true;
+ }
+ }
+ else if (isHorizontal)
+ {
+ //treat horizontal joins differently to non-horizontal joins since with
+ //them we're not yet sure where the overlapping is. OutPt1.Pt & OutPt2.Pt
+ //may be anywhere along the horizontal edge.
+ op1b = op1;
+ while (op1->Prev->Pt.Y == op1->Pt.Y && op1->Prev != op1b && op1->Prev != op2)
+ op1 = op1->Prev;
+ while (op1b->Next->Pt.Y == op1b->Pt.Y && op1b->Next != op1 && op1b->Next != op2)
+ op1b = op1b->Next;
+ if (op1b->Next == op1 || op1b->Next == op2) return false; //a flat 'polygon'
+
+ op2b = op2;
+ while (op2->Prev->Pt.Y == op2->Pt.Y && op2->Prev != op2b && op2->Prev != op1b)
+ op2 = op2->Prev;
+ while (op2b->Next->Pt.Y == op2b->Pt.Y && op2b->Next != op2 && op2b->Next != op1)
+ op2b = op2b->Next;
+ if (op2b->Next == op2 || op2b->Next == op1) return false; //a flat 'polygon'
+
+ cInt Left, Right;
+ //Op1 --> Op1b & Op2 --> Op2b are the extremites of the horizontal edges
+ if (!GetOverlap(op1->Pt.X, op1b->Pt.X, op2->Pt.X, op2b->Pt.X, Left, Right))
+ return false;
+
+ //DiscardLeftSide: when overlapping edges are joined, a spike will created
+ //which needs to be cleaned up. However, we don't want Op1 or Op2 caught up
+ //on the discard Side as either may still be needed for other joins ...
+ IntPoint Pt;
+ bool DiscardLeftSide;
+ if (op1->Pt.X >= Left && op1->Pt.X <= Right)
+ {
+ Pt = op1->Pt; DiscardLeftSide = (op1->Pt.X > op1b->Pt.X);
+ }
+ else if (op2->Pt.X >= Left&& op2->Pt.X <= Right)
+ {
+ Pt = op2->Pt; DiscardLeftSide = (op2->Pt.X > op2b->Pt.X);
+ }
+ else if (op1b->Pt.X >= Left && op1b->Pt.X <= Right)
+ {
+ Pt = op1b->Pt; DiscardLeftSide = op1b->Pt.X > op1->Pt.X;
+ }
+ else
+ {
+ Pt = op2b->Pt; DiscardLeftSide = (op2b->Pt.X > op2->Pt.X);
+ }
+ j->OutPt1 = op1; j->OutPt2 = op2;
+ return JoinHorz(op1, op1b, op2, op2b, Pt, DiscardLeftSide);
+ } else
+ {
+ //nb: For non-horizontal joins ...
+ // 1. Jr.OutPt1.Pt.Y == Jr.OutPt2.Pt.Y
+ // 2. Jr.OutPt1.Pt > Jr.OffPt.Y
+
+ //make sure the polygons are correctly oriented ...
+ op1b = op1->Next;
+ while ((op1b->Pt == op1->Pt) && (op1b != op1)) op1b = op1b->Next;
+ bool Reverse1 = ((op1b->Pt.Y > op1->Pt.Y) ||
+ !SlopesEqual(op1->Pt, op1b->Pt, j->OffPt, m_UseFullRange));
+ if (Reverse1)
+ {
+ op1b = op1->Prev;
+ while ((op1b->Pt == op1->Pt) && (op1b != op1)) op1b = op1b->Prev;
+ if ((op1b->Pt.Y > op1->Pt.Y) ||
+ !SlopesEqual(op1->Pt, op1b->Pt, j->OffPt, m_UseFullRange)) return false;
+ };
+ op2b = op2->Next;
+ while ((op2b->Pt == op2->Pt) && (op2b != op2))op2b = op2b->Next;
+ bool Reverse2 = ((op2b->Pt.Y > op2->Pt.Y) ||
+ !SlopesEqual(op2->Pt, op2b->Pt, j->OffPt, m_UseFullRange));
+ if (Reverse2)
+ {
+ op2b = op2->Prev;
+ while ((op2b->Pt == op2->Pt) && (op2b != op2)) op2b = op2b->Prev;
+ if ((op2b->Pt.Y > op2->Pt.Y) ||
+ !SlopesEqual(op2->Pt, op2b->Pt, j->OffPt, m_UseFullRange)) return false;
+ }
+
+ if ((op1b == op1) || (op2b == op2) || (op1b == op2b) ||
+ ((outRec1 == outRec2) && (Reverse1 == Reverse2))) return false;
+
+ if (Reverse1)
+ {
+ op1b = DupOutPt(op1, false);
+ op2b = DupOutPt(op2, true);
+ op1->Prev = op2;
+ op2->Next = op1;
+ op1b->Next = op2b;
+ op2b->Prev = op1b;
+ j->OutPt1 = op1;
+ j->OutPt2 = op1b;
+ return true;
+ } else
+ {
+ op1b = DupOutPt(op1, true);
+ op2b = DupOutPt(op2, false);
+ op1->Next = op2;
+ op2->Prev = op1;
+ op1b->Prev = op2b;
+ op2b->Next = op1b;
+ j->OutPt1 = op1;
+ j->OutPt2 = op1b;
+ return true;
+ }
+ }
+}
+//----------------------------------------------------------------------
+
+static OutRec* ParseFirstLeft(OutRec* FirstLeft)
+{
+ while (FirstLeft && !FirstLeft->Pts)
+ FirstLeft = FirstLeft->FirstLeft;
+ return FirstLeft;
+}
+//------------------------------------------------------------------------------
+
+void Clipper::FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec)
+{
+ //tests if NewOutRec contains the polygon before reassigning FirstLeft
+ for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i)
+ {
+ OutRec* outRec = m_PolyOuts[i];
+ OutRec* firstLeft = ParseFirstLeft(outRec->FirstLeft);
+ if (outRec->Pts && firstLeft == OldOutRec)
+ {
+ if (Poly2ContainsPoly1(outRec->Pts, NewOutRec->Pts))
+ outRec->FirstLeft = NewOutRec;
+ }
+ }
+}
+//----------------------------------------------------------------------
+
+void Clipper::FixupFirstLefts2(OutRec* InnerOutRec, OutRec* OuterOutRec)
+{
+ //A polygon has split into two such that one is now the inner of the other.
+ //It's possible that these polygons now wrap around other polygons, so check
+ //every polygon that's also contained by OuterOutRec's FirstLeft container
+ //(including 0) to see if they've become inner to the new inner polygon ...
+ OutRec* orfl = OuterOutRec->FirstLeft;
+ for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i)
+ {
+ OutRec* outRec = m_PolyOuts[i];
+
+ if (!outRec->Pts || outRec == OuterOutRec || outRec == InnerOutRec)
+ continue;
+ OutRec* firstLeft = ParseFirstLeft(outRec->FirstLeft);
+ if (firstLeft != orfl && firstLeft != InnerOutRec && firstLeft != OuterOutRec)
+ continue;
+ if (Poly2ContainsPoly1(outRec->Pts, InnerOutRec->Pts))
+ outRec->FirstLeft = InnerOutRec;
+ else if (Poly2ContainsPoly1(outRec->Pts, OuterOutRec->Pts))
+ outRec->FirstLeft = OuterOutRec;
+ else if (outRec->FirstLeft == InnerOutRec || outRec->FirstLeft == OuterOutRec)
+ outRec->FirstLeft = orfl;
+ }
+}
+//----------------------------------------------------------------------
+void Clipper::FixupFirstLefts3(OutRec* OldOutRec, OutRec* NewOutRec)
+{
+ //reassigns FirstLeft WITHOUT testing if NewOutRec contains the polygon
+ for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i)
+ {
+ OutRec* outRec = m_PolyOuts[i];
+ OutRec* firstLeft = ParseFirstLeft(outRec->FirstLeft);
+ if (outRec->Pts && outRec->FirstLeft == OldOutRec)
+ outRec->FirstLeft = NewOutRec;
+ }
+}
+//----------------------------------------------------------------------
+
+void Clipper::JoinCommonEdges()
+{
+ for (JoinList::size_type i = 0; i < m_Joins.size(); i++)
+ {
+ Join* join = m_Joins[i];
+
+ OutRec *outRec1 = GetOutRec(join->OutPt1->Idx);
+ OutRec *outRec2 = GetOutRec(join->OutPt2->Idx);
+
+ if (!outRec1->Pts || !outRec2->Pts) continue;
+ if (outRec1->IsOpen || outRec2->IsOpen) continue;
+
+ //get the polygon fragment with the correct hole state (FirstLeft)
+ //before calling JoinPoints() ...
+ OutRec *holeStateRec;
+ if (outRec1 == outRec2) holeStateRec = outRec1;
+ else if (OutRec1RightOfOutRec2(outRec1, outRec2)) holeStateRec = outRec2;
+ else if (OutRec1RightOfOutRec2(outRec2, outRec1)) holeStateRec = outRec1;
+ else holeStateRec = GetLowermostRec(outRec1, outRec2);
+
+ if (!JoinPoints(join, outRec1, outRec2)) continue;
+
+ if (outRec1 == outRec2)
+ {
+ //instead of joining two polygons, we've just created a new one by
+ //splitting one polygon into two.
+ outRec1->Pts = join->OutPt1;
+ outRec1->BottomPt = 0;
+ outRec2 = CreateOutRec();
+ outRec2->Pts = join->OutPt2;
+
+ //update all OutRec2.Pts Idx's ...
+ UpdateOutPtIdxs(*outRec2);
+
+ if (Poly2ContainsPoly1(outRec2->Pts, outRec1->Pts))
+ {
+ //outRec1 contains outRec2 ...
+ outRec2->IsHole = !outRec1->IsHole;
+ outRec2->FirstLeft = outRec1;
+
+ if (m_UsingPolyTree) FixupFirstLefts2(outRec2, outRec1);
+
+ if ((outRec2->IsHole ^ m_ReverseOutput) == (Area(*outRec2) > 0))
+ ReversePolyPtLinks(outRec2->Pts);
+
+ } else if (Poly2ContainsPoly1(outRec1->Pts, outRec2->Pts))
+ {
+ //outRec2 contains outRec1 ...
+ outRec2->IsHole = outRec1->IsHole;
+ outRec1->IsHole = !outRec2->IsHole;
+ outRec2->FirstLeft = outRec1->FirstLeft;
+ outRec1->FirstLeft = outRec2;
+
+ if (m_UsingPolyTree) FixupFirstLefts2(outRec1, outRec2);
+
+ if ((outRec1->IsHole ^ m_ReverseOutput) == (Area(*outRec1) > 0))
+ ReversePolyPtLinks(outRec1->Pts);
+ }
+ else
+ {
+ //the 2 polygons are completely separate ...
+ outRec2->IsHole = outRec1->IsHole;
+ outRec2->FirstLeft = outRec1->FirstLeft;
+
+ //fixup FirstLeft pointers that may need reassigning to OutRec2
+ if (m_UsingPolyTree) FixupFirstLefts1(outRec1, outRec2);
+ }
+
+ } else
+ {
+ //joined 2 polygons together ...
+
+ outRec2->Pts = 0;
+ outRec2->BottomPt = 0;
+ outRec2->Idx = outRec1->Idx;
+
+ outRec1->IsHole = holeStateRec->IsHole;
+ if (holeStateRec == outRec2)
+ outRec1->FirstLeft = outRec2->FirstLeft;
+ outRec2->FirstLeft = outRec1;
+
+ if (m_UsingPolyTree) FixupFirstLefts3(outRec2, outRec1);
+ }
+ }
+}
+
+//------------------------------------------------------------------------------
+// ClipperOffset support functions ...
+//------------------------------------------------------------------------------
+
+DoublePoint GetUnitNormal(const IntPoint &pt1, const IntPoint &pt2)
+{
+ if(pt2.X == pt1.X && pt2.Y == pt1.Y)
+ return DoublePoint(0, 0);
+
+ double Dx = (double)(pt2.X - pt1.X);
+ double dy = (double)(pt2.Y - pt1.Y);
+ double f = 1 *1.0/ std::sqrt( Dx*Dx + dy*dy );
+ Dx *= f;
+ dy *= f;
+ return DoublePoint(dy, -Dx);
+}
+
+//------------------------------------------------------------------------------
+// ClipperOffset class
+//------------------------------------------------------------------------------
+
+ClipperOffset::ClipperOffset(double miterLimit, double arcTolerance)
+{
+ this->MiterLimit = miterLimit;
+ this->ArcTolerance = arcTolerance;
+ m_lowest.X = -1;
+
+ //Avoid uninitialized vars:
+ m_delta = m_sinA = m_sin = m_cos = 0;
+ m_miterLim = m_StepsPerRad = 0;
+
+}
+//------------------------------------------------------------------------------
+
+ClipperOffset::~ClipperOffset()
+{
+ Clear();
+}
+//------------------------------------------------------------------------------
+
+void ClipperOffset::Clear()
+{
+ for (int i = 0; i < m_polyNodes.ChildCount(); ++i)
+ delete m_polyNodes.Childs[i];
+ m_polyNodes.Childs.clear();
+ m_lowest.X = -1;
+}
+//------------------------------------------------------------------------------
+
+void ClipperOffset::AddPath(const Path& path, JoinType joinType, EndType endType)
+{
+ int highI = (int)path.size() - 1;
+ if (highI < 0) return;
+ PolyNode* newNode = new PolyNode();
+ newNode->m_jointype = joinType;
+ newNode->m_endtype = endType;
+
+ //strip duplicate points from path and also get index to the lowest point ...
+ if (endType == etClosedLine || endType == etClosedPolygon)
+ while (highI > 0 && path[0] == path[highI]) highI--;
+ newNode->Contour.reserve(highI + 1);
+ newNode->Contour.push_back(path[0]);
+ int j = 0, k = 0;
+ for (int i = 1; i <= highI; i++)
+ if (newNode->Contour[j] != path[i])
+ {
+ j++;
+ newNode->Contour.push_back(path[i]);
+ if (path[i].Y > newNode->Contour[k].Y ||
+ (path[i].Y == newNode->Contour[k].Y &&
+ path[i].X < newNode->Contour[k].X)) k = j;
+ }
+ if (endType == etClosedPolygon && j < 2)
+ {
+ delete newNode;
+ return;
+ }
+ m_polyNodes.AddChild(*newNode);
+
+ //if this path's lowest pt is lower than all the others then update m_lowest
+ if (endType != etClosedPolygon) return;
+ if (m_lowest.X < 0)
+ m_lowest = IntPoint(m_polyNodes.ChildCount() - 1, k);
+ else
+ {
+ IntPoint ip = m_polyNodes.Childs[(int)m_lowest.X]->Contour[(int)m_lowest.Y];
+ if (newNode->Contour[k].Y > ip.Y ||
+ (newNode->Contour[k].Y == ip.Y &&
+ newNode->Contour[k].X < ip.X))
+ m_lowest = IntPoint(m_polyNodes.ChildCount() - 1, k);
+ }
+}
+//------------------------------------------------------------------------------
+
+void ClipperOffset::AddPaths(const Paths& paths, JoinType joinType, EndType endType)
+{
+ for (Paths::size_type i = 0; i < paths.size(); ++i)
+ AddPath(paths[i], joinType, endType);
+}
+//------------------------------------------------------------------------------
+
+void ClipperOffset::FixOrientations()
+{
+ //fixup orientations of all closed paths if the orientation of the
+ //closed path with the lowermost vertex is wrong ...
+ if (m_lowest.X >= 0 &&
+ !Orientation(m_polyNodes.Childs[(int)m_lowest.X]->Contour))
+ {
+ for (int i = 0; i < m_polyNodes.ChildCount(); ++i)
+ {
+ PolyNode& node = *m_polyNodes.Childs[i];
+ if (node.m_endtype == etClosedPolygon ||
+ (node.m_endtype == etClosedLine && Orientation(node.Contour)))
+ ReversePath(node.Contour);
+ }
+ } else
+ {
+ for (int i = 0; i < m_polyNodes.ChildCount(); ++i)
+ {
+ PolyNode& node = *m_polyNodes.Childs[i];
+ if (node.m_endtype == etClosedLine && !Orientation(node.Contour))
+ ReversePath(node.Contour);
+ }
+ }
+}
+//------------------------------------------------------------------------------
+
+void ClipperOffset::Execute(Paths& solution, double delta)
+{
+ solution.clear();
+ FixOrientations();
+ DoOffset(delta);
+
+ //now clean up 'corners' ...
+ Clipper clpr;
+ clpr.AddPaths(m_destPolys, ptSubject, true);
+ if (delta > 0)
+ {
+ clpr.Execute(ctUnion, solution, pftPositive, pftPositive);
+ }
+ else
+ {
+ IntRect r = clpr.GetBounds();
+ Path outer(4);
+ outer[0] = IntPoint(r.left - 10, r.bottom + 10);
+ outer[1] = IntPoint(r.right + 10, r.bottom + 10);
+ outer[2] = IntPoint(r.right + 10, r.top - 10);
+ outer[3] = IntPoint(r.left - 10, r.top - 10);
+
+ clpr.AddPath(outer, ptSubject, true);
+ clpr.ReverseSolution(true);
+ clpr.Execute(ctUnion, solution, pftNegative, pftNegative);
+ if (solution.size() > 0) solution.erase(solution.begin());
+ }
+}
+//------------------------------------------------------------------------------
+
+void ClipperOffset::Execute(PolyTree& solution, double delta)
+{
+ solution.Clear();
+ FixOrientations();
+ DoOffset(delta);
+
+ //now clean up 'corners' ...
+ Clipper clpr;
+ clpr.AddPaths(m_destPolys, ptSubject, true);
+ if (delta > 0)
+ {
+ clpr.Execute(ctUnion, solution, pftPositive, pftPositive);
+ }
+ else
+ {
+ IntRect r = clpr.GetBounds();
+ Path outer(4);
+ outer[0] = IntPoint(r.left - 10, r.bottom + 10);
+ outer[1] = IntPoint(r.right + 10, r.bottom + 10);
+ outer[2] = IntPoint(r.right + 10, r.top - 10);
+ outer[3] = IntPoint(r.left - 10, r.top - 10);
+
+ clpr.AddPath(outer, ptSubject, true);
+ clpr.ReverseSolution(true);
+ clpr.Execute(ctUnion, solution, pftNegative, pftNegative);
+ //remove the outer PolyNode rectangle ...
+ if (solution.ChildCount() == 1 && solution.Childs[0]->ChildCount() > 0)
+ {
+ PolyNode* outerNode = solution.Childs[0];
+ solution.Childs.reserve(outerNode->ChildCount());
+ solution.Childs[0] = outerNode->Childs[0];
+ solution.Childs[0]->Parent = outerNode->Parent;
+ for (int i = 1; i < outerNode->ChildCount(); ++i)
+ solution.AddChild(*outerNode->Childs[i]);
+ }
+ else
+ solution.Clear();
+ }
+}
+//------------------------------------------------------------------------------
+
+void ClipperOffset::DoOffset(double delta)
+{
+ m_destPolys.clear();
+ m_delta = delta;
+
+ //if Zero offset, just copy any CLOSED polygons to m_p and return ...
+ if (NEAR_ZERO(delta))
+ {
+ m_destPolys.reserve(m_polyNodes.ChildCount());
+ for (int i = 0; i < m_polyNodes.ChildCount(); i++)
+ {
+ PolyNode& node = *m_polyNodes.Childs[i];
+ if (node.m_endtype == etClosedPolygon)
+ m_destPolys.push_back(node.Contour);
+ }
+ return;
+ }
+
+ //see offset_triginometry3.svg in the documentation folder ...
+ if (MiterLimit > 2) m_miterLim = 2/(MiterLimit * MiterLimit);
+ else m_miterLim = 0.5;
+
+ double y;
+ if (ArcTolerance <= 0.0) y = def_arc_tolerance;
+ else if (ArcTolerance > std::fabs(delta) * def_arc_tolerance)
+ y = std::fabs(delta) * def_arc_tolerance;
+ else y = ArcTolerance;
+ //see offset_triginometry2.svg in the documentation folder ...
+ double steps = pi / std::acos(1 - y / std::fabs(delta));
+ if (steps > std::fabs(delta) * pi)
+ steps = std::fabs(delta) * pi; //ie excessive precision check
+ m_sin = std::sin(two_pi / steps);
+ m_cos = std::cos(two_pi / steps);
+ m_StepsPerRad = steps / two_pi;
+ if (delta < 0.0) m_sin = -m_sin;
+
+ m_destPolys.reserve(m_polyNodes.ChildCount() * 2);
+ for (int i = 0; i < m_polyNodes.ChildCount(); i++)
+ {
+ PolyNode& node = *m_polyNodes.Childs[i];
+ m_srcPoly = node.Contour;
+
+ int len = (int)m_srcPoly.size();
+ if (len == 0 || (delta <= 0 && (len < 3 || node.m_endtype != etClosedPolygon)))
+ continue;
+
+ m_destPoly.clear();
+ if (len == 1)
+ {
+ if (node.m_jointype == jtRound)
+ {
+ double X = 1.0, Y = 0.0;
+ for (cInt j = 1; j <= steps; j++)
+ {
+ m_destPoly.push_back(IntPoint(
+ Round(m_srcPoly[0].X + X * delta),
+ Round(m_srcPoly[0].Y + Y * delta)));
+ double X2 = X;
+ X = X * m_cos - m_sin * Y;
+ Y = X2 * m_sin + Y * m_cos;
+ }
+ }
+ else
+ {
+ double X = -1.0, Y = -1.0;
+ for (int j = 0; j < 4; ++j)
+ {
+ m_destPoly.push_back(IntPoint(
+ Round(m_srcPoly[0].X + X * delta),
+ Round(m_srcPoly[0].Y + Y * delta)));
+ if (X < 0) X = 1;
+ else if (Y < 0) Y = 1;
+ else X = -1;
+ }
+ }
+ m_destPolys.push_back(m_destPoly);
+ continue;
+ }
+ //build m_normals ...
+ m_normals.clear();
+ m_normals.reserve(len);
+ for (int j = 0; j < len - 1; ++j)
+ m_normals.push_back(GetUnitNormal(m_srcPoly[j], m_srcPoly[j + 1]));
+ if (node.m_endtype == etClosedLine || node.m_endtype == etClosedPolygon)
+ m_normals.push_back(GetUnitNormal(m_srcPoly[len - 1], m_srcPoly[0]));
+ else
+ m_normals.push_back(DoublePoint(m_normals[len - 2]));
+
+ if (node.m_endtype == etClosedPolygon)
+ {
+ int k = len - 1;
+ for (int j = 0; j < len; ++j)
+ OffsetPoint(j, k, node.m_jointype);
+ m_destPolys.push_back(m_destPoly);
+ }
+ else if (node.m_endtype == etClosedLine)
+ {
+ int k = len - 1;
+ for (int j = 0; j < len; ++j)
+ OffsetPoint(j, k, node.m_jointype);
+ m_destPolys.push_back(m_destPoly);
+ m_destPoly.clear();
+ //re-build m_normals ...
+ DoublePoint n = m_normals[len -1];
+ for (int j = len - 1; j > 0; j--)
+ m_normals[j] = DoublePoint(-m_normals[j - 1].X, -m_normals[j - 1].Y);
+ m_normals[0] = DoublePoint(-n.X, -n.Y);
+ k = 0;
+ for (int j = len - 1; j >= 0; j--)
+ OffsetPoint(j, k, node.m_jointype);
+ m_destPolys.push_back(m_destPoly);
+ }
+ else
+ {
+ int k = 0;
+ for (int j = 1; j < len - 1; ++j)
+ OffsetPoint(j, k, node.m_jointype);
+
+ IntPoint pt1;
+ if (node.m_endtype == etOpenButt)
+ {
+ int j = len - 1;
+ pt1 = IntPoint((cInt)Round(m_srcPoly[j].X + m_normals[j].X *
+ delta), (cInt)Round(m_srcPoly[j].Y + m_normals[j].Y * delta));
+ m_destPoly.push_back(pt1);
+ pt1 = IntPoint((cInt)Round(m_srcPoly[j].X - m_normals[j].X *
+ delta), (cInt)Round(m_srcPoly[j].Y - m_normals[j].Y * delta));
+ m_destPoly.push_back(pt1);
+ }
+ else
+ {
+ int j = len - 1;
+ k = len - 2;
+ m_sinA = 0;
+ m_normals[j] = DoublePoint(-m_normals[j].X, -m_normals[j].Y);
+ if (node.m_endtype == etOpenSquare)
+ DoSquare(j, k);
+ else
+ DoRound(j, k);
+ }
+
+ //re-build m_normals ...
+ for (int j = len - 1; j > 0; j--)
+ m_normals[j] = DoublePoint(-m_normals[j - 1].X, -m_normals[j - 1].Y);
+ m_normals[0] = DoublePoint(-m_normals[1].X, -m_normals[1].Y);
+
+ k = len - 1;
+ for (int j = k - 1; j > 0; --j) OffsetPoint(j, k, node.m_jointype);
+
+ if (node.m_endtype == etOpenButt)
+ {
+ pt1 = IntPoint((cInt)Round(m_srcPoly[0].X - m_normals[0].X * delta),
+ (cInt)Round(m_srcPoly[0].Y - m_normals[0].Y * delta));
+ m_destPoly.push_back(pt1);
+ pt1 = IntPoint((cInt)Round(m_srcPoly[0].X + m_normals[0].X * delta),
+ (cInt)Round(m_srcPoly[0].Y + m_normals[0].Y * delta));
+ m_destPoly.push_back(pt1);
+ }
+ else
+ {
+ k = 1;
+ m_sinA = 0;
+ if (node.m_endtype == etOpenSquare)
+ DoSquare(0, 1);
+ else
+ DoRound(0, 1);
+ }
+ m_destPolys.push_back(m_destPoly);
+ }
+ }
+}
+//------------------------------------------------------------------------------
+
+void ClipperOffset::OffsetPoint(int j, int& k, JoinType jointype)
+{
+ //cross product ...
+ m_sinA = (m_normals[k].X * m_normals[j].Y - m_normals[j].X * m_normals[k].Y);
+ if (std::fabs(m_sinA * m_delta) < 1.0)
+ {
+ //dot product ...
+ double cosA = (m_normals[k].X * m_normals[j].X + m_normals[j].Y * m_normals[k].Y );
+ if (cosA > 0) // angle => 0 degrees
+ {
+ m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + m_normals[k].X * m_delta),
+ Round(m_srcPoly[j].Y + m_normals[k].Y * m_delta)));
+ return;
+ }
+ //else angle => 180 degrees
+ }
+ else if (m_sinA > 1.0) m_sinA = 1.0;
+ else if (m_sinA < -1.0) m_sinA = -1.0;
+
+ if (m_sinA * m_delta < 0)
+ {
+ m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + m_normals[k].X * m_delta),
+ Round(m_srcPoly[j].Y + m_normals[k].Y * m_delta)));
+ m_destPoly.push_back(m_srcPoly[j]);
+ m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + m_normals[j].X * m_delta),
+ Round(m_srcPoly[j].Y + m_normals[j].Y * m_delta)));
+ }
+ else
+ switch (jointype)
+ {
+ case jtMiter:
+ {
+ double r = 1 + (m_normals[j].X * m_normals[k].X +
+ m_normals[j].Y * m_normals[k].Y);
+ if (r >= m_miterLim) DoMiter(j, k, r); else DoSquare(j, k);
+ break;
+ }
+ case jtSquare: DoSquare(j, k); break;
+ case jtRound: DoRound(j, k); break;
+ }
+ k = j;
+}
+//------------------------------------------------------------------------------
+
+void ClipperOffset::DoSquare(int j, int k)
+{
+ double dx = std::tan(std::atan2(m_sinA,
+ m_normals[k].X * m_normals[j].X + m_normals[k].Y * m_normals[j].Y) / 4);
+ m_destPoly.push_back(IntPoint(
+ Round(m_srcPoly[j].X + m_delta * (m_normals[k].X - m_normals[k].Y * dx)),
+ Round(m_srcPoly[j].Y + m_delta * (m_normals[k].Y + m_normals[k].X * dx))));
+ m_destPoly.push_back(IntPoint(
+ Round(m_srcPoly[j].X + m_delta * (m_normals[j].X + m_normals[j].Y * dx)),
+ Round(m_srcPoly[j].Y + m_delta * (m_normals[j].Y - m_normals[j].X * dx))));
+}
+//------------------------------------------------------------------------------
+
+void ClipperOffset::DoMiter(int j, int k, double r)
+{
+ double q = m_delta / r;
+ m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + (m_normals[k].X + m_normals[j].X) * q),
+ Round(m_srcPoly[j].Y + (m_normals[k].Y + m_normals[j].Y) * q)));
+}
+//------------------------------------------------------------------------------
+
+void ClipperOffset::DoRound(int j, int k)
+{
+ double a = std::atan2(m_sinA,
+ m_normals[k].X * m_normals[j].X + m_normals[k].Y * m_normals[j].Y);
+ int steps = std::max((int)Round(m_StepsPerRad * std::fabs(a)), 1);
+
+ double X = m_normals[k].X, Y = m_normals[k].Y, X2;
+ for (int i = 0; i < steps; ++i)
+ {
+ m_destPoly.push_back(IntPoint(
+ Round(m_srcPoly[j].X + X * m_delta),
+ Round(m_srcPoly[j].Y + Y * m_delta)));
+ X2 = X;
+ X = X * m_cos - m_sin * Y;
+ Y = X2 * m_sin + Y * m_cos;
+ }
+ m_destPoly.push_back(IntPoint(
+ Round(m_srcPoly[j].X + m_normals[j].X * m_delta),
+ Round(m_srcPoly[j].Y + m_normals[j].Y * m_delta)));
+}
+
+//------------------------------------------------------------------------------
+// Miscellaneous public functions
+//------------------------------------------------------------------------------
+
+void Clipper::DoSimplePolygons()
+{
+ PolyOutList::size_type i = 0;
+ while (i < m_PolyOuts.size())
+ {
+ OutRec* outrec = m_PolyOuts[i++];
+ OutPt* op = outrec->Pts;
+ if (!op || outrec->IsOpen) continue;
+ do //for each Pt in Polygon until duplicate found do ...
+ {
+ OutPt* op2 = op->Next;
+ while (op2 != outrec->Pts)
+ {
+ if ((op->Pt == op2->Pt) && op2->Next != op && op2->Prev != op)
+ {
+ //split the polygon into two ...
+ OutPt* op3 = op->Prev;
+ OutPt* op4 = op2->Prev;
+ op->Prev = op4;
+ op4->Next = op;
+ op2->Prev = op3;
+ op3->Next = op2;
+
+ outrec->Pts = op;
+ OutRec* outrec2 = CreateOutRec();
+ outrec2->Pts = op2;
+ UpdateOutPtIdxs(*outrec2);
+ if (Poly2ContainsPoly1(outrec2->Pts, outrec->Pts))
+ {
+ //OutRec2 is contained by OutRec1 ...
+ outrec2->IsHole = !outrec->IsHole;
+ outrec2->FirstLeft = outrec;
+ if (m_UsingPolyTree) FixupFirstLefts2(outrec2, outrec);
+ }
+ else
+ if (Poly2ContainsPoly1(outrec->Pts, outrec2->Pts))
+ {
+ //OutRec1 is contained by OutRec2 ...
+ outrec2->IsHole = outrec->IsHole;
+ outrec->IsHole = !outrec2->IsHole;
+ outrec2->FirstLeft = outrec->FirstLeft;
+ outrec->FirstLeft = outrec2;
+ if (m_UsingPolyTree) FixupFirstLefts2(outrec, outrec2);
+ }
+ else
+ {
+ //the 2 polygons are separate ...
+ outrec2->IsHole = outrec->IsHole;
+ outrec2->FirstLeft = outrec->FirstLeft;
+ if (m_UsingPolyTree) FixupFirstLefts1(outrec, outrec2);
+ }
+ op2 = op; //ie get ready for the Next iteration
+ }
+ op2 = op2->Next;
+ }
+ op = op->Next;
+ }
+ while (op != outrec->Pts);
+ }
+}
+//------------------------------------------------------------------------------
+
+void ReversePath(Path& p)
+{
+ std::reverse(p.begin(), p.end());
+}
+//------------------------------------------------------------------------------
+
+void ReversePaths(Paths& p)
+{
+ for (Paths::size_type i = 0; i < p.size(); ++i)
+ ReversePath(p[i]);
+}
+//------------------------------------------------------------------------------
+
+void SimplifyPolygon(const Path &in_poly, Paths &out_polys, PolyFillType fillType)
+{
+ Clipper c;
+ c.StrictlySimple(true);
+ c.AddPath(in_poly, ptSubject, true);
+ c.Execute(ctUnion, out_polys, fillType, fillType);
+}
+//------------------------------------------------------------------------------
+
+void SimplifyPolygons(const Paths &in_polys, Paths &out_polys, PolyFillType fillType)
+{
+ Clipper c;
+ c.StrictlySimple(true);
+ c.AddPaths(in_polys, ptSubject, true);
+ c.Execute(ctUnion, out_polys, fillType, fillType);
+}
+//------------------------------------------------------------------------------
+
+void SimplifyPolygons(Paths &polys, PolyFillType fillType)
+{
+ SimplifyPolygons(polys, polys, fillType);
+}
+//------------------------------------------------------------------------------
+
+inline double DistanceSqrd(const IntPoint& pt1, const IntPoint& pt2)
+{
+ double Dx = ((double)pt1.X - pt2.X);
+ double dy = ((double)pt1.Y - pt2.Y);
+ return (Dx*Dx + dy*dy);
+}
+//------------------------------------------------------------------------------
+
+double DistanceFromLineSqrd(
+ const IntPoint& pt, const IntPoint& ln1, const IntPoint& ln2)
+{
+ //The equation of a line in general form (Ax + By + C = 0)
+ //given 2 points (xą,yą) & (x˛,y˛) is ...
+ //(yą - y˛)x + (x˛ - xą)y + (y˛ - yą)xą - (x˛ - xą)yą = 0
+ //A = (yą - y˛); B = (x˛ - xą); C = (y˛ - yą)xą - (x˛ - xą)yą
+ //perpendicular distance of point (xł,ył) = (Axł + Był + C)/Sqrt(A˛ + B˛)
+ //see http://en.wikipedia.org/wiki/Perpendicular_distance
+ double A = double(ln1.Y - ln2.Y);
+ double B = double(ln2.X - ln1.X);
+ double C = A * ln1.X + B * ln1.Y;
+ C = A * pt.X + B * pt.Y - C;
+ return (C * C) / (A * A + B * B);
+}
+//---------------------------------------------------------------------------
+
+bool SlopesNearCollinear(const IntPoint& pt1,
+ const IntPoint& pt2, const IntPoint& pt3, double distSqrd)
+{
+ //this function is more accurate when the point that's geometrically
+ //between the other 2 points is the one that's tested for distance.
+ //ie makes it more likely to pick up 'spikes' ...
+ if (Abs(pt1.X - pt2.X) > Abs(pt1.Y - pt2.Y))
+ {
+ if ((pt1.X > pt2.X) == (pt1.X < pt3.X))
+ return DistanceFromLineSqrd(pt1, pt2, pt3) < distSqrd;
+ else if ((pt2.X > pt1.X) == (pt2.X < pt3.X))
+ return DistanceFromLineSqrd(pt2, pt1, pt3) < distSqrd;
+ else
+ return DistanceFromLineSqrd(pt3, pt1, pt2) < distSqrd;
+ }
+ else
+ {
+ if ((pt1.Y > pt2.Y) == (pt1.Y < pt3.Y))
+ return DistanceFromLineSqrd(pt1, pt2, pt3) < distSqrd;
+ else if ((pt2.Y > pt1.Y) == (pt2.Y < pt3.Y))
+ return DistanceFromLineSqrd(pt2, pt1, pt3) < distSqrd;
+ else
+ return DistanceFromLineSqrd(pt3, pt1, pt2) < distSqrd;
+ }
+}
+//------------------------------------------------------------------------------
+
+bool PointsAreClose(IntPoint pt1, IntPoint pt2, double distSqrd)
+{
+ double Dx = (double)pt1.X - pt2.X;
+ double dy = (double)pt1.Y - pt2.Y;
+ return ((Dx * Dx) + (dy * dy) <= distSqrd);
+}
+//------------------------------------------------------------------------------
+
+OutPt* ExcludeOp(OutPt* op)
+{
+ OutPt* result = op->Prev;
+ result->Next = op->Next;
+ op->Next->Prev = result;
+ result->Idx = 0;
+ return result;
+}
+//------------------------------------------------------------------------------
+
+void CleanPolygon(const Path& in_poly, Path& out_poly, double distance)
+{
+ //distance = proximity in units/pixels below which vertices
+ //will be stripped. Default ~= sqrt(2).
+
+ size_t size = in_poly.size();
+
+ if (size == 0)
+ {
+ out_poly.clear();
+ return;
+ }
+
+ OutPt* outPts = new OutPt[size];
+ for (size_t i = 0; i < size; ++i)
+ {
+ outPts[i].Pt = in_poly[i];
+ outPts[i].Next = &outPts[(i + 1) % size];
+ outPts[i].Next->Prev = &outPts[i];
+ outPts[i].Idx = 0;
+ }
+
+ double distSqrd = distance * distance;
+ OutPt* op = &outPts[0];
+ while (op->Idx == 0 && op->Next != op->Prev)
+ {
+ if (PointsAreClose(op->Pt, op->Prev->Pt, distSqrd))
+ {
+ op = ExcludeOp(op);
+ size--;
+ }
+ else if (PointsAreClose(op->Prev->Pt, op->Next->Pt, distSqrd))
+ {
+ ExcludeOp(op->Next);
+ op = ExcludeOp(op);
+ size -= 2;
+ }
+ else if (SlopesNearCollinear(op->Prev->Pt, op->Pt, op->Next->Pt, distSqrd))
+ {
+ op = ExcludeOp(op);
+ size--;
+ }
+ else
+ {
+ op->Idx = 1;
+ op = op->Next;
+ }
+ }
+
+ if (size < 3) size = 0;
+ out_poly.resize(size);
+ for (size_t i = 0; i < size; ++i)
+ {
+ out_poly[i] = op->Pt;
+ op = op->Next;
+ }
+ delete [] outPts;
+}
+//------------------------------------------------------------------------------
+
+void CleanPolygon(Path& poly, double distance)
+{
+ CleanPolygon(poly, poly, distance);
+}
+//------------------------------------------------------------------------------
+
+void CleanPolygons(const Paths& in_polys, Paths& out_polys, double distance)
+{
+ out_polys.resize(in_polys.size());
+ for (Paths::size_type i = 0; i < in_polys.size(); ++i)
+ CleanPolygon(in_polys[i], out_polys[i], distance);
+}
+//------------------------------------------------------------------------------
+
+void CleanPolygons(Paths& polys, double distance)
+{
+ CleanPolygons(polys, polys, distance);
+}
+//------------------------------------------------------------------------------
+
+void Minkowski(const Path& poly, const Path& path,
+ Paths& solution, bool isSum, bool isClosed)
+{
+ int delta = (isClosed ? 1 : 0);
+ size_t polyCnt = poly.size();
+ size_t pathCnt = path.size();
+ Paths pp;
+ pp.reserve(pathCnt);
+ if (isSum)
+ for (size_t i = 0; i < pathCnt; ++i)
+ {
+ Path p;
+ p.reserve(polyCnt);
+ for (size_t j = 0; j < poly.size(); ++j)
+ p.push_back(IntPoint(path[i].X + poly[j].X, path[i].Y + poly[j].Y));
+ pp.push_back(p);
+ }
+ else
+ for (size_t i = 0; i < pathCnt; ++i)
+ {
+ Path p;
+ p.reserve(polyCnt);
+ for (size_t j = 0; j < poly.size(); ++j)
+ p.push_back(IntPoint(path[i].X - poly[j].X, path[i].Y - poly[j].Y));
+ pp.push_back(p);
+ }
+
+ solution.clear();
+ solution.reserve((pathCnt + delta) * (polyCnt + 1));
+ for (size_t i = 0; i < pathCnt - 1 + delta; ++i)
+ for (size_t j = 0; j < polyCnt; ++j)
+ {
+ Path quad;
+ quad.reserve(4);
+ quad.push_back(pp[i % pathCnt][j % polyCnt]);
+ quad.push_back(pp[(i + 1) % pathCnt][j % polyCnt]);
+ quad.push_back(pp[(i + 1) % pathCnt][(j + 1) % polyCnt]);
+ quad.push_back(pp[i % pathCnt][(j + 1) % polyCnt]);
+ if (!Orientation(quad)) ReversePath(quad);
+ solution.push_back(quad);
+ }
+}
+//------------------------------------------------------------------------------
+
+void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed)
+{
+ Minkowski(pattern, path, solution, true, pathIsClosed);
+ Clipper c;
+ c.AddPaths(solution, ptSubject, true);
+ c.Execute(ctUnion, solution, pftNonZero, pftNonZero);
+}
+//------------------------------------------------------------------------------
+
+void TranslatePath(const Path& input, Path& output, const IntPoint delta)
+{
+ //precondition: input != output
+ output.resize(input.size());
+ for (size_t i = 0; i < input.size(); ++i)
+ output[i] = IntPoint(input[i].X + delta.X, input[i].Y + delta.Y);
+}
+//------------------------------------------------------------------------------
+
+void MinkowskiSum(const Path& pattern, const Paths& paths, Paths& solution, bool pathIsClosed)
+{
+ Clipper c;
+ for (size_t i = 0; i < paths.size(); ++i)
+ {
+ Paths tmp;
+ Minkowski(pattern, paths[i], tmp, true, pathIsClosed);
+ c.AddPaths(tmp, ptSubject, true);
+ if (pathIsClosed)
+ {
+ Path tmp2;
+ TranslatePath(paths[i], tmp2, pattern[0]);
+ c.AddPath(tmp2, ptClip, true);
+ }
+ }
+ c.Execute(ctUnion, solution, pftNonZero, pftNonZero);
+}
+//------------------------------------------------------------------------------
+
+void MinkowskiDiff(const Path& poly1, const Path& poly2, Paths& solution)
+{
+ Minkowski(poly1, poly2, solution, false, true);
+ Clipper c;
+ c.AddPaths(solution, ptSubject, true);
+ c.Execute(ctUnion, solution, pftNonZero, pftNonZero);
+}
+//------------------------------------------------------------------------------
+
+enum NodeType {ntAny, ntOpen, ntClosed};
+
+void AddPolyNodeToPaths(const PolyNode& polynode, NodeType nodetype, Paths& paths)
+{
+ bool match = true;
+ if (nodetype == ntClosed) match = !polynode.IsOpen();
+ else if (nodetype == ntOpen) return;
+
+ if (!polynode.Contour.empty() && match)
+ paths.push_back(polynode.Contour);
+ for (int i = 0; i < polynode.ChildCount(); ++i)
+ AddPolyNodeToPaths(*polynode.Childs[i], nodetype, paths);
+}
+//------------------------------------------------------------------------------
+
+void PolyTreeToPaths(const PolyTree& polytree, Paths& paths)
+{
+ paths.resize(0);
+ paths.reserve(polytree.Total());
+ AddPolyNodeToPaths(polytree, ntAny, paths);
+}
+//------------------------------------------------------------------------------
+
+void ClosedPathsFromPolyTree(const PolyTree& polytree, Paths& paths)
+{
+ paths.resize(0);
+ paths.reserve(polytree.Total());
+ AddPolyNodeToPaths(polytree, ntClosed, paths);
+}
+//------------------------------------------------------------------------------
+
+void OpenPathsFromPolyTree(PolyTree& polytree, Paths& paths)
+{
+ paths.resize(0);
+ paths.reserve(polytree.Total());
+ //Open paths are top level only, so ...
+ for (int i = 0; i < polytree.ChildCount(); ++i)
+ if (polytree.Childs[i]->IsOpen())
+ paths.push_back(polytree.Childs[i]->Contour);
+}
+//------------------------------------------------------------------------------
+
+std::ostream& operator <<(std::ostream &s, const IntPoint &p)
+{
+ s << "(" << p.X << "," << p.Y << ")";
+ return s;
+}
+//------------------------------------------------------------------------------
+
+std::ostream& operator <<(std::ostream &s, const Path &p)
+{
+ if (p.empty()) return s;
+ Path::size_type last = p.size() -1;
+ for (Path::size_type i = 0; i < last; i++)
+ s << "(" << p[i].X << "," << p[i].Y << "), ";
+ s << "(" << p[last].X << "," << p[last].Y << ")\n";
+ return s;
+}
+//------------------------------------------------------------------------------
+
+std::ostream& operator <<(std::ostream &s, const Paths &p)
+{
+ for (Paths::size_type i = 0; i < p.size(); i++)
+ s << p[i];
+ s << "\n";
+ return s;
+}
+//------------------------------------------------------------------------------
+
+} //ClipperLib namespace
diff --git a/polygon/clipper.hpp b/polygon/clipper.hpp
new file mode 100644
index 0000000..1c38371
--- /dev/null
+++ b/polygon/clipper.hpp
@@ -0,0 +1,404 @@
+/*******************************************************************************
+* *
+* Author : Angus Johnson *
+* Version : 6.4.0 *
+* Date : 2 July 2015 *
+* Website : http://www.angusj.com *
+* Copyright : Angus Johnson 2010-2015 *
+* *
+* License: *
+* Use, modification & distribution is subject to Boost Software License Ver 1. *
+* http://www.boost.org/LICENSE_1_0.txt *
+* *
+* Attributions: *
+* The code in this library is an extension of Bala Vatti's clipping algorithm: *
+* "A generic solution to polygon clipping" *
+* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. *
+* http://portal.acm.org/citation.cfm?id=129906 *
+* *
+* Computer graphics and geometric modeling: implementation and algorithms *
+* By Max K. Agoston *
+* Springer; 1 edition (January 4, 2005) *
+* http://books.google.com/books?q=vatti+clipping+agoston *
+* *
+* See also: *
+* "Polygon Offsetting by Computing Winding Numbers" *
+* Paper no. DETC2005-85513 pp. 565-575 *
+* ASME 2005 International Design Engineering Technical Conferences *
+* and Computers and Information in Engineering Conference (IDETC/CIE2005) *
+* September 24-28, 2005 , Long Beach, California, USA *
+* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf *
+* *
+*******************************************************************************/
+
+#ifndef clipper_hpp
+#define clipper_hpp
+
+#define CLIPPER_VERSION "6.2.6"
+
+//use_int32: When enabled 32bit ints are used instead of 64bit ints. This
+//improve performance but coordinate values are limited to the range +/- 46340
+//#define use_int32
+
+//use_xyz: adds a Z member to IntPoint. Adds a minor cost to perfomance.
+//#define use_xyz
+
+//use_lines: Enables line clipping. Adds a very minor cost to performance.
+#define use_lines
+
+//use_deprecated: Enables temporary support for the obsolete functions
+//#define use_deprecated
+
+#include <vector>
+#include <list>
+#include <set>
+#include <stdexcept>
+#include <cstring>
+#include <cstdlib>
+#include <ostream>
+#include <functional>
+#include <queue>
+
+namespace ClipperLib {
+
+enum ClipType { ctIntersection, ctUnion, ctDifference, ctXor };
+enum PolyType { ptSubject, ptClip };
+//By far the most widely used winding rules for polygon filling are
+//EvenOdd & NonZero (GDI, GDI+, XLib, OpenGL, Cairo, AGG, Quartz, SVG, Gr32)
+//Others rules include Positive, Negative and ABS_GTR_EQ_TWO (only in OpenGL)
+//see http://glprogramming.com/red/chapter11.html
+enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative };
+
+#ifdef use_int32
+ typedef int cInt;
+ static cInt const loRange = 0x7FFF;
+ static cInt const hiRange = 0x7FFF;
+#else
+ typedef signed long long cInt;
+ static cInt const loRange = 0x3FFFFFFF;
+ static cInt const hiRange = 0x3FFFFFFFFFFFFFFFLL;
+ typedef signed long long long64; //used by Int128 class
+ typedef unsigned long long ulong64;
+
+#endif
+
+struct IntPoint {
+ cInt X;
+ cInt Y;
+#ifdef use_xyz
+ cInt Z;
+ IntPoint(cInt x = 0, cInt y = 0, cInt z = 0): X(x), Y(y), Z(z) {};
+#else
+ IntPoint(cInt x = 0, cInt y = 0): X(x), Y(y) {};
+#endif
+
+ friend inline bool operator== (const IntPoint& a, const IntPoint& b)
+ {
+ return a.X == b.X && a.Y == b.Y;
+ }
+ friend inline bool operator!= (const IntPoint& a, const IntPoint& b)
+ {
+ return a.X != b.X || a.Y != b.Y;
+ }
+};
+//------------------------------------------------------------------------------
+
+typedef std::vector< IntPoint > Path;
+typedef std::vector< Path > Paths;
+
+inline Path& operator <<(Path& poly, const IntPoint& p) {poly.push_back(p); return poly;}
+inline Paths& operator <<(Paths& polys, const Path& p) {polys.push_back(p); return polys;}
+
+std::ostream& operator <<(std::ostream &s, const IntPoint &p);
+std::ostream& operator <<(std::ostream &s, const Path &p);
+std::ostream& operator <<(std::ostream &s, const Paths &p);
+
+struct DoublePoint
+{
+ double X;
+ double Y;
+ DoublePoint(double x = 0, double y = 0) : X(x), Y(y) {}
+ DoublePoint(IntPoint ip) : X((double)ip.X), Y((double)ip.Y) {}
+};
+//------------------------------------------------------------------------------
+
+#ifdef use_xyz
+typedef void (*ZFillCallback)(IntPoint& e1bot, IntPoint& e1top, IntPoint& e2bot, IntPoint& e2top, IntPoint& pt);
+#endif
+
+enum InitOptions {ioReverseSolution = 1, ioStrictlySimple = 2, ioPreserveCollinear = 4};
+enum JoinType {jtSquare, jtRound, jtMiter};
+enum EndType {etClosedPolygon, etClosedLine, etOpenButt, etOpenSquare, etOpenRound};
+
+class PolyNode;
+typedef std::vector< PolyNode* > PolyNodes;
+
+class PolyNode
+{
+public:
+ PolyNode();
+ virtual ~PolyNode(){};
+ Path Contour;
+ PolyNodes Childs;
+ PolyNode* Parent;
+ PolyNode* GetNext() const;
+ bool IsHole() const;
+ bool IsOpen() const;
+ int ChildCount() const;
+private:
+ unsigned Index; //node index in Parent.Childs
+ bool m_IsOpen;
+ JoinType m_jointype;
+ EndType m_endtype;
+ PolyNode* GetNextSiblingUp() const;
+ void AddChild(PolyNode& child);
+ friend class Clipper; //to access Index
+ friend class ClipperOffset;
+};
+
+class PolyTree: public PolyNode
+{
+public:
+ ~PolyTree(){Clear();};
+ PolyNode* GetFirst() const;
+ void Clear();
+ int Total() const;
+private:
+ PolyNodes AllNodes;
+ friend class Clipper; //to access AllNodes
+};
+
+bool Orientation(const Path &poly);
+double Area(const Path &poly);
+int PointInPolygon(const IntPoint &pt, const Path &path);
+
+void SimplifyPolygon(const Path &in_poly, Paths &out_polys, PolyFillType fillType = pftEvenOdd);
+void SimplifyPolygons(const Paths &in_polys, Paths &out_polys, PolyFillType fillType = pftEvenOdd);
+void SimplifyPolygons(Paths &polys, PolyFillType fillType = pftEvenOdd);
+
+void CleanPolygon(const Path& in_poly, Path& out_poly, double distance = 1.415);
+void CleanPolygon(Path& poly, double distance = 1.415);
+void CleanPolygons(const Paths& in_polys, Paths& out_polys, double distance = 1.415);
+void CleanPolygons(Paths& polys, double distance = 1.415);
+
+void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed);
+void MinkowskiSum(const Path& pattern, const Paths& paths, Paths& solution, bool pathIsClosed);
+void MinkowskiDiff(const Path& poly1, const Path& poly2, Paths& solution);
+
+void PolyTreeToPaths(const PolyTree& polytree, Paths& paths);
+void ClosedPathsFromPolyTree(const PolyTree& polytree, Paths& paths);
+void OpenPathsFromPolyTree(PolyTree& polytree, Paths& paths);
+
+void ReversePath(Path& p);
+void ReversePaths(Paths& p);
+
+struct IntRect { cInt left; cInt top; cInt right; cInt bottom; };
+
+//enums that are used internally ...
+enum EdgeSide { esLeft = 1, esRight = 2};
+
+//forward declarations (for stuff used internally) ...
+struct TEdge;
+struct IntersectNode;
+struct LocalMinimum;
+struct OutPt;
+struct OutRec;
+struct Join;
+
+typedef std::vector < OutRec* > PolyOutList;
+typedef std::vector < TEdge* > EdgeList;
+typedef std::vector < Join* > JoinList;
+typedef std::vector < IntersectNode* > IntersectList;
+
+//------------------------------------------------------------------------------
+
+//ClipperBase is the ancestor to the Clipper class. It should not be
+//instantiated directly. This class simply abstracts the conversion of sets of
+//polygon coordinates into edge objects that are stored in a LocalMinima list.
+class ClipperBase
+{
+public:
+ ClipperBase();
+ virtual ~ClipperBase();
+ virtual bool AddPath(const Path &pg, PolyType PolyTyp, bool Closed);
+ bool AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed);
+ virtual void Clear();
+ IntRect GetBounds();
+ bool PreserveCollinear() {return m_PreserveCollinear;};
+ void PreserveCollinear(bool value) {m_PreserveCollinear = value;};
+protected:
+ void DisposeLocalMinimaList();
+ TEdge* AddBoundsToLML(TEdge *e, bool IsClosed);
+ virtual void Reset();
+ TEdge* ProcessBound(TEdge* E, bool IsClockwise);
+ void InsertScanbeam(const cInt Y);
+ bool PopScanbeam(cInt &Y);
+ bool LocalMinimaPending();
+ bool PopLocalMinima(cInt Y, const LocalMinimum *&locMin);
+ OutRec* CreateOutRec();
+ void DisposeAllOutRecs();
+ void DisposeOutRec(PolyOutList::size_type index);
+ void SwapPositionsInAEL(TEdge *edge1, TEdge *edge2);
+ void DeleteFromAEL(TEdge *e);
+ void UpdateEdgeIntoAEL(TEdge *&e);
+
+ typedef std::vector<LocalMinimum> MinimaList;
+ MinimaList::iterator m_CurrentLM;
+ MinimaList m_MinimaList;
+
+ bool m_UseFullRange;
+ EdgeList m_edges;
+ bool m_PreserveCollinear;
+ bool m_HasOpenPaths;
+ PolyOutList m_PolyOuts;
+ TEdge *m_ActiveEdges;
+
+ typedef std::priority_queue<cInt> ScanbeamList;
+ ScanbeamList m_Scanbeam;
+};
+//------------------------------------------------------------------------------
+
+class Clipper : public virtual ClipperBase
+{
+public:
+ Clipper(int initOptions = 0);
+ bool Execute(ClipType clipType,
+ Paths &solution,
+ PolyFillType fillType = pftEvenOdd);
+ bool Execute(ClipType clipType,
+ Paths &solution,
+ PolyFillType subjFillType,
+ PolyFillType clipFillType);
+ bool Execute(ClipType clipType,
+ PolyTree &polytree,
+ PolyFillType fillType = pftEvenOdd);
+ bool Execute(ClipType clipType,
+ PolyTree &polytree,
+ PolyFillType subjFillType,
+ PolyFillType clipFillType);
+ bool ReverseSolution() { return m_ReverseOutput; };
+ void ReverseSolution(bool value) {m_ReverseOutput = value;};
+ bool StrictlySimple() {return m_StrictSimple;};
+ void StrictlySimple(bool value) {m_StrictSimple = value;};
+ //set the callback function for z value filling on intersections (otherwise Z is 0)
+#ifdef use_xyz
+ void ZFillFunction(ZFillCallback zFillFunc);
+#endif
+protected:
+ virtual bool ExecuteInternal();
+private:
+ JoinList m_Joins;
+ JoinList m_GhostJoins;
+ IntersectList m_IntersectList;
+ ClipType m_ClipType;
+ typedef std::list<cInt> MaximaList;
+ MaximaList m_Maxima;
+ TEdge *m_SortedEdges;
+ bool m_ExecuteLocked;
+ PolyFillType m_ClipFillType;
+ PolyFillType m_SubjFillType;
+ bool m_ReverseOutput;
+ bool m_UsingPolyTree;
+ bool m_StrictSimple;
+#ifdef use_xyz
+ ZFillCallback m_ZFill; //custom callback
+#endif
+ void SetWindingCount(TEdge& edge);
+ bool IsEvenOddFillType(const TEdge& edge) const;
+ bool IsEvenOddAltFillType(const TEdge& edge) const;
+ void InsertLocalMinimaIntoAEL(const cInt botY);
+ void InsertEdgeIntoAEL(TEdge *edge, TEdge* startEdge);
+ void AddEdgeToSEL(TEdge *edge);
+ bool PopEdgeFromSEL(TEdge *&edge);
+ void CopyAELToSEL();
+ void DeleteFromSEL(TEdge *e);
+ void SwapPositionsInSEL(TEdge *edge1, TEdge *edge2);
+ bool IsContributing(const TEdge& edge) const;
+ bool IsTopHorz(const cInt XPos);
+ void DoMaxima(TEdge *e);
+ void ProcessHorizontals();
+ void ProcessHorizontal(TEdge *horzEdge);
+ void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt);
+ OutPt* AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt);
+ OutRec* GetOutRec(int idx);
+ void AppendPolygon(TEdge *e1, TEdge *e2);
+ void IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &pt);
+ OutPt* AddOutPt(TEdge *e, const IntPoint &pt);
+ OutPt* GetLastOutPt(TEdge *e);
+ bool ProcessIntersections(const cInt topY);
+ void BuildIntersectList(const cInt topY);
+ void ProcessIntersectList();
+ void ProcessEdgesAtTopOfScanbeam(const cInt topY);
+ void BuildResult(Paths& polys);
+ void BuildResult2(PolyTree& polytree);
+ void SetHoleState(TEdge *e, OutRec *outrec);
+ void DisposeIntersectNodes();
+ bool FixupIntersectionOrder();
+ void FixupOutPolygon(OutRec &outrec);
+ void FixupOutPolyline(OutRec &outrec);
+ bool IsHole(TEdge *e);
+ bool FindOwnerFromSplitRecs(OutRec &outRec, OutRec *&currOrfl);
+ void FixHoleLinkage(OutRec &outrec);
+ void AddJoin(OutPt *op1, OutPt *op2, const IntPoint offPt);
+ void ClearJoins();
+ void ClearGhostJoins();
+ void AddGhostJoin(OutPt *op, const IntPoint offPt);
+ bool JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2);
+ void JoinCommonEdges();
+ void DoSimplePolygons();
+ void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec);
+ void FixupFirstLefts2(OutRec* InnerOutRec, OutRec* OuterOutRec);
+ void FixupFirstLefts3(OutRec* OldOutRec, OutRec* NewOutRec);
+#ifdef use_xyz
+ void SetZ(IntPoint& pt, TEdge& e1, TEdge& e2);
+#endif
+};
+//------------------------------------------------------------------------------
+
+class ClipperOffset
+{
+public:
+ ClipperOffset(double miterLimit = 2.0, double roundPrecision = 0.25);
+ ~ClipperOffset();
+ void AddPath(const Path& path, JoinType joinType, EndType endType);
+ void AddPaths(const Paths& paths, JoinType joinType, EndType endType);
+ void Execute(Paths& solution, double delta);
+ void Execute(PolyTree& solution, double delta);
+ void Clear();
+ double MiterLimit;
+ double ArcTolerance;
+private:
+ Paths m_destPolys;
+ Path m_srcPoly;
+ Path m_destPoly;
+ std::vector<DoublePoint> m_normals;
+ double m_delta, m_sinA, m_sin, m_cos;
+ double m_miterLim, m_StepsPerRad;
+ IntPoint m_lowest;
+ PolyNode m_polyNodes;
+
+ void FixOrientations();
+ void DoOffset(double delta);
+ void OffsetPoint(int j, int& k, JoinType jointype);
+ void DoSquare(int j, int k);
+ void DoMiter(int j, int k, double r);
+ void DoRound(int j, int k);
+};
+//------------------------------------------------------------------------------
+
+class clipperException : public std::exception
+{
+ public:
+ clipperException(const char* description): m_descr(description) {}
+ virtual ~clipperException() throw() {}
+ virtual const char* what() const throw() {return m_descr.c_str();}
+ private:
+ std::string m_descr;
+};
+//------------------------------------------------------------------------------
+
+} //ClipperLib namespace
+
+#endif //clipper_hpp
+
+
diff --git a/polygon/determine_if_point_inside_polygon.odt b/polygon/determine_if_point_inside_polygon.odt
new file mode 100644
index 0000000..af612f7
--- /dev/null
+++ b/polygon/determine_if_point_inside_polygon.odt
Binary files differ
diff --git a/polygon/math_for_graphics.cpp b/polygon/math_for_graphics.cpp
new file mode 100644
index 0000000..c43f323
--- /dev/null
+++ b/polygon/math_for_graphics.cpp
@@ -0,0 +1,520 @@
+// math for graphics utility routines and RC, from FreePCB
+
+#include <vector>
+
+#include <cmath>
+#include <float.h>
+#include <limits.h>
+#include <common.h>
+#include <fctsys.h>
+
+#include <PolyLine.h>
+#include <math_for_graphics.h>
+
+static bool InRange( double x, double xi, double xf );
+
+/* Function FindSegmentIntersections
+ * find intersections between line segment (xi,yi) to (xf,yf)
+ * and line segment (xi2,yi2) to (xf2,yf2)
+ * returns true if intersection found
+ */
+bool FindSegmentIntersections( int xi, int yi, int xf, int yf,
+ int xi2, int yi2, int xf2, int yf2 )
+{
+ if( std::max( xi, xf ) < std::min( xi2, xf2 )
+ || std::min( xi, xf ) > std::max( xi2, xf2 )
+ || std::max( yi, yf ) < std::min( yi2, yf2 )
+ || std::min( yi, yf ) > std::max( yi2, yf2 ) )
+ return false;
+
+ return TestForIntersectionOfStraightLineSegments( xi, yi, xf, yf,
+ xi2, yi2, xf2, yf2 );
+}
+
+
+/* Function FindLineSegmentIntersection
+ * find intersection between line y = a + bx and line segment (xi,yi) to (xf,yf)
+ * if b > DBL_MAX/10, assume vertical line at x = a
+ * return false if no intersection or true if intersect
+ * return coords of intersections in *x1, *y1, *x2, *y2
+ * if no intersection, returns min distance in dist
+ */
+bool FindLineSegmentIntersection( double a, double b, int xi, int yi, int xf, int yf,
+ double* x1, double* y1, double* x2, double* y2,
+ double* dist )
+{
+ double xx = 0, yy = 0; // Init made to avoid C compil "uninitialized" warning
+ bool bVert = false;
+
+ if( b > DBL_MAX / 10.0 )
+ bVert = true;
+
+ if( xf != xi ) // non-vertical segment, get intersection
+ {
+ // horizontal or oblique straight segment
+ // put into form y = c + dx;
+ double d = (double) (yf - yi) / (double) (xf - xi);
+ double c = yf - d * xf;
+
+ if( bVert )
+ {
+ // if vertical line, easy
+ if( InRange( a, xi, xf ) )
+ {
+ *x1 = a;
+ *y1 = c + d * a;
+ return 1;
+ }
+ else
+ {
+ if( dist )
+ *dist = std::min( std::abs( a - xi ), std::abs( a - xf ) );
+
+ return false;
+ }
+ }
+
+ if( std::abs( b - d ) < 1E-12 )
+ {
+ // parallel lines
+ if( dist )
+ {
+ *dist = GetPointToLineDistance( a, b, xi, xf );
+ }
+
+ return false; // lines parallel
+ }
+
+ // calculate intersection
+ xx = (c - a) / (b - d);
+ yy = a + b * (xx);
+
+ // see if intersection is within the line segment
+ if( yf == yi )
+ {
+ // horizontal line
+ if( (xx>=xi && xx>xf) || (xx<=xi && xx<xf) )
+ return false;
+ }
+ else
+ {
+ // oblique line
+ if( (xx>=xi && xx>xf) || (xx<=xi && xx<xf)
+ || (yy>yi && yy>yf) || (yy<yi && yy<yf) )
+ return false;
+ }
+ }
+ else
+ {
+ // vertical line segment
+ if( bVert )
+ return false;
+
+ xx = xi;
+ yy = a + b * xx;
+
+ if( (yy>=yi && yy>yf) || (yy<=yi && yy<yf) )
+ return 0;
+ }
+
+ *x1 = xx;
+ *y1 = yy;
+ return true;
+}
+
+
+/*
+ * Function TestForIntersectionOfStraightLineSegments
+ * Test for intersection of line segments
+ * If lines are parallel, returns false
+ * If true, returns also intersection coords in x, y
+ * if false, returns min. distance in dist (may be 0.0 if parallel)
+ */
+bool TestForIntersectionOfStraightLineSegments( int x1i, int y1i, int x1f, int y1f,
+ int x2i, int y2i, int x2f, int y2f,
+ int* x, int* y, double* d )
+{
+ double a, b, dist;
+
+ // first, test for intersection
+ if( x1i == x1f && x2i == x2f )
+ {
+ // both segments are vertical, can't intersect
+ }
+ else if( y1i == y1f && y2i == y2f )
+ {
+ // both segments are horizontal, can't intersect
+ }
+ else if( x1i == x1f && y2i == y2f )
+ {
+ // first seg. vertical, second horizontal, see if they cross
+ if( InRange( x1i, x2i, x2f )
+ && InRange( y2i, y1i, y1f ) )
+ {
+ if( x )
+ *x = x1i;
+
+ if( y )
+ *y = y2i;
+
+ if( d )
+ *d = 0.0;
+
+ return true;
+ }
+ }
+ else if( y1i == y1f && x2i == x2f )
+ {
+ // first seg. horizontal, second vertical, see if they cross
+ if( InRange( y1i, y2i, y2f )
+ && InRange( x2i, x1i, x1f ) )
+ {
+ if( x )
+ *x = x2i;
+
+ if( y )
+ *y = y1i;
+
+ if( d )
+ *d = 0.0;
+
+ return true;
+ }
+ }
+ else if( x1i == x1f )
+ {
+ // first segment vertical, second oblique
+ // get a and b for second line segment, so that y = a + bx;
+ b = double( y2f - y2i ) / (x2f - x2i);
+ a = (double) y2i - b * x2i;
+
+ double x1, y1, x2, y2;
+ int test = FindLineSegmentIntersection( a, b, x1i, y1i, x1f, y1f,
+ &x1, &y1, &x2, &y2 );
+
+ if( test )
+ {
+ if( InRange( y1, y1i, y1f ) && InRange( x1, x2i, x2f ) && InRange( y1, y2i, y2f ) )
+ {
+ if( x )
+ *x = KiROUND( x1 );
+
+ if( y )
+ *y = KiROUND( y1 );
+
+ if( d )
+ *d = 0.0;
+
+ return true;
+ }
+ }
+ }
+ else if( y1i == y1f )
+ {
+ // first segment horizontal, second oblique
+ // get a and b for second line segment, so that y = a + bx;
+ b = double( y2f - y2i ) / (x2f - x2i);
+ a = (double) y2i - b * x2i;
+
+ double x1, y1, x2, y2;
+ int test = FindLineSegmentIntersection( a, b, x1i, y1i, x1f, y1f,
+ &x1, &y1, &x2, &y2 );
+
+ if( test )
+ {
+ if( InRange( x1, x1i, x1f ) && InRange( x1, x2i, x2f ) && InRange( y1, y2i, y2f ) )
+ {
+ if( x )
+ *x = KiROUND( x1 );
+
+ if( y )
+ *y = KiROUND( y1 );
+
+ if( d )
+ *d = 0.0;
+
+ return true;
+ }
+ }
+ }
+ else if( x2i == x2f )
+ {
+ // second segment vertical, first oblique
+ // get a and b for first line segment, so that y = a + bx;
+ b = double( y1f - y1i ) / (x1f - x1i);
+ a = (double) y1i - b * x1i;
+
+ double x1, y1, x2, y2;
+ int test = FindLineSegmentIntersection( a, b, x2i, y2i, x2f, y2f,
+ &x1, &y1, &x2, &y2 );
+
+ if( test )
+ {
+ if( InRange( x1, x1i, x1f ) && InRange( y1, y1i, y1f ) && InRange( y1, y2i, y2f ) )
+ {
+ if( x )
+ *x = KiROUND( x1 );
+
+ if( y )
+ *y = KiROUND( y1 );
+
+ if( d )
+ *d = 0.0;
+
+ return true;
+ }
+ }
+ }
+ else if( y2i == y2f )
+ {
+ // second segment horizontal, first oblique
+ // get a and b for second line segment, so that y = a + bx;
+ b = double( y1f - y1i ) / (x1f - x1i);
+ a = (double) y1i - b * x1i;
+
+ double x1, y1, x2, y2;
+ int test = FindLineSegmentIntersection( a, b, x2i, y2i, x2f, y2f,
+ &x1, &y1, &x2, &y2 );
+
+ if( test )
+ {
+ if( InRange( x1, x1i, x1f ) && InRange( y1, y1i, y1f ) )
+ {
+ if( x )
+ *x = KiROUND( x1 );
+
+ if( y )
+ *y = KiROUND( y1 );
+
+ if( d )
+ *d = 0.0;
+
+ return true;
+ }
+ }
+ }
+ else
+ {
+ // both segments oblique
+ if( long( y1f - y1i ) * (x2f - x2i) != long( y2f - y2i ) * (x1f - x1i) )
+ {
+ // not parallel, get a and b for first line segment, so that y = a + bx;
+ b = double( y1f - y1i ) / (x1f - x1i);
+ a = (double) y1i - b * x1i;
+
+ double x1, y1, x2, y2;
+ int test = FindLineSegmentIntersection( a, b, x2i, y2i, x2f, y2f,
+ &x1, &y1, &x2, &y2 );
+
+ // both segments oblique
+ if( test )
+ {
+ if( InRange( x1, x1i, x1f ) && InRange( y1, y1i, y1f ) )
+ {
+ if( x )
+ *x = KiROUND( x1 );
+
+ if( y )
+ *y = KiROUND( y1 );
+
+ if( d )
+ *d = 0.0;
+
+ return true;
+ }
+ }
+ }
+ }
+
+ // don't intersect, get shortest distance between each endpoint and the other line segment
+ dist = GetPointToLineSegmentDistance( x1i, y1i, x2i, y2i, x2f, y2f );
+
+ double xx = x1i;
+ double yy = y1i;
+ double dd = GetPointToLineSegmentDistance( x1f, y1f, x2i, y2i, x2f, y2f );
+
+ if( dd < dist )
+ {
+ dist = dd;
+ xx = x1f;
+ yy = y1f;
+ }
+
+ dd = GetPointToLineSegmentDistance( x2i, y2i, x1i, y1i, x1f, y1f );
+
+ if( dd < dist )
+ {
+ dist = dd;
+ xx = x2i;
+ yy = y2i;
+ }
+
+ dd = GetPointToLineSegmentDistance( x2f, y2f, x1i, y1i, x1f, y1f );
+
+ if( dd < dist )
+ {
+ dist = dd;
+ xx = x2f;
+ yy = y2f;
+ }
+
+ if( x )
+ *x = KiROUND( xx );
+
+ if( y )
+ *y = KiROUND( yy );
+
+ if( d )
+ *d = dist;
+
+ return false;
+}
+
+
+/* Function GetClearanceBetweenSegments
+ * Get clearance between 2 segments
+ * Returns coordinates of the closest point between these 2 segments in x, y
+ * If clearance > max_cl, just returns max_cl+1 and doesn't return x,y
+ */
+int GetClearanceBetweenSegments( int x1i, int y1i, int x1f, int y1f, int w1,
+ int x2i, int y2i, int x2f, int y2f, int w2,
+ int max_cl, int* x, int* y )
+{
+ // check clearance between bounding rectangles
+ int min_dist = max_cl + ( (w1 + w2) / 2 );
+
+ if( std::min( x1i, x1f ) - std::max( x2i, x2f ) > min_dist )
+ return max_cl+1;
+
+ if( std::min( x2i, x2f ) - std::max( x1i, x1f ) > min_dist )
+ return max_cl+1;
+
+ if( std::min( y1i, y1f ) - std::max( y2i, y2f ) > min_dist )
+ return max_cl+1;
+
+ if( std::min( y2i, y2f ) - std::max( y1i, y1f ) > min_dist )
+ return max_cl+1;
+
+ int xx, yy;
+ double dist;
+ TestForIntersectionOfStraightLineSegments( x1i, y1i, x1f, y1f,
+ x2i, y2i, x2f, y2f, &xx, &yy, &dist );
+ int d = KiROUND( dist ) - ((w1 + w2) / 2);
+ if( d < 0 )
+ d = 0;
+
+ if( x )
+ *x = xx;
+
+ if( y )
+ *y = yy;
+
+ return d;
+}
+
+
+/* Function GetPointToLineDistance
+ * Get min. distance from (x,y) to line y = a + bx
+ * if b > DBL_MAX/10, assume vertical line at x = a
+ * returns closest point on line in xpp, ypp
+ */
+double GetPointToLineDistance( double a, double b, int x, int y, double* xpp, double* ypp )
+{
+ if( b > DBL_MAX / 10 )
+ {
+ // vertical line
+ if( xpp && ypp )
+ {
+ *xpp = a;
+ *ypp = y;
+ }
+
+ return std::abs( a - x );
+ }
+
+ // find c,d such that (x,y) lies on y = c + dx where d=(-1/b)
+ double d = -1.0 / b;
+ double c = (double) y - d * x;
+
+ // find nearest point to (x,y) on line through (xi,yi) to (xf,yf)
+ double xp = (a - c) / (d - b);
+ double yp = a + b * xp;
+
+ if( xpp && ypp )
+ {
+ *xpp = xp;
+ *ypp = yp;
+ }
+
+ // find distance
+ return Distance( x, y, xp, yp );
+}
+
+
+/**
+ * Function GetPointToLineSegmentDistance
+ * Get distance between line segment and point
+ * @param x,y = point
+ * @param xi,yi Start point of the line segament
+ * @param xf,yf End point of the line segment
+ * @return the distance
+ */
+double GetPointToLineSegmentDistance( int x, int y, int xi, int yi, int xf, int yf )
+{
+ // test for vertical or horizontal segment
+ if( xf==xi )
+ {
+ // vertical line segment
+ if( InRange( y, yi, yf ) )
+ return std::abs( x - xi );
+ else
+ return std::min( Distance( x, y, xi, yi ), Distance( x, y, xf, yf ) );
+ }
+ else if( yf==yi )
+ {
+ // horizontal line segment
+ if( InRange( x, xi, xf ) )
+ return std::abs( y - yi );
+ else
+ return std::min( Distance( x, y, xi, yi ), Distance( x, y, xf, yf ) );
+ }
+ else
+ {
+ // oblique segment
+ // find a,b such that (xi,yi) and (xf,yf) lie on y = a + bx
+ double b = (double) (yf - yi) / (xf - xi);
+ double a = (double) yi - b * xi;
+
+ // find c,d such that (x,y) lies on y = c + dx where d=(-1/b)
+ double d = -1.0 / b;
+ double c = (double) y - d * x;
+
+ // find nearest point to (x,y) on line through (xi,yi) to (xf,yf)
+ double xp = (a - c) / (d - b);
+ double yp = a + b * xp;
+
+ // find distance
+ if( InRange( xp, xi, xf ) && InRange( yp, yi, yf ) )
+ return Distance( x, y, xp, yp );
+ else
+ return std::min( Distance( x, y, xi, yi ), Distance( x, y, xf, yf ) );
+ }
+}
+
+
+// test for value within range
+bool InRange( double x, double xi, double xf )
+{
+ if( xf > xi )
+ {
+ if( x >= xi && x <= xf )
+ return true;
+ }
+ else
+ {
+ if( x >= xf && x <= xi )
+ return true;
+ }
+
+ return false;
+}
diff --git a/polygon/math_for_graphics.h b/polygon/math_for_graphics.h
new file mode 100644
index 0000000..c8be901
--- /dev/null
+++ b/polygon/math_for_graphics.h
@@ -0,0 +1,71 @@
+#ifndef MATH_FOR_GRAPHICS_H
+#define MATH_FOR_GRAPHICS_H
+// math stuff for graphics, from FreePCB
+
+/* Function FindLineSegmentIntersection
+ * find intersection between line y = a + bx and line segment (xi,yi) to (xf,yf)
+ * if b > DBL_MAX/10, assume vertical line at x = a
+ * return false if no intersection or true if intersect
+ * return coords of intersections in *x1, *y1, *x2, *y2
+ * if no intersection, returns min distance in dist
+ */
+bool FindLineSegmentIntersection( double a, double b, int xi, int yi, int xf, int yf,
+ double * x1, double * y1, double * x2, double * y2, double * dist=NULL );
+
+/* Function FindSegmentIntersections
+ * find intersections between line segment (xi,yi) to (xf,yf)
+ * and line segment (xi2,yi2) to (xf2,yf2)
+ * returns true if intersection found
+ */
+bool FindSegmentIntersections( int xi, int yi, int xf, int yf,
+ int xi2, int yi2, int xf2, int yf2 );
+
+/**
+ * Function TestForIntersectionOfStraightLineSegments
+ * Test for intersection of line segments
+ * If lines are parallel, returns false
+ * If true, returns also intersection coords in x, y
+ * if false, returns min. distance in dist (may be 0.0 if parallel)
+ * and coords on nearest point in one of the segments in (x,y)
+ * @param x1i, y1i, x1f, y1f = integer coordinates of the first segment
+ * @param x2i, y2i, x2f, y2f = integer coordinates of the other segment
+ * @param x, y = pointers on 2 integer to store the intersection coordinates (can be NULL)
+ * @param dist = pointeur on a double to store the dist.
+ * @return true if intersect.
+ */
+bool TestForIntersectionOfStraightLineSegments( int x1i, int y1i, int x1f, int y1f,
+ int x2i, int y2i, int x2f, int y2f,
+ int * x=NULL, int * y=NULL, double * dist=NULL );
+
+/* Function GetClearanceBetweenSegments
+ * Get clearance between 2 segments
+ * Returns coordinates of the closest point between these 2 segments in x, y
+ * If clearance > max_cl, just returns max_cl+1 and doesn't return x,y
+ */
+int GetClearanceBetweenSegments( int x1i, int y1i, int x1f, int y1f, int w1,
+ int x2i, int y2i, int x2f, int y2f, int w2,
+ int max_cl, int * x, int * y );
+
+/**
+ * Function GetPointToLineSegmentDistance
+ * Get distance between line segment and point
+ * @param x,y = point
+ * @param xi,yi, xf,yf = the end-points of the line segment
+ * @return the distance
+ */
+double GetPointToLineSegmentDistance( int x, int y, int xi, int yi, int xf, int yf );
+
+/* Function GetPointToLineDistance
+ * Get min. distance from (x,y) to line y = a + bx
+ * if b > DBL_MAX/10, assume vertical line at x = a
+ * returns closest point on line in xpp, ypp
+ */
+double GetPointToLineDistance( double a, double b, int x, int y,
+ double * xp=NULL, double * yp=NULL );
+
+inline double Distance( double x1, double y1, double x2, double y2 )
+{
+ return hypot( x1 - x2, y1 - y2 );
+}
+
+#endif
diff --git a/polygon/poly2tri/common/shapes.cc b/polygon/poly2tri/common/shapes.cc
new file mode 100644
index 0000000..06eb1f8
--- /dev/null
+++ b/polygon/poly2tri/common/shapes.cc
@@ -0,0 +1,500 @@
+/*
+ * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
+ * http://code.google.com/p/poly2tri/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "shapes.h"
+#include <iostream>
+
+namespace p2t {
+Triangle::Triangle( Point& a, Point& b, Point& c )
+{
+ points_[0] = &a; points_[1] = &b; points_[2] = &c;
+ neighbors_[0] = NULL; neighbors_[1] = NULL; neighbors_[2] = NULL;
+ constrained_edge[0] = constrained_edge[1] = constrained_edge[2] = false;
+ delaunay_edge[0] = delaunay_edge[1] = delaunay_edge[2] = false;
+ interior_ = false;
+}
+
+
+// Update neighbor pointers
+void Triangle::MarkNeighbor( Point* p1, Point* p2, Triangle* t )
+{
+ if( (p1 == points_[2] && p2 == points_[1]) || (p1 == points_[1] && p2 == points_[2]) )
+ neighbors_[0] = t;
+ else if( (p1 == points_[0] && p2 == points_[2]) || (p1 == points_[2] && p2 == points_[0]) )
+ neighbors_[1] = t;
+ else if( (p1 == points_[0] && p2 == points_[1]) || (p1 == points_[1] && p2 == points_[0]) )
+ neighbors_[2] = t;
+ else
+ assert( 0 );
+}
+
+
+// Exhaustive search to update neighbor pointers
+void Triangle::MarkNeighbor( Triangle& t )
+{
+ if( t.Contains( points_[1], points_[2] ) )
+ {
+ neighbors_[0] = &t;
+ t.MarkNeighbor( points_[1], points_[2], this );
+ }
+ else if( t.Contains( points_[0], points_[2] ) )
+ {
+ neighbors_[1] = &t;
+ t.MarkNeighbor( points_[0], points_[2], this );
+ }
+ else if( t.Contains( points_[0], points_[1] ) )
+ {
+ neighbors_[2] = &t;
+ t.MarkNeighbor( points_[0], points_[1], this );
+ }
+}
+
+
+/**
+ * Clears all references to all other triangles and points
+ */
+void Triangle::Clear()
+{
+ Triangle* t;
+
+ for( int i = 0; i<3; i++ )
+ {
+ t = neighbors_[i];
+
+ if( t != NULL )
+ {
+ t->ClearNeighbor( this );
+ }
+ }
+
+ ClearNeighbors();
+ points_[0] = points_[1] = points_[2] = NULL;
+}
+
+
+void Triangle::ClearNeighbor( Triangle* triangle )
+{
+ if( neighbors_[0] == triangle )
+ {
+ neighbors_[0] = NULL;
+ }
+ else if( neighbors_[1] == triangle )
+ {
+ neighbors_[1] = NULL;
+ }
+ else
+ {
+ neighbors_[2] = NULL;
+ }
+}
+
+
+void Triangle::ClearNeighbors()
+{
+ neighbors_[0] = NULL;
+ neighbors_[1] = NULL;
+ neighbors_[2] = NULL;
+}
+
+
+void Triangle::ClearDelunayEdges()
+{
+ delaunay_edge[0] = delaunay_edge[1] = delaunay_edge[2] = false;
+}
+
+
+Point* Triangle::OppositePoint( Triangle& t, Point& p )
+{
+ Point* cw = t.PointCW( p );
+
+ /*
+ double x = cw->x;
+ double y = cw->y;
+
+ x = p.x;
+ y = p.y;
+ */
+
+ return PointCW( *cw );
+}
+
+
+// Legalized triangle by rotating clockwise around point(0)
+void Triangle::Legalize( Point& point )
+{
+ points_[1] = points_[0];
+ points_[0] = points_[2];
+ points_[2] = &point;
+}
+
+
+// Legalize triagnle by rotating clockwise around oPoint
+void Triangle::Legalize( Point& opoint, Point& npoint )
+{
+ if( &opoint == points_[0] )
+ {
+ points_[1] = points_[0];
+ points_[0] = points_[2];
+ points_[2] = &npoint;
+ }
+ else if( &opoint == points_[1] )
+ {
+ points_[2] = points_[1];
+ points_[1] = points_[0];
+ points_[0] = &npoint;
+ }
+ else if( &opoint == points_[2] )
+ {
+ points_[0] = points_[2];
+ points_[2] = points_[1];
+ points_[1] = &npoint;
+ }
+ else
+ {
+ assert( 0 );
+ }
+}
+
+
+int Triangle::Index( const Point* p )
+{
+ if( p == points_[0] )
+ {
+ return 0;
+ }
+ else if( p == points_[1] )
+ {
+ return 1;
+ }
+ else if( p == points_[2] )
+ {
+ return 2;
+ }
+
+ assert( 0 );
+ return 0; // you better hope its a Debug build.
+}
+
+
+int Triangle::EdgeIndex( const Point* p1, const Point* p2 )
+{
+ if( points_[0] == p1 )
+ {
+ if( points_[1] == p2 )
+ {
+ return 2;
+ }
+ else if( points_[2] == p2 )
+ {
+ return 1;
+ }
+ }
+ else if( points_[1] == p1 )
+ {
+ if( points_[2] == p2 )
+ {
+ return 0;
+ }
+ else if( points_[0] == p2 )
+ {
+ return 2;
+ }
+ }
+ else if( points_[2] == p1 )
+ {
+ if( points_[0] == p2 )
+ {
+ return 1;
+ }
+ else if( points_[1] == p2 )
+ {
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+
+void Triangle::MarkConstrainedEdge( const int index )
+{
+ constrained_edge[index] = true;
+}
+
+
+void Triangle::MarkConstrainedEdge( Edge& edge )
+{
+ MarkConstrainedEdge( edge.p, edge.q );
+}
+
+
+// Mark edge as constrained
+void Triangle::MarkConstrainedEdge( Point* p, Point* q )
+{
+ if( (q == points_[0] && p == points_[1]) || (q == points_[1] && p == points_[0]) )
+ {
+ constrained_edge[2] = true;
+ }
+ else if( (q == points_[0] && p == points_[2]) || (q == points_[2] && p == points_[0]) )
+ {
+ constrained_edge[1] = true;
+ }
+ else if( (q == points_[1] && p == points_[2]) || (q == points_[2] && p == points_[1]) )
+ {
+ constrained_edge[0] = true;
+ }
+}
+
+
+// The point counter-clockwise to given point
+Point* Triangle::PointCW( Point& point )
+{
+ if( &point == points_[0] )
+ {
+ return points_[2];
+ }
+ else if( &point == points_[1] )
+ {
+ return points_[0];
+ }
+ else if( &point == points_[2] )
+ {
+ return points_[1];
+ }
+
+ assert( 0 );
+ return NULL; // you better hope its a Debug build.
+}
+
+
+// The point counter-clockwise to given point
+Point* Triangle::PointCCW( Point& point )
+{
+ if( &point == points_[0] )
+ {
+ return points_[1];
+ }
+ else if( &point == points_[1] )
+ {
+ return points_[2];
+ }
+ else if( &point == points_[2] )
+ {
+ return points_[0];
+ }
+
+ assert( 0 );
+ return NULL; // you better hope its a Debug build.
+}
+
+
+// The neighbor clockwise to given point
+Triangle* Triangle::NeighborCW( Point& point )
+{
+ if( &point == points_[0] )
+ {
+ return neighbors_[1];
+ }
+ else if( &point == points_[1] )
+ {
+ return neighbors_[2];
+ }
+
+ return neighbors_[0];
+}
+
+
+// The neighbor counter-clockwise to given point
+Triangle* Triangle::NeighborCCW( Point& point )
+{
+ if( &point == points_[0] )
+ {
+ return neighbors_[2];
+ }
+ else if( &point == points_[1] )
+ {
+ return neighbors_[0];
+ }
+
+ return neighbors_[1];
+}
+
+
+bool Triangle::GetConstrainedEdgeCCW( Point& p )
+{
+ if( &p == points_[0] )
+ {
+ return constrained_edge[2];
+ }
+ else if( &p == points_[1] )
+ {
+ return constrained_edge[0];
+ }
+
+ return constrained_edge[1];
+}
+
+
+bool Triangle::GetConstrainedEdgeCW( Point& p )
+{
+ if( &p == points_[0] )
+ {
+ return constrained_edge[1];
+ }
+ else if( &p == points_[1] )
+ {
+ return constrained_edge[2];
+ }
+
+ return constrained_edge[0];
+}
+
+
+void Triangle::SetConstrainedEdgeCCW( Point& p, bool ce )
+{
+ if( &p == points_[0] )
+ {
+ constrained_edge[2] = ce;
+ }
+ else if( &p == points_[1] )
+ {
+ constrained_edge[0] = ce;
+ }
+ else
+ {
+ constrained_edge[1] = ce;
+ }
+}
+
+
+void Triangle::SetConstrainedEdgeCW( Point& p, bool ce )
+{
+ if( &p == points_[0] )
+ {
+ constrained_edge[1] = ce;
+ }
+ else if( &p == points_[1] )
+ {
+ constrained_edge[2] = ce;
+ }
+ else
+ {
+ constrained_edge[0] = ce;
+ }
+}
+
+
+bool Triangle::GetDelunayEdgeCCW( Point& p )
+{
+ if( &p == points_[0] )
+ {
+ return delaunay_edge[2];
+ }
+ else if( &p == points_[1] )
+ {
+ return delaunay_edge[0];
+ }
+
+ return delaunay_edge[1];
+}
+
+
+bool Triangle::GetDelunayEdgeCW( Point& p )
+{
+ if( &p == points_[0] )
+ {
+ return delaunay_edge[1];
+ }
+ else if( &p == points_[1] )
+ {
+ return delaunay_edge[2];
+ }
+
+ return delaunay_edge[0];
+}
+
+
+void Triangle::SetDelunayEdgeCCW( Point& p, bool e )
+{
+ if( &p == points_[0] )
+ {
+ delaunay_edge[2] = e;
+ }
+ else if( &p == points_[1] )
+ {
+ delaunay_edge[0] = e;
+ }
+ else
+ {
+ delaunay_edge[1] = e;
+ }
+}
+
+
+void Triangle::SetDelunayEdgeCW( Point& p, bool e )
+{
+ if( &p == points_[0] )
+ {
+ delaunay_edge[1] = e;
+ }
+ else if( &p == points_[1] )
+ {
+ delaunay_edge[2] = e;
+ }
+ else
+ {
+ delaunay_edge[0] = e;
+ }
+}
+
+
+// The neighbor across to given point
+Triangle& Triangle::NeighborAcross( Point& opoint )
+{
+ if( &opoint == points_[0] )
+ {
+ return *neighbors_[0];
+ }
+ else if( &opoint == points_[1] )
+ {
+ return *neighbors_[1];
+ }
+
+ return *neighbors_[2];
+}
+
+
+void Triangle::DebugPrint()
+{
+ std::cout << points_[0]->x << "," << points_[0]->y << " ";
+ std::cout << points_[1]->x << "," << points_[1]->y << " ";
+ std::cout << points_[2]->x << "," << points_[2]->y << "\n";
+}
+}
diff --git a/polygon/poly2tri/common/shapes.h b/polygon/poly2tri/common/shapes.h
new file mode 100644
index 0000000..c65f485
--- /dev/null
+++ b/polygon/poly2tri/common/shapes.h
@@ -0,0 +1,351 @@
+/*
+ * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
+ * http://code.google.com/p/poly2tri/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// Include guard
+#ifndef SHAPES_H
+#define SHAPES_H
+
+#include <vector>
+#include <cstddef>
+#include <assert.h>
+#include <cmath>
+
+namespace p2t {
+struct Edge;
+
+struct Point
+{
+ double x, y;
+
+ /// Default constructor does nothing (for performance).
+ Point()
+ {
+ x = 0.0;
+ y = 0.0;
+ }
+
+ /// The edges this point constitutes an upper ending point
+ std::vector<Edge*> edge_list;
+
+ /// Construct using coordinates.
+ Point( double x, double y ) : x( x ), y( y ) {}
+
+ /// Set this point to all zeros.
+ void set_zero()
+ {
+ x = 0.0;
+ y = 0.0;
+ }
+
+ /// Set this point to some specified coordinates.
+ void set( double x_, double y_ )
+ {
+ x = x_;
+ y = y_;
+ }
+
+ /// Negate this point.
+ Point operator -() const
+ {
+ Point v;
+
+ v.set( -x, -y );
+ return v;
+ }
+
+ /// Add a point to this point.
+ void operator +=( const Point& v )
+ {
+ x += v.x;
+ y += v.y;
+ }
+
+ /// Subtract a point from this point.
+ void operator -=( const Point& v )
+ {
+ x -= v.x;
+ y -= v.y;
+ }
+
+ /// Multiply this point by a scalar.
+ void operator *=( double a )
+ {
+ x *= a;
+ y *= a;
+ }
+
+ /// Get the length of this point (the norm).
+ double Length() const
+ {
+ return sqrt( x * x + y * y );
+ }
+
+ /// Convert this point into a unit point. Returns the Length.
+ double Normalize()
+ {
+ double len = Length();
+
+ x /= len;
+ y /= len;
+ return len;
+ }
+};
+
+// Represents a simple polygon's edge
+struct Edge
+{
+ Point* p, * q;
+
+ /// Constructor
+ Edge( Point& p1, Point& p2 ) : p( &p1 ), q( &p2 )
+ {
+ if( p1.y > p2.y )
+ {
+ q = &p1;
+ p = &p2;
+ }
+ else if( p1.y == p2.y )
+ {
+ if( p1.x > p2.x )
+ {
+ q = &p1;
+ p = &p2;
+ }
+ else if( p1.x == p2.x )
+ {
+ // Repeat points
+ assert( false );
+ }
+ }
+
+ q->edge_list.push_back( this );
+ }
+};
+
+// Triangle-based data structures are know to have better performance than quad-edge structures
+// See: J. Shewchuk, "Triangle: Engineering a 2D Quality Mesh Generator and Delaunay Triangulator"
+// "Triangulations in CGAL"
+class Triangle
+{
+public:
+
+/// Constructor
+ Triangle( Point& a, Point& b, Point& c );
+
+/// Flags to determine if an edge is a Constrained edge
+ bool constrained_edge[3];
+/// Flags to determine if an edge is a Delauney edge
+ bool delaunay_edge[3];
+
+ Point* GetPoint( const int& index );
+ Point* PointCW( Point& point );
+ Point* PointCCW( Point& point );
+ Point* OppositePoint( Triangle& t, Point& p );
+
+ Triangle* GetNeighbor( const int& index );
+ void MarkNeighbor( Point* p1, Point* p2, Triangle* t );
+ void MarkNeighbor( Triangle& t );
+
+ void MarkConstrainedEdge( const int index );
+ void MarkConstrainedEdge( Edge& edge );
+ void MarkConstrainedEdge( Point* p, Point* q );
+
+ int Index( const Point* p );
+ int EdgeIndex( const Point* p1, const Point* p2 );
+
+ Triangle* NeighborCW( Point& point );
+ Triangle* NeighborCCW( Point& point );
+ bool GetConstrainedEdgeCCW( Point& p );
+ bool GetConstrainedEdgeCW( Point& p );
+ void SetConstrainedEdgeCCW( Point& p, bool ce );
+ void SetConstrainedEdgeCW( Point& p, bool ce );
+ bool GetDelunayEdgeCCW( Point& p );
+ bool GetDelunayEdgeCW( Point& p );
+ void SetDelunayEdgeCCW( Point& p, bool e );
+ void SetDelunayEdgeCW( Point& p, bool e );
+
+ bool Contains( Point* p );
+ bool Contains( const Edge& e );
+ bool Contains( Point* p, Point* q );
+ void Legalize( Point& point );
+ void Legalize( Point& opoint, Point& npoint );
+
+/**
+ * Clears all references to all other triangles and points
+ */
+ void Clear();
+ void ClearNeighbor( Triangle* triangle );
+ void ClearNeighbors();
+ void ClearDelunayEdges();
+
+ inline bool IsInterior();
+ inline void IsInterior( bool b );
+
+ Triangle& NeighborAcross( Point& opoint );
+
+ void DebugPrint();
+
+private:
+
+/// Triangle points
+ Point* points_[3];
+/// Neighbor list
+ Triangle* neighbors_[3];
+
+/// Has this triangle been marked as an interior triangle?
+ bool interior_;
+};
+
+inline bool cmp( const Point* a, const Point* b )
+{
+ if( a->y < b->y )
+ {
+ return true;
+ }
+ else if( a->y == b->y )
+ {
+ // Make sure q is point with greater x value
+ if( a->x < b->x )
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+/// Add two points_ component-wise.
+inline Point operator +( const Point& a, const Point& b )
+{
+ return Point( a.x + b.x, a.y + b.y );
+}
+
+
+/// Subtract two points_ component-wise.
+inline Point operator -( const Point& a, const Point& b )
+{
+ return Point( a.x - b.x, a.y - b.y );
+}
+
+
+/// Multiply point by scalar
+inline Point operator *( double s, const Point& a )
+{
+ return Point( s * a.x, s * a.y );
+}
+
+
+inline bool operator ==( const Point& a, const Point& b )
+{
+ return a.x == b.x && a.y == b.y;
+}
+
+
+inline bool operator !=( const Point& a, const Point& b )
+{
+ return !(a.x == b.x) && !(a.y == b.y);
+}
+
+
+/// Peform the dot product on two vectors.
+inline double Dot( const Point& a, const Point& b )
+{
+ return a.x * b.x + a.y * b.y;
+}
+
+
+/// Perform the cross product on two vectors. In 2D this produces a scalar.
+inline double Cross( const Point& a, const Point& b )
+{
+ return a.x * b.y - a.y * b.x;
+}
+
+
+/// Perform the cross product on a point and a scalar. In 2D this produces
+/// a point.
+inline Point Cross( const Point& a, double s )
+{
+ return Point( s * a.y, -s * a.x );
+}
+
+
+/// Perform the cross product on a scalar and a point. In 2D this produces
+/// a point.
+inline Point Cross( const double s, const Point& a )
+{
+ return Point( -s * a.y, s * a.x );
+}
+
+
+inline Point* Triangle::GetPoint( const int& index )
+{
+ return points_[index];
+}
+
+
+inline Triangle* Triangle::GetNeighbor( const int& index )
+{
+ return neighbors_[index];
+}
+
+
+inline bool Triangle::Contains( Point* p )
+{
+ return p == points_[0] || p == points_[1] || p == points_[2];
+}
+
+
+inline bool Triangle::Contains( const Edge& e )
+{
+ return Contains( e.p ) && Contains( e.q );
+}
+
+
+inline bool Triangle::Contains( Point* p, Point* q )
+{
+ return Contains( p ) && Contains( q );
+}
+
+
+inline bool Triangle::IsInterior()
+{
+ return interior_;
+}
+
+
+inline void Triangle::IsInterior( bool b )
+{
+ interior_ = b;
+}
+}
+
+#endif
diff --git a/polygon/poly2tri/common/utils.h b/polygon/poly2tri/common/utils.h
new file mode 100644
index 0000000..3de9fb1
--- /dev/null
+++ b/polygon/poly2tri/common/utils.h
@@ -0,0 +1,133 @@
+/*
+ * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
+ * http://code.google.com/p/poly2tri/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UTILS_H
+#define UTILS_H
+
+// Otherwise #defines like M_PI are undeclared under Visual Studio
+#define _USE_MATH_DEFINES
+
+#include <exception>
+#include <math.h>
+
+namespace p2t {
+const double PI_3div4 = 3 * M_PI / 4;
+const double PI_div2 = 1.57079632679489661923;
+const double EPSILON = 1e-12;
+
+enum Orientation {
+ CW, CCW, COLLINEAR
+};
+
+/**
+ * Forumla to calculate signed area<br>
+ * Positive if CCW<br>
+ * Negative if CW<br>
+ * 0 if collinear<br>
+ * <pre>
+ * A[P1,P2,P3] = (x1*y2 - y1*x2) + (x2*y3 - y2*x3) + (x3*y1 - y3*x1)
+ * = (x1-x3)*(y2-y3) - (y1-y3)*(x2-x3)
+ * </pre>
+ */
+Orientation Orient2d( Point& pa, Point& pb, Point& pc )
+{
+ double detleft = (pa.x - pc.x) * (pb.y - pc.y);
+ double detright = (pa.y - pc.y) * (pb.x - pc.x);
+ double val = detleft - detright;
+
+ if( val > -EPSILON && val < EPSILON )
+ {
+ return COLLINEAR;
+ }
+ else if( val > 0 )
+ {
+ return CCW;
+ }
+
+ return CW;
+}
+
+
+/*
+ * bool InScanArea(Point& pa, Point& pb, Point& pc, Point& pd)
+ * {
+ * double pdx = pd.x;
+ * double pdy = pd.y;
+ * double adx = pa.x - pdx;
+ * double ady = pa.y - pdy;
+ * double bdx = pb.x - pdx;
+ * double bdy = pb.y - pdy;
+ *
+ * double adxbdy = adx * bdy;
+ * double bdxady = bdx * ady;
+ * double oabd = adxbdy - bdxady;
+ *
+ * if (oabd <= EPSILON) {
+ * return false;
+ * }
+ *
+ * double cdx = pc.x - pdx;
+ * double cdy = pc.y - pdy;
+ *
+ * double cdxady = cdx * ady;
+ * double adxcdy = adx * cdy;
+ * double ocad = cdxady - adxcdy;
+ *
+ * if (ocad <= EPSILON) {
+ * return false;
+ * }
+ *
+ * return true;
+ * }
+ *
+ */
+
+bool InScanArea( Point& pa, Point& pb, Point& pc, Point& pd )
+{
+ double oadb = (pa.x - pb.x) * (pd.y - pb.y) - (pd.x - pb.x) * (pa.y - pb.y);
+
+ if( oadb >= -EPSILON )
+ {
+ return false;
+ }
+
+ double oadc = (pa.x - pc.x) * (pd.y - pc.y) - (pd.x - pc.x) * (pa.y - pc.y);
+
+ if( oadc <= EPSILON )
+ {
+ return false;
+ }
+
+ return true;
+}
+}
+
+#endif
diff --git a/polygon/poly2tri/poly2tri.h b/polygon/poly2tri/poly2tri.h
new file mode 100644
index 0000000..487755e
--- /dev/null
+++ b/polygon/poly2tri/poly2tri.h
@@ -0,0 +1,39 @@
+/*
+ * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
+ * http://code.google.com/p/poly2tri/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef POLY2TRI_H
+#define POLY2TRI_H
+
+#include "common/shapes.h"
+#include "sweep/cdt.h"
+
+#endif
+
diff --git a/polygon/poly2tri/sweep/advancing_front.cc b/polygon/poly2tri/sweep/advancing_front.cc
new file mode 100644
index 0000000..019df4a
--- /dev/null
+++ b/polygon/poly2tri/sweep/advancing_front.cc
@@ -0,0 +1,109 @@
+/*
+ * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
+ * http://code.google.com/p/poly2tri/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "advancing_front.h"
+
+namespace p2t {
+
+AdvancingFront::AdvancingFront(Node& head, Node& tail)
+{
+ head_ = &head;
+ tail_ = &tail;
+ search_node_ = &head;
+}
+
+Node* AdvancingFront::LocateNode(const double& x)
+{
+ Node* node = search_node_;
+
+ if (x < node->value) {
+ while ((node = node->prev) != NULL) {
+ if (x >= node->value) {
+ search_node_ = node;
+ return node;
+ }
+ }
+ } else {
+ while ((node = node->next) != NULL) {
+ if (x < node->value) {
+ search_node_ = node->prev;
+ return node->prev;
+ }
+ }
+ }
+ return NULL;
+}
+
+Node* AdvancingFront::FindSearchNode(const double& x)
+{
+ (void)x; // suppress compiler warnings "unused parameter 'x'"
+ // TODO: implement BST index
+ return search_node_;
+}
+
+Node* AdvancingFront::LocatePoint(const Point* point)
+{
+ const double px = point->x;
+ Node* node = FindSearchNode(px);
+ const double nx = node->point->x;
+
+ if (px == nx) {
+ if (point != node->point) {
+ // We might have two nodes with same x value for a short time
+ if (point == node->prev->point) {
+ node = node->prev;
+ } else if (point == node->next->point) {
+ node = node->next;
+ } else {
+ assert(0);
+ }
+ }
+ } else if (px < nx) {
+ while ((node = node->prev) != NULL) {
+ if (point == node->point) {
+ break;
+ }
+ }
+ } else {
+ while ((node = node->next) != NULL) {
+ if (point == node->point)
+ break;
+ }
+ }
+ if(node) search_node_ = node;
+ return node;
+}
+
+AdvancingFront::~AdvancingFront()
+{
+}
+
+}
+
diff --git a/polygon/poly2tri/sweep/advancing_front.h b/polygon/poly2tri/sweep/advancing_front.h
new file mode 100644
index 0000000..bab73d4
--- /dev/null
+++ b/polygon/poly2tri/sweep/advancing_front.h
@@ -0,0 +1,118 @@
+/*
+ * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
+ * http://code.google.com/p/poly2tri/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ADVANCED_FRONT_H
+#define ADVANCED_FRONT_H
+
+#include "../common/shapes.h"
+
+namespace p2t {
+
+struct Node;
+
+// Advancing front node
+struct Node {
+ Point* point;
+ Triangle* triangle;
+
+ Node* next;
+ Node* prev;
+
+ double value;
+
+ Node(Point& p) : point(&p), triangle(NULL), next(NULL), prev(NULL), value(p.x)
+ {
+ }
+
+ Node(Point& p, Triangle& t) : point(&p), triangle(&t), next(NULL), prev(NULL), value(p.x)
+ {
+ }
+
+};
+
+// Advancing front
+class AdvancingFront {
+public:
+
+AdvancingFront(Node& head, Node& tail);
+// Destructor
+~AdvancingFront();
+
+Node* head();
+void set_head(Node* node);
+Node* tail();
+void set_tail(Node* node);
+Node* search();
+void set_search(Node* node);
+
+/// Locate insertion point along advancing front
+Node* LocateNode(const double& x);
+
+Node* LocatePoint(const Point* point);
+
+private:
+
+Node* head_, *tail_, *search_node_;
+
+Node* FindSearchNode(const double& x);
+};
+
+inline Node* AdvancingFront::head()
+{
+ return head_;
+}
+inline void AdvancingFront::set_head(Node* node)
+{
+ head_ = node;
+}
+
+inline Node* AdvancingFront::tail()
+{
+ return tail_;
+}
+inline void AdvancingFront::set_tail(Node* node)
+{
+ tail_ = node;
+}
+
+inline Node* AdvancingFront::search()
+{
+ return search_node_;
+}
+
+inline void AdvancingFront::set_search(Node* node)
+{
+ search_node_ = node;
+}
+
+}
+
+#endif
diff --git a/polygon/poly2tri/sweep/cdt.cc b/polygon/poly2tri/sweep/cdt.cc
new file mode 100644
index 0000000..d783825
--- /dev/null
+++ b/polygon/poly2tri/sweep/cdt.cc
@@ -0,0 +1,72 @@
+/*
+ * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
+ * http://code.google.com/p/poly2tri/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "cdt.h"
+
+namespace p2t {
+
+CDT::CDT(std::vector<Point*> polyline)
+{
+ sweep_context_ = new SweepContext(polyline);
+ sweep_ = new Sweep;
+}
+
+void CDT::AddHole(std::vector<Point*> polyline)
+{
+ sweep_context_->AddHole(polyline);
+}
+
+void CDT::AddPoint(Point* point) {
+ sweep_context_->AddPoint(point);
+}
+
+void CDT::Triangulate()
+{
+ sweep_->Triangulate(*sweep_context_);
+}
+
+std::vector<p2t::Triangle*> CDT::GetTriangles()
+{
+ return sweep_context_->GetTriangles();
+}
+
+std::list<p2t::Triangle*> CDT::GetMap()
+{
+ return sweep_context_->GetMap();
+}
+
+CDT::~CDT()
+{
+ delete sweep_context_;
+ delete sweep_;
+}
+
+}
+
diff --git a/polygon/poly2tri/sweep/cdt.h b/polygon/poly2tri/sweep/cdt.h
new file mode 100644
index 0000000..3e6f024
--- /dev/null
+++ b/polygon/poly2tri/sweep/cdt.h
@@ -0,0 +1,105 @@
+/*
+ * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
+ * http://code.google.com/p/poly2tri/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef CDT_H
+#define CDT_H
+
+#include "advancing_front.h"
+#include "sweep_context.h"
+#include "sweep.h"
+
+/**
+ *
+ * @author Mason Green <mason.green@gmail.com>
+ *
+ */
+
+namespace p2t {
+
+class CDT
+{
+public:
+
+ /**
+ * Constructor - add polyline with non repeating points
+ *
+ * @param polyline
+ */
+ CDT(std::vector<Point*> polyline);
+
+ /**
+ * Destructor - clean up memory
+ */
+ ~CDT();
+
+ /**
+ * Add a hole
+ *
+ * @param polyline
+ */
+ void AddHole(std::vector<Point*> polyline);
+
+ /**
+ * Add a steiner point
+ *
+ * @param point
+ */
+ void AddPoint(Point* point);
+
+ /**
+ * Triangulate - do this AFTER you've added the polyline, holes, and Steiner points
+ */
+ void Triangulate();
+
+ /**
+ * Get CDT triangles
+ */
+ std::vector<Triangle*> GetTriangles();
+
+ /**
+ * Get triangle map
+ */
+ std::list<Triangle*> GetMap();
+
+ private:
+
+ /**
+ * Internals
+ */
+
+ SweepContext* sweep_context_;
+ Sweep* sweep_;
+
+};
+
+}
+
+#endif
diff --git a/polygon/poly2tri/sweep/sweep.cc b/polygon/poly2tri/sweep/sweep.cc
new file mode 100644
index 0000000..75e7adf
--- /dev/null
+++ b/polygon/poly2tri/sweep/sweep.cc
@@ -0,0 +1,817 @@
+/*
+ * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
+ * http://code.google.com/p/poly2tri/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <stdexcept>
+#include "sweep.h"
+#include "sweep_context.h"
+#include "advancing_front.h"
+#include "../common/utils.h"
+
+namespace p2t {
+
+// Triangulate simple polygon with holes
+void Sweep::Triangulate(SweepContext& tcx)
+{
+ tcx.InitTriangulation();
+ tcx.CreateAdvancingFront(nodes_);
+ // Sweep points; build mesh
+ SweepPoints(tcx);
+ // Clean up
+ FinalizationPolygon(tcx);
+}
+
+void Sweep::SweepPoints(SweepContext& tcx)
+{
+ for (int i = 1; i < tcx.point_count(); i++) {
+ Point& point = *tcx.GetPoint(i);
+ Node* node = &PointEvent(tcx, point);
+ for (unsigned int i = 0; i < point.edge_list.size(); i++) {
+ EdgeEvent(tcx, point.edge_list[i], node);
+ }
+ }
+}
+
+void Sweep::FinalizationPolygon(SweepContext& tcx)
+{
+ // Get an Internal triangle to start with
+ Triangle* t = tcx.front()->head()->next->triangle;
+ Point* p = tcx.front()->head()->next->point;
+ while (!t->GetConstrainedEdgeCW(*p)) {
+ t = t->NeighborCCW(*p);
+ }
+
+ // Collect interior triangles constrained by edges
+ tcx.MeshClean(*t);
+}
+
+Node& Sweep::PointEvent(SweepContext& tcx, Point& point)
+{
+ Node& node = tcx.LocateNode(point);
+ Node& new_node = NewFrontTriangle(tcx, point, node);
+
+ // Only need to check +epsilon since point never have smaller
+ // x value than node due to how we fetch nodes from the front
+ if (point.x <= node.point->x + EPSILON) {
+ Fill(tcx, node);
+ }
+
+ //tcx.AddNode(new_node);
+
+ FillAdvancingFront(tcx, new_node);
+ return new_node;
+}
+
+void Sweep::EdgeEvent(SweepContext& tcx, Edge* edge, Node* node)
+{
+ tcx.edge_event.constrained_edge = edge;
+ tcx.edge_event.right = (edge->p->x > edge->q->x);
+
+ if (IsEdgeSideOfTriangle(*node->triangle, *edge->p, *edge->q)) {
+ return;
+ }
+
+ // For now we will do all needed filling
+ // TODO: integrate with flip process might give some better performance
+ // but for now this avoid the issue with cases that needs both flips and fills
+ FillEdgeEvent(tcx, edge, node);
+ EdgeEvent(tcx, *edge->p, *edge->q, node->triangle, *edge->q);
+}
+
+void Sweep::EdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* triangle, Point& point)
+{
+ if (IsEdgeSideOfTriangle(*triangle, ep, eq)) {
+ return;
+ }
+
+ Point* p1 = triangle->PointCCW(point);
+ Orientation o1 = Orient2d(eq, *p1, ep);
+ if (o1 == COLLINEAR) {
+ if( triangle->Contains(&eq, p1)) {
+ triangle->MarkConstrainedEdge(&eq, p1 );
+ // We are modifying the constraint maybe it would be better to
+ // not change the given constraint and just keep a variable for the new constraint
+ tcx.edge_event.constrained_edge->q = p1;
+ triangle = &triangle->NeighborAcross(point);
+ EdgeEvent( tcx, ep, *p1, triangle, *p1 );
+ } else {
+ std::runtime_error("EdgeEvent - collinear points not supported");
+ assert(0);
+ }
+ return;
+ }
+
+ Point* p2 = triangle->PointCW(point);
+ Orientation o2 = Orient2d(eq, *p2, ep);
+ if (o2 == COLLINEAR) {
+ if( triangle->Contains(&eq, p2)) {
+ triangle->MarkConstrainedEdge(&eq, p2 );
+ // We are modifying the constraint maybe it would be better to
+ // not change the given constraint and just keep a variable for the new constraint
+ tcx.edge_event.constrained_edge->q = p2;
+ triangle = &triangle->NeighborAcross(point);
+ EdgeEvent( tcx, ep, *p2, triangle, *p2 );
+ } else {
+ std::runtime_error("EdgeEvent - collinear points not supported");
+ assert(0);
+ }
+ return;
+ }
+
+ if (o1 == o2) {
+ // Need to decide if we are rotating CW or CCW to get to a triangle
+ // that will cross edge
+ if (o1 == CW) {
+ triangle = triangle->NeighborCCW(point);
+ } else{
+ triangle = triangle->NeighborCW(point);
+ }
+ EdgeEvent(tcx, ep, eq, triangle, point);
+ } else {
+ // This triangle crosses constraint so lets flippin start!
+ FlipEdgeEvent(tcx, ep, eq, triangle, point);
+ }
+}
+
+bool Sweep::IsEdgeSideOfTriangle(Triangle& triangle, Point& ep, Point& eq)
+{
+ int index = triangle.EdgeIndex(&ep, &eq);
+
+ if (index != -1) {
+ triangle.MarkConstrainedEdge(index);
+ Triangle* t = triangle.GetNeighbor(index);
+ if (t) {
+ t->MarkConstrainedEdge(&ep, &eq);
+ }
+ return true;
+ }
+ return false;
+}
+
+Node& Sweep::NewFrontTriangle(SweepContext& tcx, Point& point, Node& node)
+{
+ Triangle* triangle = new Triangle(point, *node.point, *node.next->point);
+
+ triangle->MarkNeighbor(*node.triangle);
+ tcx.AddToMap(triangle);
+
+ Node* new_node = new Node(point);
+ nodes_.push_back(new_node);
+
+ new_node->next = node.next;
+ new_node->prev = &node;
+ node.next->prev = new_node;
+ node.next = new_node;
+
+ if (!Legalize(tcx, *triangle)) {
+ tcx.MapTriangleToNodes(*triangle);
+ }
+
+ return *new_node;
+}
+
+void Sweep::Fill(SweepContext& tcx, Node& node)
+{
+ Triangle* triangle = new Triangle(*node.prev->point, *node.point, *node.next->point);
+
+ // TODO: should copy the constrained_edge value from neighbor triangles
+ // for now constrained_edge values are copied during the legalize
+ triangle->MarkNeighbor(*node.prev->triangle);
+ triangle->MarkNeighbor(*node.triangle);
+
+ tcx.AddToMap(triangle);
+
+ // Update the advancing front
+ node.prev->next = node.next;
+ node.next->prev = node.prev;
+
+ // If it was legalized the triangle has already been mapped
+ if (!Legalize(tcx, *triangle)) {
+ tcx.MapTriangleToNodes(*triangle);
+ }
+
+}
+
+void Sweep::FillAdvancingFront(SweepContext& tcx, Node& n)
+{
+
+ // Fill right holes
+ Node* node = n.next;
+
+ while (node->next) {
+ // if HoleAngle exceeds 90 degrees then break.
+ if (LargeHole_DontFill(node)) break;
+ Fill(tcx, *node);
+ node = node->next;
+ }
+
+ // Fill left holes
+ node = n.prev;
+
+ while (node->prev) {
+ // if HoleAngle exceeds 90 degrees then break.
+ if (LargeHole_DontFill(node)) break;
+ Fill(tcx, *node);
+ node = node->prev;
+ }
+
+ // Fill right basins
+ if (n.next && n.next->next) {
+ double angle = BasinAngle(n);
+ if (angle < PI_3div4) {
+ FillBasin(tcx, n);
+ }
+ }
+}
+
+// True if HoleAngle exceeds 90 degrees.
+bool Sweep::LargeHole_DontFill(Node* node) {
+
+ Node* nextNode = node->next;
+ Node* prevNode = node->prev;
+ if (!AngleExceeds90Degrees(node->point, nextNode->point, prevNode->point))
+ return false;
+
+ // Check additional points on front.
+ Node* next2Node = nextNode->next;
+ // "..Plus.." because only want angles on same side as point being added.
+ if ((next2Node != NULL) && !AngleExceedsPlus90DegreesOrIsNegative(node->point, next2Node->point, prevNode->point))
+ return false;
+
+ Node* prev2Node = prevNode->prev;
+ // "..Plus.." because only want angles on same side as point being added.
+ if ((prev2Node != NULL) && !AngleExceedsPlus90DegreesOrIsNegative(node->point, nextNode->point, prev2Node->point))
+ return false;
+
+ return true;
+}
+
+bool Sweep::AngleExceeds90Degrees(Point* origin, Point* pa, Point* pb) {
+ double angle = Angle(*origin, *pa, *pb);
+ bool exceeds90Degrees = ((angle > PI_div2) || (angle < -PI_div2));
+ return exceeds90Degrees;
+}
+
+bool Sweep::AngleExceedsPlus90DegreesOrIsNegative(Point* origin, Point* pa, Point* pb) {
+ double angle = Angle(*origin, *pa, *pb);
+ bool exceedsPlus90DegreesOrIsNegative = (angle > PI_div2) || (angle < 0);
+ return exceedsPlus90DegreesOrIsNegative;
+}
+
+double Sweep::Angle(Point& origin, Point& pa, Point& pb) {
+ /* Complex plane
+ * ab = cosA +i*sinA
+ * ab = (ax + ay*i)(bx + by*i) = (ax*bx + ay*by) + i(ax*by-ay*bx)
+ * atan2(y,x) computes the principal value of the argument function
+ * applied to the complex number x+iy
+ * Where x = ax*bx + ay*by
+ * y = ax*by - ay*bx
+ */
+ double px = origin.x;
+ double py = origin.y;
+ double ax = pa.x- px;
+ double ay = pa.y - py;
+ double bx = pb.x - px;
+ double by = pb.y - py;
+ double x = ax * by - ay * bx;
+ double y = ax * bx + ay * by;
+ double angle = atan2(x, y);
+ return angle;
+}
+
+double Sweep::BasinAngle(Node& node)
+{
+ double ax = node.point->x - node.next->next->point->x;
+ double ay = node.point->y - node.next->next->point->y;
+ return atan2(ay, ax);
+}
+
+double Sweep::HoleAngle(Node& node)
+{
+ /* Complex plane
+ * ab = cosA +i*sinA
+ * ab = (ax + ay*i)(bx + by*i) = (ax*bx + ay*by) + i(ax*by-ay*bx)
+ * atan2(y,x) computes the principal value of the argument function
+ * applied to the complex number x+iy
+ * Where x = ax*bx + ay*by
+ * y = ax*by - ay*bx
+ */
+ double ax = node.next->point->x - node.point->x;
+ double ay = node.next->point->y - node.point->y;
+ double bx = node.prev->point->x - node.point->x;
+ double by = node.prev->point->y - node.point->y;
+ return atan2(ax * by - ay * bx, ax * bx + ay * by);
+}
+
+bool Sweep::Legalize(SweepContext& tcx, Triangle& t)
+{
+ // To legalize a triangle we start by finding if any of the three edges
+ // violate the Delaunay condition
+ for (int i = 0; i < 3; i++) {
+ if (t.delaunay_edge[i])
+ continue;
+
+ Triangle* ot = t.GetNeighbor(i);
+
+ if (ot) {
+ Point* p = t.GetPoint(i);
+ Point* op = ot->OppositePoint(t, *p);
+ int oi = ot->Index(op);
+
+ // If this is a Constrained Edge or a Delaunay Edge(only during recursive legalization)
+ // then we should not try to legalize
+ if (ot->constrained_edge[oi] || ot->delaunay_edge[oi]) {
+ t.constrained_edge[i] = ot->constrained_edge[oi];
+ continue;
+ }
+
+ bool inside = Incircle(*p, *t.PointCCW(*p), *t.PointCW(*p), *op);
+
+ if (inside) {
+ // Lets mark this shared edge as Delaunay
+ t.delaunay_edge[i] = true;
+ ot->delaunay_edge[oi] = true;
+
+ // Lets rotate shared edge one vertex CW to legalize it
+ RotateTrianglePair(t, *p, *ot, *op);
+
+ // We now got one valid Delaunay Edge shared by two triangles
+ // This gives us 4 new edges to check for Delaunay
+
+ // Make sure that triangle to node mapping is done only one time for a specific triangle
+ bool not_legalized = !Legalize(tcx, t);
+ if (not_legalized) {
+ tcx.MapTriangleToNodes(t);
+ }
+
+ not_legalized = !Legalize(tcx, *ot);
+ if (not_legalized)
+ tcx.MapTriangleToNodes(*ot);
+
+ // Reset the Delaunay edges, since they only are valid Delaunay edges
+ // until we add a new triangle or point.
+ // XXX: need to think about this. Can these edges be tried after we
+ // return to previous recursive level?
+ t.delaunay_edge[i] = false;
+ ot->delaunay_edge[oi] = false;
+
+ // If triangle have been legalized no need to check the other edges since
+ // the recursive legalization will handles those so we can end here.
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool Sweep::Incircle(Point& pa, Point& pb, Point& pc, Point& pd)
+{
+ double adx = pa.x - pd.x;
+ double ady = pa.y - pd.y;
+ double bdx = pb.x - pd.x;
+ double bdy = pb.y - pd.y;
+
+ double adxbdy = adx * bdy;
+ double bdxady = bdx * ady;
+ double oabd = adxbdy - bdxady;
+
+ if (oabd <= 0)
+ return false;
+
+ double cdx = pc.x - pd.x;
+ double cdy = pc.y - pd.y;
+
+ double cdxady = cdx * ady;
+ double adxcdy = adx * cdy;
+ double ocad = cdxady - adxcdy;
+
+ if (ocad <= 0)
+ return false;
+
+ double bdxcdy = bdx * cdy;
+ double cdxbdy = cdx * bdy;
+
+ double alift = adx * adx + ady * ady;
+ double blift = bdx * bdx + bdy * bdy;
+ double clift = cdx * cdx + cdy * cdy;
+
+ double det = alift * (bdxcdy - cdxbdy) + blift * ocad + clift * oabd;
+
+ return det > 0;
+}
+
+void Sweep::RotateTrianglePair(Triangle& t, Point& p, Triangle& ot, Point& op)
+{
+ Triangle* n1, *n2, *n3, *n4;
+ n1 = t.NeighborCCW(p);
+ n2 = t.NeighborCW(p);
+ n3 = ot.NeighborCCW(op);
+ n4 = ot.NeighborCW(op);
+
+ bool ce1, ce2, ce3, ce4;
+ ce1 = t.GetConstrainedEdgeCCW(p);
+ ce2 = t.GetConstrainedEdgeCW(p);
+ ce3 = ot.GetConstrainedEdgeCCW(op);
+ ce4 = ot.GetConstrainedEdgeCW(op);
+
+ bool de1, de2, de3, de4;
+ de1 = t.GetDelunayEdgeCCW(p);
+ de2 = t.GetDelunayEdgeCW(p);
+ de3 = ot.GetDelunayEdgeCCW(op);
+ de4 = ot.GetDelunayEdgeCW(op);
+
+ t.Legalize(p, op);
+ ot.Legalize(op, p);
+
+ // Remap delaunay_edge
+ ot.SetDelunayEdgeCCW(p, de1);
+ t.SetDelunayEdgeCW(p, de2);
+ t.SetDelunayEdgeCCW(op, de3);
+ ot.SetDelunayEdgeCW(op, de4);
+
+ // Remap constrained_edge
+ ot.SetConstrainedEdgeCCW(p, ce1);
+ t.SetConstrainedEdgeCW(p, ce2);
+ t.SetConstrainedEdgeCCW(op, ce3);
+ ot.SetConstrainedEdgeCW(op, ce4);
+
+ // Remap neighbors
+ // XXX: might optimize the markNeighbor by keeping track of
+ // what side should be assigned to what neighbor after the
+ // rotation. Now mark neighbor does lots of testing to find
+ // the right side.
+ t.ClearNeighbors();
+ ot.ClearNeighbors();
+ if (n1) ot.MarkNeighbor(*n1);
+ if (n2) t.MarkNeighbor(*n2);
+ if (n3) t.MarkNeighbor(*n3);
+ if (n4) ot.MarkNeighbor(*n4);
+ t.MarkNeighbor(ot);
+}
+
+void Sweep::FillBasin(SweepContext& tcx, Node& node)
+{
+ if (Orient2d(*node.point, *node.next->point, *node.next->next->point) == CCW) {
+ tcx.basin.left_node = node.next->next;
+ } else {
+ tcx.basin.left_node = node.next;
+ }
+
+ // Find the bottom and right node
+ tcx.basin.bottom_node = tcx.basin.left_node;
+ while (tcx.basin.bottom_node->next
+ && tcx.basin.bottom_node->point->y >= tcx.basin.bottom_node->next->point->y) {
+ tcx.basin.bottom_node = tcx.basin.bottom_node->next;
+ }
+ if (tcx.basin.bottom_node == tcx.basin.left_node) {
+ // No valid basin
+ return;
+ }
+
+ tcx.basin.right_node = tcx.basin.bottom_node;
+ while (tcx.basin.right_node->next
+ && tcx.basin.right_node->point->y < tcx.basin.right_node->next->point->y) {
+ tcx.basin.right_node = tcx.basin.right_node->next;
+ }
+ if (tcx.basin.right_node == tcx.basin.bottom_node) {
+ // No valid basins
+ return;
+ }
+
+ tcx.basin.width = tcx.basin.right_node->point->x - tcx.basin.left_node->point->x;
+ tcx.basin.left_highest = tcx.basin.left_node->point->y > tcx.basin.right_node->point->y;
+
+ FillBasinReq(tcx, tcx.basin.bottom_node);
+}
+
+void Sweep::FillBasinReq(SweepContext& tcx, Node* node)
+{
+ // if shallow stop filling
+ if (IsShallow(tcx, *node)) {
+ return;
+ }
+
+ Fill(tcx, *node);
+
+ if (node->prev == tcx.basin.left_node && node->next == tcx.basin.right_node) {
+ return;
+ } else if (node->prev == tcx.basin.left_node) {
+ Orientation o = Orient2d(*node->point, *node->next->point, *node->next->next->point);
+ if (o == CW) {
+ return;
+ }
+ node = node->next;
+ } else if (node->next == tcx.basin.right_node) {
+ Orientation o = Orient2d(*node->point, *node->prev->point, *node->prev->prev->point);
+ if (o == CCW) {
+ return;
+ }
+ node = node->prev;
+ } else {
+ // Continue with the neighbor node with lowest Y value
+ if (node->prev->point->y < node->next->point->y) {
+ node = node->prev;
+ } else {
+ node = node->next;
+ }
+ }
+
+ FillBasinReq(tcx, node);
+}
+
+bool Sweep::IsShallow(SweepContext& tcx, Node& node)
+{
+ double height;
+
+ if (tcx.basin.left_highest) {
+ height = tcx.basin.left_node->point->y - node.point->y;
+ } else {
+ height = tcx.basin.right_node->point->y - node.point->y;
+ }
+
+ // if shallow stop filling
+ if (tcx.basin.width > height) {
+ return true;
+ }
+ return false;
+}
+
+void Sweep::FillEdgeEvent(SweepContext& tcx, Edge* edge, Node* node)
+{
+ if (tcx.edge_event.right) {
+ FillRightAboveEdgeEvent(tcx, edge, node);
+ } else {
+ FillLeftAboveEdgeEvent(tcx, edge, node);
+ }
+}
+
+void Sweep::FillRightAboveEdgeEvent(SweepContext& tcx, Edge* edge, Node* node)
+{
+ while (node->next->point->x < edge->p->x) {
+ // Check if next node is below the edge
+ if (Orient2d(*edge->q, *node->next->point, *edge->p) == CCW) {
+ FillRightBelowEdgeEvent(tcx, edge, *node);
+ } else {
+ node = node->next;
+ }
+ }
+}
+
+void Sweep::FillRightBelowEdgeEvent(SweepContext& tcx, Edge* edge, Node& node)
+{
+ if (node.point->x < edge->p->x) {
+ if (Orient2d(*node.point, *node.next->point, *node.next->next->point) == CCW) {
+ // Concave
+ FillRightConcaveEdgeEvent(tcx, edge, node);
+ } else{
+ // Convex
+ FillRightConvexEdgeEvent(tcx, edge, node);
+ // Retry this one
+ FillRightBelowEdgeEvent(tcx, edge, node);
+ }
+ }
+}
+
+void Sweep::FillRightConcaveEdgeEvent(SweepContext& tcx, Edge* edge, Node& node)
+{
+ Fill(tcx, *node.next);
+ if (node.next->point != edge->p) {
+ // Next above or below edge?
+ if (Orient2d(*edge->q, *node.next->point, *edge->p) == CCW) {
+ // Below
+ if (Orient2d(*node.point, *node.next->point, *node.next->next->point) == CCW) {
+ // Next is concave
+ FillRightConcaveEdgeEvent(tcx, edge, node);
+ } else {
+ // Next is convex
+ }
+ }
+ }
+
+}
+
+void Sweep::FillRightConvexEdgeEvent(SweepContext& tcx, Edge* edge, Node& node)
+{
+ // Next concave or convex?
+ if (Orient2d(*node.next->point, *node.next->next->point, *node.next->next->next->point) == CCW) {
+ // Concave
+ FillRightConcaveEdgeEvent(tcx, edge, *node.next);
+ } else{
+ // Convex
+ // Next above or below edge?
+ if (Orient2d(*edge->q, *node.next->next->point, *edge->p) == CCW) {
+ // Below
+ FillRightConvexEdgeEvent(tcx, edge, *node.next);
+ } else{
+ // Above
+ }
+ }
+}
+
+void Sweep::FillLeftAboveEdgeEvent(SweepContext& tcx, Edge* edge, Node* node)
+{
+ while (node->prev->point->x > edge->p->x) {
+ // Check if next node is below the edge
+ if (Orient2d(*edge->q, *node->prev->point, *edge->p) == CW) {
+ FillLeftBelowEdgeEvent(tcx, edge, *node);
+ } else {
+ node = node->prev;
+ }
+ }
+}
+
+void Sweep::FillLeftBelowEdgeEvent(SweepContext& tcx, Edge* edge, Node& node)
+{
+ if (node.point->x > edge->p->x) {
+ if (Orient2d(*node.point, *node.prev->point, *node.prev->prev->point) == CW) {
+ // Concave
+ FillLeftConcaveEdgeEvent(tcx, edge, node);
+ } else {
+ // Convex
+ FillLeftConvexEdgeEvent(tcx, edge, node);
+ // Retry this one
+ FillLeftBelowEdgeEvent(tcx, edge, node);
+ }
+ }
+}
+
+void Sweep::FillLeftConvexEdgeEvent(SweepContext& tcx, Edge* edge, Node& node)
+{
+ // Next concave or convex?
+ if (Orient2d(*node.prev->point, *node.prev->prev->point, *node.prev->prev->prev->point) == CW) {
+ // Concave
+ FillLeftConcaveEdgeEvent(tcx, edge, *node.prev);
+ } else{
+ // Convex
+ // Next above or below edge?
+ if (Orient2d(*edge->q, *node.prev->prev->point, *edge->p) == CW) {
+ // Below
+ FillLeftConvexEdgeEvent(tcx, edge, *node.prev);
+ } else{
+ // Above
+ }
+ }
+}
+
+void Sweep::FillLeftConcaveEdgeEvent(SweepContext& tcx, Edge* edge, Node& node)
+{
+ Fill(tcx, *node.prev);
+ if (node.prev->point != edge->p) {
+ // Next above or below edge?
+ if (Orient2d(*edge->q, *node.prev->point, *edge->p) == CW) {
+ // Below
+ if (Orient2d(*node.point, *node.prev->point, *node.prev->prev->point) == CW) {
+ // Next is concave
+ FillLeftConcaveEdgeEvent(tcx, edge, node);
+ } else{
+ // Next is convex
+ }
+ }
+ }
+
+}
+
+void Sweep::FlipEdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* t, Point& p)
+{
+ Triangle& ot = t->NeighborAcross(p);
+ Point& op = *ot.OppositePoint(*t, p);
+
+ if (&ot == NULL) {
+ // If we want to integrate the fillEdgeEvent do it here
+ // With current implementation we should never get here
+ //throw new RuntimeException( "[BUG:FIXME] FLIP failed due to missing triangle");
+ assert(0);
+ }
+
+ if (InScanArea(p, *t->PointCCW(p), *t->PointCW(p), op)) {
+ // Lets rotate shared edge one vertex CW
+ RotateTrianglePair(*t, p, ot, op);
+ tcx.MapTriangleToNodes(*t);
+ tcx.MapTriangleToNodes(ot);
+
+ if (p == eq && op == ep) {
+ if (eq == *tcx.edge_event.constrained_edge->q && ep == *tcx.edge_event.constrained_edge->p) {
+ t->MarkConstrainedEdge(&ep, &eq);
+ ot.MarkConstrainedEdge(&ep, &eq);
+ Legalize(tcx, *t);
+ Legalize(tcx, ot);
+ } else {
+ // XXX: I think one of the triangles should be legalized here?
+ }
+ } else {
+ Orientation o = Orient2d(eq, op, ep);
+ t = &NextFlipTriangle(tcx, (int)o, *t, ot, p, op);
+ FlipEdgeEvent(tcx, ep, eq, t, p);
+ }
+ } else {
+ Point& newP = NextFlipPoint(ep, eq, ot, op);
+ FlipScanEdgeEvent(tcx, ep, eq, *t, ot, newP);
+ EdgeEvent(tcx, ep, eq, t, p);
+ }
+}
+
+Triangle& Sweep::NextFlipTriangle(SweepContext& tcx, int o, Triangle& t, Triangle& ot, Point& p, Point& op)
+{
+ if (o == CCW) {
+ // ot is not crossing edge after flip
+ int edge_index = ot.EdgeIndex(&p, &op);
+ ot.delaunay_edge[edge_index] = true;
+ Legalize(tcx, ot);
+ ot.ClearDelunayEdges();
+ return t;
+ }
+
+ // t is not crossing edge after flip
+ int edge_index = t.EdgeIndex(&p, &op);
+
+ t.delaunay_edge[edge_index] = true;
+ Legalize(tcx, t);
+ t.ClearDelunayEdges();
+ return ot;
+}
+
+Point& Sweep::NextFlipPoint(Point& ep, Point& eq, Triangle& ot, Point& op)
+{
+ Orientation o2d = Orient2d(eq, op, ep);
+ if (o2d == CW) {
+ // Right
+ return *ot.PointCCW(op);
+ } else if (o2d == CCW) {
+ // Left
+ return *ot.PointCW(op);
+ }
+
+ //throw new RuntimeException("[Unsupported] Opposing point on constrained edge");
+ assert(0);
+
+ // Never executed, due tu assert( 0 ). Just to avoid compil warning
+ return ep;
+}
+
+void Sweep::FlipScanEdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle& flip_triangle,
+ Triangle& t, Point& p)
+{
+ Triangle& ot = t.NeighborAcross(p);
+ Point& op = *ot.OppositePoint(t, p);
+
+ if (&t.NeighborAcross(p) == NULL) {
+ // If we want to integrate the fillEdgeEvent do it here
+ // With current implementation we should never get here
+ //throw new RuntimeException( "[BUG:FIXME] FLIP failed due to missing triangle");
+ assert(0);
+ }
+
+ if (InScanArea(eq, *flip_triangle.PointCCW(eq), *flip_triangle.PointCW(eq), op)) {
+ // flip with new edge op->eq
+ FlipEdgeEvent(tcx, eq, op, &ot, op);
+ // TODO: Actually I just figured out that it should be possible to
+ // improve this by getting the next ot and op before the the above
+ // flip and continue the flipScanEdgeEvent here
+ // set new ot and op here and loop back to inScanArea test
+ // also need to set a new flip_triangle first
+ // Turns out at first glance that this is somewhat complicated
+ // so it will have to wait.
+ } else{
+ Point& newP = NextFlipPoint(ep, eq, ot, op);
+ FlipScanEdgeEvent(tcx, ep, eq, flip_triangle, ot, newP);
+ }
+}
+
+Sweep::~Sweep() {
+
+ // Clean up memory
+ for( unsigned i = 0; i < nodes_.size(); i++ )
+ {
+ delete nodes_[i];
+ }
+
+}
+
+}
+
diff --git a/polygon/poly2tri/sweep/sweep.h b/polygon/poly2tri/sweep/sweep.h
new file mode 100644
index 0000000..07822d1
--- /dev/null
+++ b/polygon/poly2tri/sweep/sweep.h
@@ -0,0 +1,284 @@
+/*
+ * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
+ * http://code.google.com/p/poly2tri/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * Sweep-line, Constrained Delauney Triangulation (CDT) See: Domiter, V. and
+ * Zalik, B.(2008)'Sweep-line algorithm for constrained Delaunay triangulation',
+ * International Journal of Geographical Information Science
+ *
+ * "FlipScan" Constrained Edge Algorithm invented by Thomas Ĺhlén, thahlen@gmail.com
+ */
+
+#ifndef SWEEP_H
+#define SWEEP_H
+
+#include <vector>
+
+namespace p2t {
+
+class SweepContext;
+struct Node;
+struct Point;
+struct Edge;
+class Triangle;
+
+class Sweep
+{
+public:
+
+ /**
+ * Triangulate
+ *
+ * @param tcx
+ */
+ void Triangulate(SweepContext& tcx);
+
+ /**
+ * Destructor - clean up memory
+ */
+ ~Sweep();
+
+private:
+
+ /**
+ * Start sweeping the Y-sorted point set from bottom to top
+ *
+ * @param tcx
+ */
+ void SweepPoints(SweepContext& tcx);
+
+ /**
+ * Find closes node to the left of the new point and
+ * create a new triangle. If needed new holes and basins
+ * will be filled to.
+ *
+ * @param tcx
+ * @param point
+ * @return
+ */
+ Node& PointEvent(SweepContext& tcx, Point& point);
+
+ /**
+ *
+ *
+ * @param tcx
+ * @param edge
+ * @param node
+ */
+ void EdgeEvent(SweepContext& tcx, Edge* edge, Node* node);
+
+ void EdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* triangle, Point& point);
+
+ /**
+ * Creates a new front triangle and legalize it
+ *
+ * @param tcx
+ * @param point
+ * @param node
+ * @return
+ */
+ Node& NewFrontTriangle(SweepContext& tcx, Point& point, Node& node);
+
+ /**
+ * Adds a triangle to the advancing front to fill a hole.
+ * @param tcx
+ * @param node - middle node, that is the bottom of the hole
+ */
+ void Fill(SweepContext& tcx, Node& node);
+
+ /**
+ * Returns true if triangle was legalized
+ */
+ bool Legalize(SweepContext& tcx, Triangle& t);
+
+ /**
+ * <b>Requirement</b>:<br>
+ * 1. a,b and c form a triangle.<br>
+ * 2. a and d is know to be on opposite side of bc<br>
+ * <pre>
+ * a
+ * +
+ * / \
+ * / \
+ * b/ \c
+ * +-------+
+ * / d \
+ * / \
+ * </pre>
+ * <b>Fact</b>: d has to be in area B to have a chance to be inside the circle formed by
+ * a,b and c<br>
+ * d is outside B if orient2d(a,b,d) or orient2d(c,a,d) is CW<br>
+ * This preknowledge gives us a way to optimize the incircle test
+ * @param pa - triangle point, opposite d
+ * @param pb - triangle point
+ * @param pc - triangle point
+ * @param pd - point opposite a
+ * @return true if d is inside circle, false if on circle edge
+ */
+ bool Incircle(Point& pa, Point& pb, Point& pc, Point& pd);
+
+ /**
+ * Rotates a triangle pair one vertex CW
+ *<pre>
+ * n2 n2
+ * P +-----+ P +-----+
+ * | t /| |\ t |
+ * | / | | \ |
+ * n1| / |n3 n1| \ |n3
+ * | / | after CW | \ |
+ * |/ oT | | oT \|
+ * +-----+ oP +-----+
+ * n4 n4
+ * </pre>
+ */
+ void RotateTrianglePair(Triangle& t, Point& p, Triangle& ot, Point& op);
+
+ /**
+ * Fills holes in the Advancing Front
+ *
+ *
+ * @param tcx
+ * @param n
+ */
+ void FillAdvancingFront(SweepContext& tcx, Node& n);
+
+ // Decision-making about when to Fill hole.
+ // Contributed by ToolmakerSteve2
+ bool LargeHole_DontFill(Node* node);
+ bool AngleExceeds90Degrees(Point* origin, Point* pa, Point* pb);
+ bool AngleExceedsPlus90DegreesOrIsNegative(Point* origin, Point* pa, Point* pb);
+ double Angle(Point& origin, Point& pa, Point& pb);
+
+ /**
+ *
+ * @param node - middle node
+ * @return the angle between 3 front nodes
+ */
+ double HoleAngle(Node& node);
+
+ /**
+ * The basin angle is decided against the horizontal line [1,0]
+ */
+ double BasinAngle(Node& node);
+
+ /**
+ * Fills a basin that has formed on the Advancing Front to the right
+ * of given node.<br>
+ * First we decide a left,bottom and right node that forms the
+ * boundaries of the basin. Then we do a reqursive fill.
+ *
+ * @param tcx
+ * @param node - starting node, this or next node will be left node
+ */
+ void FillBasin(SweepContext& tcx, Node& node);
+
+ /**
+ * Recursive algorithm to fill a Basin with triangles
+ *
+ * @param tcx
+ * @param node - bottom_node
+ */
+ void FillBasinReq(SweepContext& tcx, Node* node);
+
+ bool IsShallow(SweepContext& tcx, Node& node);
+
+ bool IsEdgeSideOfTriangle(Triangle& triangle, Point& ep, Point& eq);
+
+ void FillEdgeEvent(SweepContext& tcx, Edge* edge, Node* node);
+
+ void FillRightAboveEdgeEvent(SweepContext& tcx, Edge* edge, Node* node);
+
+ void FillRightBelowEdgeEvent(SweepContext& tcx, Edge* edge, Node& node);
+
+ void FillRightConcaveEdgeEvent(SweepContext& tcx, Edge* edge, Node& node);
+
+ void FillRightConvexEdgeEvent(SweepContext& tcx, Edge* edge, Node& node);
+
+ void FillLeftAboveEdgeEvent(SweepContext& tcx, Edge* edge, Node* node);
+
+ void FillLeftBelowEdgeEvent(SweepContext& tcx, Edge* edge, Node& node);
+
+ void FillLeftConcaveEdgeEvent(SweepContext& tcx, Edge* edge, Node& node);
+
+ void FillLeftConvexEdgeEvent(SweepContext& tcx, Edge* edge, Node& node);
+
+ void FlipEdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* t, Point& p);
+
+ /**
+ * After a flip we have two triangles and know that only one will still be
+ * intersecting the edge. So decide which to contiune with and legalize the other
+ *
+ * @param tcx
+ * @param o - should be the result of an orient2d( eq, op, ep )
+ * @param t - triangle 1
+ * @param ot - triangle 2
+ * @param p - a point shared by both triangles
+ * @param op - another point shared by both triangles
+ * @return returns the triangle still intersecting the edge
+ */
+ Triangle& NextFlipTriangle(SweepContext& tcx, int o, Triangle& t, Triangle& ot, Point& p, Point& op);
+
+ /**
+ * When we need to traverse from one triangle to the next we need
+ * the point in current triangle that is the opposite point to the next
+ * triangle.
+ *
+ * @param ep
+ * @param eq
+ * @param ot
+ * @param op
+ * @return
+ */
+ Point& NextFlipPoint(Point& ep, Point& eq, Triangle& ot, Point& op);
+
+ /**
+ * Scan part of the FlipScan algorithm<br>
+ * When a triangle pair isn't flippable we will scan for the next
+ * point that is inside the flip triangle scan area. When found
+ * we generate a new flipEdgeEvent
+ *
+ * @param tcx
+ * @param ep - last point on the edge we are traversing
+ * @param eq - first point on the edge we are traversing
+ * @param flipTriangle - the current triangle sharing the point eq with edge
+ * @param t
+ * @param p
+ */
+ void FlipScanEdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle& flip_triangle, Triangle& t, Point& p);
+
+ void FinalizationPolygon(SweepContext& tcx);
+
+ std::vector<Node*> nodes_;
+
+};
+
+}
+
+#endif
diff --git a/polygon/poly2tri/sweep/sweep_context.cc b/polygon/poly2tri/sweep/sweep_context.cc
new file mode 100644
index 0000000..6c0b044
--- /dev/null
+++ b/polygon/poly2tri/sweep/sweep_context.cc
@@ -0,0 +1,216 @@
+/*
+ * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
+ * http://code.google.com/p/poly2tri/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "sweep_context.h"
+#include <algorithm>
+#include "advancing_front.h"
+
+namespace p2t {
+
+SweepContext::SweepContext(std::vector<Point*> polyline) :
+ front_(0),
+ head_(0),
+ tail_(0),
+ af_head_(0),
+ af_middle_(0),
+ af_tail_(0)
+{
+ basin = Basin();
+ edge_event = EdgeEvent();
+
+ points_ = polyline;
+
+ InitEdges(points_);
+}
+
+void SweepContext::AddHole(std::vector<Point*> polyline)
+{
+ InitEdges(polyline);
+ for(unsigned int i = 0; i < polyline.size(); i++) {
+ points_.push_back(polyline[i]);
+ }
+}
+
+void SweepContext::AddPoint(Point* point) {
+ points_.push_back(point);
+}
+
+std::vector<Triangle*> SweepContext::GetTriangles()
+{
+ return triangles_;
+}
+
+std::list<Triangle*> SweepContext::GetMap()
+{
+ return map_;
+}
+
+void SweepContext::InitTriangulation()
+{
+ double xmax(points_[0]->x), xmin(points_[0]->x);
+ double ymax(points_[0]->y), ymin(points_[0]->y);
+
+ // Calculate bounds.
+ for (unsigned int i = 0; i < points_.size(); i++) {
+ Point& p = *points_[i];
+ if (p.x > xmax)
+ xmax = p.x;
+ if (p.x < xmin)
+ xmin = p.x;
+ if (p.y > ymax)
+ ymax = p.y;
+ if (p.y < ymin)
+ ymin = p.y;
+ }
+
+ double dx = kAlpha * (xmax - xmin);
+ double dy = kAlpha * (ymax - ymin);
+ head_ = new Point(xmax + dx, ymin - dy);
+ tail_ = new Point(xmin - dx, ymin - dy);
+
+ // Sort points along y-axis
+ std::sort(points_.begin(), points_.end(), cmp);
+
+}
+
+void SweepContext::InitEdges(std::vector<Point*> polyline)
+{
+ int num_points = polyline.size();
+ for (int i = 0; i < num_points; i++) {
+ int j = i < num_points - 1 ? i + 1 : 0;
+ edge_list.push_back(new Edge(*polyline[i], *polyline[j]));
+ }
+}
+
+Point* SweepContext::GetPoint(const int& index)
+{
+ return points_[index];
+}
+
+void SweepContext::AddToMap(Triangle* triangle)
+{
+ map_.push_back(triangle);
+}
+
+Node& SweepContext::LocateNode(Point& point)
+{
+ // TODO implement search tree
+ return *front_->LocateNode(point.x);
+}
+
+void SweepContext::CreateAdvancingFront(std::vector<Node*> nodes)
+{
+
+ (void) nodes;
+ // Initial triangle
+ Triangle* triangle = new Triangle(*points_[0], *tail_, *head_);
+
+ map_.push_back(triangle);
+
+ af_head_ = new Node(*triangle->GetPoint(1), *triangle);
+ af_middle_ = new Node(*triangle->GetPoint(0), *triangle);
+ af_tail_ = new Node(*triangle->GetPoint(2));
+ front_ = new AdvancingFront(*af_head_, *af_tail_);
+
+ // TODO: More intuitive if head is middles next and not previous?
+ // so swap head and tail
+ af_head_->next = af_middle_;
+ af_middle_->next = af_tail_;
+ af_middle_->prev = af_head_;
+ af_tail_->prev = af_middle_;
+}
+
+void SweepContext::RemoveNode(Node* node)
+{
+ delete node;
+}
+
+void SweepContext::MapTriangleToNodes(Triangle& t)
+{
+ for (int i = 0; i < 3; i++) {
+ if (!t.GetNeighbor(i)) {
+ Node* n = front_->LocatePoint(t.PointCW(*t.GetPoint(i)));
+ if (n)
+ n->triangle = &t;
+ }
+ }
+}
+
+void SweepContext::RemoveFromMap(Triangle* triangle)
+{
+ map_.remove(triangle);
+}
+
+void SweepContext::MeshClean(Triangle& triangle)
+{
+ std::vector<Triangle *> triangles;
+ triangles.push_back(&triangle);
+
+ while(!triangles.empty()){
+ Triangle *t = triangles.back();
+ triangles.pop_back();
+
+ if (t != NULL && !t->IsInterior()) {
+ t->IsInterior(true);
+ triangles_.push_back(t);
+ for (int i = 0; i < 3; i++) {
+ if (!t->constrained_edge[i])
+ triangles.push_back(t->GetNeighbor(i));
+ }
+ }
+ }
+}
+
+SweepContext::~SweepContext()
+{
+
+ // Clean up memory
+
+ delete head_;
+ delete tail_;
+ delete front_;
+ delete af_head_;
+ delete af_middle_;
+ delete af_tail_;
+
+ typedef std::list<Triangle*> type_list;
+
+ for(type_list::iterator iter = map_.begin(); iter != map_.end(); ++iter) {
+ Triangle* ptr = *iter;
+ delete ptr;
+ }
+
+ for(unsigned int i = 0; i < edge_list.size(); i++) {
+ delete edge_list[i];
+ }
+
+}
+
+}
diff --git a/polygon/poly2tri/sweep/sweep_context.h b/polygon/poly2tri/sweep/sweep_context.h
new file mode 100644
index 0000000..1010c0e
--- /dev/null
+++ b/polygon/poly2tri/sweep/sweep_context.h
@@ -0,0 +1,186 @@
+/*
+ * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
+ * http://code.google.com/p/poly2tri/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef SWEEP_CONTEXT_H
+#define SWEEP_CONTEXT_H
+
+#include <list>
+#include <vector>
+#include <cstddef>
+
+namespace p2t {
+
+// Inital triangle factor, seed triangle will extend 30% of
+// PointSet width to both left and right.
+const double kAlpha = 0.3;
+
+struct Point;
+class Triangle;
+struct Node;
+struct Edge;
+class AdvancingFront;
+
+class SweepContext {
+public:
+
+/// Constructor
+SweepContext(std::vector<Point*> polyline);
+/// Destructor
+~SweepContext();
+
+void set_head(Point* p1);
+
+Point* head();
+
+void set_tail(Point* p1);
+
+Point* tail();
+
+int point_count();
+
+Node& LocateNode(Point& point);
+
+void RemoveNode(Node* node);
+
+void CreateAdvancingFront(std::vector<Node*> nodes);
+
+/// Try to map a node to all sides of this triangle that don't have a neighbor
+void MapTriangleToNodes(Triangle& t);
+
+void AddToMap(Triangle* triangle);
+
+Point* GetPoint(const int& index);
+
+Point* GetPoints();
+
+void RemoveFromMap(Triangle* triangle);
+
+void AddHole(std::vector<Point*> polyline);
+
+void AddPoint(Point* point);
+
+AdvancingFront* front();
+
+void MeshClean(Triangle& triangle);
+
+std::vector<Triangle*> GetTriangles();
+std::list<Triangle*> GetMap();
+
+std::vector<Edge*> edge_list;
+
+struct Basin {
+ Node* left_node;
+ Node* bottom_node;
+ Node* right_node;
+ double width;
+ bool left_highest;
+
+ Basin() : left_node(NULL), bottom_node(NULL), right_node(NULL), width(0.0), left_highest(false)
+ {
+ }
+
+ void Clear()
+ {
+ left_node = NULL;
+ bottom_node = NULL;
+ right_node = NULL;
+ width = 0.0;
+ left_highest = false;
+ }
+};
+
+struct EdgeEvent {
+ Edge* constrained_edge;
+ bool right;
+
+ EdgeEvent() : constrained_edge(NULL), right(false)
+ {
+ }
+};
+
+Basin basin;
+EdgeEvent edge_event;
+
+private:
+
+friend class Sweep;
+
+std::vector<Triangle*> triangles_;
+std::list<Triangle*> map_;
+std::vector<Point*> points_;
+
+// Advancing front
+AdvancingFront* front_;
+// head point used with advancing front
+Point* head_;
+// tail point used with advancing front
+Point* tail_;
+
+Node *af_head_, *af_middle_, *af_tail_;
+
+void InitTriangulation();
+void InitEdges(std::vector<Point*> polyline);
+
+};
+
+inline AdvancingFront* SweepContext::front()
+{
+ return front_;
+}
+
+inline int SweepContext::point_count()
+{
+ return points_.size();
+}
+
+inline void SweepContext::set_head(Point* p1)
+{
+ head_ = p1;
+}
+
+inline Point* SweepContext::head()
+{
+ return head_;
+}
+
+inline void SweepContext::set_tail(Point* p1)
+{
+ tail_ = p1;
+}
+
+inline Point* SweepContext::tail()
+{
+ return tail_;
+}
+
+}
+
+#endif
diff --git a/polygon/polygon_test_point_inside.cpp b/polygon/polygon_test_point_inside.cpp
new file mode 100644
index 0000000..df2a34b
--- /dev/null
+++ b/polygon/polygon_test_point_inside.cpp
@@ -0,0 +1,171 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2007-2014 Jean-Pierre Charras, jp.charras at wanadoo.fr
+ * Copyright (C) 2007-2014 KiCad Developers, see CHANGELOG.TXT for contributors.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * @file polygon_test_point_inside.cpp
+ */
+
+#include <cmath>
+#include <vector>
+#include <PolyLine.h>
+
+/* this algo uses the the Jordan curve theorem to find if a point is inside or outside a polygon:
+ * It run a semi-infinite line horizontally (increasing x, fixed y)
+ * out from the test point, and count how many edges it crosses.
+ * At each crossing, the ray switches between inside and outside.
+ * If odd count, the test point is inside the polygon
+ * This is called the Jordan curve theorem, or sometimes referred to as the "even-odd" test.
+ * Take care to starting and ending points of segments outlines, when the horizontal line crosses a segment outline
+ * exactly on an ending point:
+ * Because the starting point of a segment is also the ending point of the previous, only one must be used.
+ * And we do no use twice the same segment, so we do NOT use both starting and ending points of these 2 segments.
+ * So we must use only one ending point of each segment when calculating intersections
+ * but it cannot be always the starting or the ending point. This depend on relative position of 2 consectutive segments
+ * Here, the ending point above the Y reference position is used
+ * and the ending point below or equal the Y reference position is NOT used
+ * Obviously, others cases are irrelevant because there is not intersection.
+ */
+
+#define OUTSIDE false
+#define INSIDE true
+
+bool TestPointInsidePolygon( const CPOLYGONS_LIST& aPolysList,
+ int aIdxstart,
+ int aIdxend,
+ int aRefx,
+ int aRefy)
+
+/**
+ * Function TestPointInsidePolygon
+ * test if a point is inside or outside a polygon.
+ * the polygon must have only lines (not arcs) for outlines.
+ * @param aPolysList: the list of polygons
+ * @param aIdxstart: the starting point of a given polygon in m_FilledPolysList.
+ * @param aIdxend: the ending point of this polygon in m_FilledPolysList.
+ * @param aRefx, aRefy: the point coordinate to test
+ * @return true if the point is inside, false for outside
+ */
+{
+ // count intersection points to right of (refx,refy). If odd number, point (refx,refy) is inside polyline
+ int ics, ice;
+ int count = 0;
+
+ // find all intersection points of line with polyline sides
+ for( ics = aIdxstart, ice = aIdxend; ics <= aIdxend; ice = ics++ )
+ {
+ int seg_startX = aPolysList.GetX( ics );
+ int seg_startY = aPolysList.GetY( ics );
+ int seg_endX = aPolysList.GetX( ice );
+ int seg_endY = aPolysList.GetY( ice );
+
+ /* Trivial cases: skip if ref above or below the segment to test */
+ if( ( seg_startY > aRefy ) && (seg_endY > aRefy ) )
+ continue;
+
+ // segment below ref point, or one of its ends has the same Y pos as the ref point: skip
+ // So we eliminate one end point of 2 consecutive segments.
+ // Note: also we skip horizontal segments if ref point is on this horizontal line
+ // So reference points on horizontal segments outlines always are seen as outside the polygon
+ if( ( seg_startY <= aRefy ) && (seg_endY <= aRefy ) )
+ continue;
+
+ /* refy is between seg_startY and seg_endY.
+ * note: here: horizontal segments (seg_startY == seg_endY) are skipped,
+ * either by the first test or by the second test
+ * see if an horizontal semi infinite line from refx is intersecting the segment
+ */
+
+ // calculate the x position of the intersection of this segment and the semi infinite line
+ // this is more easier if we move the X,Y axis origin to the segment start point:
+ seg_endX -= seg_startX;
+ seg_endY -= seg_startY;
+ double newrefx = (double) (aRefx - seg_startX);
+ double newrefy = (double) (aRefy - seg_startY);
+
+ // Now calculate the x intersection coordinate of the line from (0,0) to (seg_endX,seg_endY)
+ // with the horizontal line at the new refy position
+ // the line slope = seg_endY/seg_endX;
+ // and the x pos relative to the new origin is intersec_x = refy/slope
+ // Note: because horizontal segments are skipped, 1/slope exists (seg_endY never == O)
+ double intersec_x = (newrefy * seg_endX) / seg_endY;
+ if( newrefx < intersec_x ) // Intersection found with the semi-infinite line from refx to infinite
+ count++;
+ }
+
+ return count & 1 ? INSIDE : OUTSIDE;
+}
+
+
+/* Function TestPointInsidePolygon (overlaid)
+ * same as previous, but use wxPoint and aCount corners
+ */
+bool TestPointInsidePolygon( const wxPoint *aPolysList, int aCount, const wxPoint &aRefPoint )
+{
+ // count intersection points to right of (refx,refy). If odd number, point (refx,refy) is inside polyline
+ int ics, ice;
+ int count = 0;
+ // find all intersection points of line with polyline sides
+ for( ics = 0, ice = aCount-1; ics < aCount; ice = ics++ )
+ {
+ int seg_startX = aPolysList[ics].x;
+ int seg_startY = aPolysList[ics].y;
+ int seg_endX = aPolysList[ice].x;
+ int seg_endY = aPolysList[ice].y;
+
+ /* Trivial cases: skip if ref above or below the segment to test */
+ if( ( seg_startY > aRefPoint.y ) && (seg_endY > aRefPoint.y ) )
+ continue;
+
+ // segment below ref point, or one of its ends has the same Y pos as the ref point: skip
+ // So we eliminate one end point of 2 consecutive segments.
+ // Note: also we skip horizontal segments if ref point is on this horizontal line
+ // So reference points on horizontal segments outlines always are seen as outside the polygon
+ if( ( seg_startY <= aRefPoint.y ) && (seg_endY <= aRefPoint.y ) )
+ continue;
+
+ /* refy is between seg_startY and seg_endY.
+ * note: here: horizontal segments (seg_startY == seg_endY) are skipped,
+ * either by the first test or by the second test
+ * see if an horizontal semi infinite line from refx is intersecting the segment
+ */
+
+ // calculate the x position of the intersection of this segment and the semi infinite line
+ // this is more easier if we move the X,Y axis origin to the segment start point:
+ seg_endX -= seg_startX;
+ seg_endY -= seg_startY;
+ double newrefx = (double) (aRefPoint.x - seg_startX);
+ double newrefy = (double) (aRefPoint.y - seg_startY);
+
+ // Now calculate the x intersection coordinate of the line from (0,0) to (seg_endX,seg_endY)
+ // with the horizontal line at the new refy position
+ // the line slope = seg_endY/seg_endX;
+ // and the x pos relative to the new origin is intersec_x = refy/slope
+ // Note: because horizontal segments are skipped, 1/slope exists (seg_endY never == O)
+ double intersec_x = (newrefy * seg_endX) / seg_endY;
+ if( newrefx < intersec_x ) // Intersection found with the semi-infinite line from refx to infinite
+ count++;
+ }
+
+ return count & 1 ? INSIDE : OUTSIDE;
+}
diff --git a/polygon/polygon_test_point_inside.h b/polygon/polygon_test_point_inside.h
new file mode 100644
index 0000000..833eba8
--- /dev/null
+++ b/polygon/polygon_test_point_inside.h
@@ -0,0 +1,59 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2007-2014 Jean-Pierre Charras, jp.charras at wanadoo.fr
+ * Copyright (C) 2007-2014 KiCad Developers, see CHANGELOG.TXT for contributors.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifndef __WXWINDOWS__
+// define here wxPoint if we want to compile outside wxWidgets
+class wxPoint
+{
+public:
+ int x, y;
+};
+#endif
+class CPOLYGONS_LIST;
+
+/**
+ * Function TestPointInsidePolygon
+ * test if a point is inside or outside a polygon.
+ * @param aPolysList: the list of polygons
+ * @param aIdxstart: the starting point of a given polygon in m_FilledPolysList.
+ * @param aIdxend: the ending point of the polygon in m_FilledPolysList.
+ * @param aRefx, aRefy: the point coordinate to test
+ * @return true if the point is inside, false for outside
+ */
+bool TestPointInsidePolygon( const CPOLYGONS_LIST& aPolysList,
+ int aIdxstart,
+ int aIdxend,
+ int aRefx,
+ int aRefy);
+/**
+ * Function TestPointInsidePolygon (overlaid)
+ * same as previous, but mainly use wxPoint
+ * @param aPolysList: the list of polygons
+ * @param aCount: corners count in aPolysList.
+ * @param aRefPoint: the point coordinate to test
+ * @return true if the point is inside, false for outside
+ */
+bool TestPointInsidePolygon( const wxPoint* aPolysList,
+ int aCount,
+ const wxPoint &aRefPoint );
diff --git a/polygon/polygons_defs.h b/polygon/polygons_defs.h
new file mode 100644
index 0000000..82bccfa
--- /dev/null
+++ b/polygon/polygons_defs.h
@@ -0,0 +1,89 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2012-2014 Jean-Pierre Charras, jp.charras at wanadoo.fr
+ * Copyright (C) 2012-2014 KiCad Developers, see CHANGELOG.TXT for contributors.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/*
+ * file polygons_defs.h
+ * definitions to use boost::polygon in KiCad.
+ */
+
+#ifndef _POLYGONS_DEFS_H_
+#define _POLYGONS_DEFS_H_
+
+#include <boost/polygon/polygon.hpp>
+
+// Define some types used here from boost::polygon
+namespace bpl = boost::polygon; // bpl = boost polygon library
+using namespace bpl::operators; // +, -, =, ...
+
+// Definitions needed by boost::polygon
+typedef int coordinate_type;
+
+/**
+ * KI_POLYGON defines a single polygon ( boost::polygon_data type.
+ * When holes are created in a KPolygon, they are
+ * linked to main outline by overlapping segments,
+ * so there is always one polygon and one list of corners
+ * coordinates are int
+ */
+typedef bpl::polygon_data<int> KI_POLYGON;
+
+/**
+ * KI_POLYGON_SET defines a set of single KI_POLYGON.
+ * A KI_POLYGON_SET is used to store a set of polygons
+ * when performing operations between 2 polygons
+ * or 2 sets of polygons
+ * The result of operations like and, xor... between 2 polygons
+ * is always stored in a KI_POLYGON_SET, because these operations
+ * can create many polygons
+ */
+typedef std::vector<KI_POLYGON> KI_POLYGON_SET;
+
+/**
+ * KI_POLY_POINT defines a point for boost::polygon.
+ * KI_POLY_POINT store x and y coordinates (int)
+ */
+typedef bpl::point_data<int> KI_POLY_POINT;
+
+/**
+ * KI_POLYGON_WITH_HOLES defines a single polygon with holes
+ * When holes are created in a KI_POLYGON_WITH_HOLES, they are
+ * stored as separate single polygons,
+ * KI_POLYGON_WITH_HOLES store always one polygon for the external outline
+ * and one list of polygons (holes) which can be empty
+ */
+typedef bpl::polygon_with_holes_data<int> KI_POLYGON_WITH_HOLES;
+
+/**
+ * KI_POLYGON_WITH_HOLES_SET defines a set of KI_POLYGON_WITH_HOLES.
+ * A KI_POLYGON_WITH_HOLES_SET is used to store a set of polygons with holes
+ * when performing operations between 2 polygons
+ * or 2 sets of polygons with holes
+ * The result of operations like and, xor... between 2 polygons with holes
+ * is always stored in a KI_POLYGON_WITH_HOLES_SET, because these operations
+ * can create many separate polygons with holespolygons
+ */
+typedef std::vector<KI_POLYGON_WITH_HOLES> KI_POLYGON_WITH_HOLES_SET;
+
+
+#endif // #ifndef _POLYGONS_DEFS_H_
diff --git a/potrace/AUTHORS b/potrace/AUTHORS
new file mode 100644
index 0000000..0a8b8e0
--- /dev/null
+++ b/potrace/AUTHORS
@@ -0,0 +1,5 @@
+Known contributors are listed here, in alphabetical order by their
+abbreviations (which are used in Changelog).
+
+ PS1 Peter Selinger <selinger at users.sourceforge.net> (author)
+ TA1 Tor Andersson <tor at ghostscript.com>
diff --git a/potrace/CMakeLists.txt b/potrace/CMakeLists.txt
new file mode 100644
index 0000000..476bb07
--- /dev/null
+++ b/potrace/CMakeLists.txt
@@ -0,0 +1,16 @@
+include_directories(BEFORE ${INC_BEFORE})
+include_directories(
+ ${INC_AFTER}
+ )
+
+set(POTRACE_SRCS
+ bitmap_io.cpp
+ curve.cpp
+ decompose.cpp
+ greymap.cpp
+ potracelib.cpp
+ render.cpp
+ trace.cpp
+ )
+
+add_library(potrace STATIC ${POTRACE_SRCS})
diff --git a/potrace/auxiliary.h b/potrace/auxiliary.h
new file mode 100644
index 0000000..f96c6a8
--- /dev/null
+++ b/potrace/auxiliary.h
@@ -0,0 +1,89 @@
+/* Copyright (C) 2001-2007 Peter Selinger.
+ * This file is part of Potrace. It is free software and it is covered
+ * by the GNU General Public License. See the file COPYING for details. */
+
+/* This header file collects some general-purpose macros (and static
+ * inline functions) that are used in various places. */
+
+#ifndef AUXILIARY_H
+#define AUXILIARY_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* ---------------------------------------------------------------------- */
+/* point arithmetic */
+
+#include <potracelib.h>
+
+struct point_s
+{
+ long x;
+ long y;
+};
+typedef struct point_s point_t;
+
+typedef potrace_dpoint_t dpoint_t;
+
+/* convert point_t to dpoint_t */
+static inline dpoint_t dpoint( point_t p )
+{
+ dpoint_t res;
+
+ res.x = p.x;
+ res.y = p.y;
+ return res;
+}
+
+
+/* range over the straight line segment [a,b] when lambda ranges over [0,1] */
+static inline dpoint_t interval( double lambda, dpoint_t a, dpoint_t b )
+{
+ dpoint_t res;
+
+ res.x = a.x + lambda * (b.x - a.x);
+ res.y = a.y + lambda * (b.y - a.y);
+ return res;
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+/* some useful macros. Note: the "mod" macro works correctly for
+ * negative a. Also note that the test for a>=n, while redundant,
+ * speeds up the mod function by 70% in the average case (significant
+ * since the program spends about 16% of its time here - or 40%
+ * without the test). The "floordiv" macro returns the largest integer
+ * <= a/n, and again this works correctly for negative a, as long as
+ * a,n are integers and n>0. */
+
+/* integer arithmetic */
+
+static inline int mod( int a, int n )
+{
+ return a>=n ? a % n : a>=0 ? a : n - 1 - (-1 - a) % n;
+}
+
+
+static inline int floordiv( int a, int n )
+{
+ return a>=0 ? a / n : -1 - (-1 - a) / n;
+}
+
+
+/* Note: the following work for integers and other numeric types. */
+#undef sign
+#undef abs
+#undef min
+#undef max
+#undef sq
+#undef cu
+#define sign( x ) ( (x)>0 ? 1 : (x)<0 ? -1 : 0 )
+#define abs( a ) ( (a)>0 ? (a) : -(a) )
+#define min( a, b ) ( (a)<(b) ? (a) : (b) )
+#define max( a, b ) ( (a)>(b) ? (a) : (b) )
+#define sq( a ) ( (a) * (a) )
+#define cu( a ) ( (a) * (a) * (a) )
+
+#endif /* AUXILIARY_H */
diff --git a/potrace/bitmap.h b/potrace/bitmap.h
new file mode 100644
index 0000000..6885fc3
--- /dev/null
+++ b/potrace/bitmap.h
@@ -0,0 +1,118 @@
+/* Copyright (C) 2001-2007 Peter Selinger.
+ * This file is part of Potrace. It is free software and it is covered
+ * by the GNU General Public License. See the file COPYING for details. */
+
+#ifndef BITMAP_H
+#define BITMAP_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+
+/* The bitmap type is defined in potracelib.h */
+#include <potracelib.h>
+
+/* The present file defines some convenient macros and static inline
+ * functions for accessing bitmaps. Since they only produce inline
+ * code, they can be conveniently shared by the library and frontends,
+ * if desired */
+
+/* ---------------------------------------------------------------------- */
+/* some measurements */
+
+#define BM_WORDSIZE ( (int) sizeof(potrace_word) )
+#define BM_WORDBITS (8 * BM_WORDSIZE)
+#define BM_HIBIT ( ( (potrace_word) 1 ) << (BM_WORDBITS - 1) )
+#define BM_ALLBITS (~(potrace_word) 0)
+
+/* macros for accessing pixel at index (x,y). U* macros omit the
+ * bounds check. */
+
+#define bm_scanline( bm, y ) ( (bm)->map + (y) * (bm)->dy )
+#define bm_index( bm, x, y ) (&bm_scanline( bm, y )[(x) / BM_WORDBITS])
+#define bm_mask( x ) ( BM_HIBIT >> ( (x) & (BM_WORDBITS - 1) ) )
+#define bm_range( x, a ) ( (int) (x) >= 0 && (int) (x) < (a) )
+#define bm_safe( bm, x, y ) ( bm_range( x, (bm)->w ) && bm_range( y, (bm)->h ) )
+#define BM_UGET( bm, x, y ) ( ( *bm_index( bm, x, y ) & bm_mask( x ) ) != 0 )
+#define BM_USET( bm, x, y ) ( *bm_index( bm, x, y ) |= bm_mask( x ) )
+#define BM_UCLR( bm, x, y ) ( *bm_index( bm, x, y ) &= ~bm_mask( x ) )
+#define BM_UINV( bm, x, y ) ( *bm_index( bm, x, y ) ^= bm_mask( x ) )
+#define BM_UPUT( bm, x, y, b ) ( (b) ? BM_USET( bm, x, y ) : BM_UCLR( bm, x, y ) )
+#define BM_GET( bm, x, y ) (bm_safe( bm, x, y ) ? BM_UGET( bm, x, y ) : 0)
+#define BM_SET( bm, x, y ) (bm_safe( bm, x, y ) ? BM_USET( bm, x, y ) : 0)
+#define BM_CLR( bm, x, y ) (bm_safe( bm, x, y ) ? BM_UCLR( bm, x, y ) : 0)
+#define BM_INV( bm, x, y ) (bm_safe( bm, x, y ) ? BM_UINV( bm, x, y ) : 0)
+#define BM_PUT( bm, x, y, b ) (bm_safe( bm, x, y ) ? BM_UPUT( bm, x, y, b ) : 0)
+
+/* free the given bitmap. Leaves errno untouched. */
+static inline void bm_free( potrace_bitmap_t* bm )
+{
+ if( bm )
+ {
+ free( bm->map );
+ }
+ free( bm );
+}
+
+
+/* return new un-initialized bitmap. NULL with errno on error */
+static inline potrace_bitmap_t* bm_new( int w, int h )
+{
+ potrace_bitmap_t* bm;
+ int dy = (w + BM_WORDBITS - 1) / BM_WORDBITS;
+
+ bm = (potrace_bitmap_t*) malloc( sizeof(potrace_bitmap_t) );
+ if( !bm )
+ {
+ return NULL;
+ }
+ bm->w = w;
+ bm->h = h;
+ bm->dy = dy;
+ bm->map = (potrace_word*) malloc( dy * h * BM_WORDSIZE );
+ if( !bm->map )
+ {
+ free( bm );
+ return NULL;
+ }
+ return bm;
+}
+
+
+/* clear the given bitmap. Set all bits to c. */
+static inline void bm_clear( potrace_bitmap_t* bm, int c )
+{
+ memset( bm->map, c ? -1 : 0, bm->dy * bm->h * BM_WORDSIZE );
+}
+
+
+/* duplicate the given bitmap. Return NULL on error with errno set. */
+static inline potrace_bitmap_t* bm_dup( const potrace_bitmap_t* bm )
+{
+ potrace_bitmap_t* bm1 = bm_new( bm->w, bm->h );
+
+ if( !bm1 )
+ {
+ return NULL;
+ }
+ memcpy( bm1->map, bm->map, bm->dy * bm->h * BM_WORDSIZE );
+ return bm1;
+}
+
+
+/* invert the given bitmap. */
+static inline void bm_invert( potrace_bitmap_t* bm )
+{
+ int i;
+
+ for( i = 0; i < bm->dy * bm->h; i++ )
+ {
+ bm->map[i] ^= BM_ALLBITS;
+ }
+}
+
+
+#endif /* BITMAP_H */
diff --git a/potrace/bitmap_io.cpp b/potrace/bitmap_io.cpp
new file mode 100644
index 0000000..3866593
--- /dev/null
+++ b/potrace/bitmap_io.cpp
@@ -0,0 +1,969 @@
+/* Copyright (C) 2001-2007 Peter Selinger.
+ * This file is part of Potrace. It is free software and it is covered
+ * by the GNU General Public License. See the file COPYING for details. */
+
+/* $Id: bitmap_io.c 147 2007-04-09 00:44:09Z selinger $ */
+
+/* Routines for manipulating bitmaps, including reading pbm files. */
+
+#include <stdio.h>
+
+#include <bitmap.h>
+
+#define INTBITS ( 8 * sizeof(int) )
+
+static int bm_readbody_bmp( FILE* f, double threshold, potrace_bitmap_t** bmp );
+static int bm_readbody_pnm( FILE* f, double threshold, potrace_bitmap_t** bmp, int magic );
+
+/* ---------------------------------------------------------------------- */
+/* routines for reading pnm streams */
+
+/* read next character after whitespace and comments. Return EOF on
+ * end of file or error. */
+static int fgetc_ws( FILE* f )
+{
+ int c;
+
+ while( 1 )
+ {
+ c = fgetc( f );
+ if( c=='#' )
+ {
+ while( 1 )
+ {
+ c = fgetc( f );
+ if( c=='\n' || c==EOF )
+ {
+ break;
+ }
+ }
+ }
+ /* space, tab, line feed, carriage return, form-feed */
+ if( c!=' ' && c!='\t' && c!='\r' && c!='\n' && c!=12 )
+ {
+ return c;
+ }
+ }
+}
+
+
+/* skip whitespace and comments, then read a non-negative decimal
+ * number from a stream. Return -1 on EOF. Tolerate other errors (skip
+ * bad characters). Do not the read any characters following the
+ * number (put next character back into the stream) */
+
+static int readnum( FILE* f )
+{
+ int c;
+ int acc;
+
+ /* skip whitespace and comments */
+ while( 1 )
+ {
+ c = fgetc_ws( f );
+ if( c==EOF )
+ {
+ return -1;
+ }
+ if( c>='0' && c<='9' )
+ {
+ break;
+ }
+ }
+
+ /* first digit is already in c */
+ acc = c - '0';
+ while( 1 )
+ {
+ c = fgetc( f );
+ if( c==EOF )
+ {
+ break;
+ }
+ if( c<'0' || c>'9' )
+ {
+ ungetc( c, f );
+ break;
+ }
+ acc *= 10;
+ acc += c - '0';
+ }
+
+ return acc;
+}
+
+
+/* similar to readnum, but read only a single 0 or 1, and do not read
+ * any characters after it. */
+
+static int readbit( FILE* f )
+{
+ int c;
+
+ /* skip whitespace and comments */
+ while( 1 )
+ {
+ c = fgetc_ws( f );
+ if( c==EOF )
+ {
+ return -1;
+ }
+ if( c>='0' && c<='1' )
+ {
+ break;
+ }
+ }
+
+ return c - '0';
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+/* read a PNM stream: P1-P6 format (see pnm(5)), or a BMP stream, and
+ * convert the output to a bitmap. Return bitmap in *bmp. Return 0 on
+ * success, -1 on error with errno set, -2 on bad file format (with
+ * error message in bm_read_error), and 1 on premature end of file, -3
+ * on empty file (including files which contain only whitespace and
+ * comments), -4 if wrong magic number. If the return value is >=0,
+ *bmp is valid. */
+
+const char* bm_read_error = NULL;
+
+int bm_read( FILE* f, double threshold, potrace_bitmap_t** bmp )
+{
+ int magic[2];
+
+ /* read magic number. We ignore whitespace and comments before the
+ * magic, for the benefit of concatenated files in P1-P3 format.
+ * Multiple P1-P3 images in a single file are not formally allowed
+ * by the PNM standard, but there is no harm in being lenient. */
+
+ magic[0] = fgetc_ws( f );
+ if( magic[0] == EOF )
+ {
+ return -3;
+ }
+ magic[1] = fgetc( f );
+ if( magic[0] == 'P' && magic[1] >= '1' && magic[1] <= '6' )
+ {
+ return bm_readbody_pnm( f, threshold, bmp, magic[1] );
+ }
+ if( magic[0] == 'B' && magic[1] == 'M' )
+ {
+ return bm_readbody_bmp( f, threshold, bmp );
+ }
+ return -4;
+}
+
+
+/* ---------------------------------------------------------------------- */
+/* read PNM format */
+
+/* read PNM stream after magic number. Return values as for bm_read */
+static int bm_readbody_pnm( FILE* f, double threshold, potrace_bitmap_t** bmp, int magic )
+{
+ potrace_bitmap_t* bm;
+ int x, y, i, b, b1, sum;
+ int bpr; /* bytes per row (as opposed to 4*bm->c) */
+ int w, h, max;
+
+ bm = NULL;
+
+ w = readnum( f );
+ if( w<0 )
+ {
+ goto format_error;
+ }
+
+ h = readnum( f );
+ if( h<0 )
+ {
+ goto format_error;
+ }
+
+ /* allocate bitmap */
+ bm = bm_new( w, h );
+ if( !bm )
+ {
+ return -1;
+ }
+
+ /* zero it out */
+ bm_clear( bm, 0 );
+
+ switch( magic )
+ {
+ default:
+ /* not reached */
+ goto format_error;
+
+ case '1':
+ /* read P1 format: PBM ascii */
+
+ for( y = h - 1; y>=0; y-- )
+ {
+ for( x = 0; x<w; x++ )
+ {
+ b = readbit( f );
+ if( b<0 )
+ {
+ goto eof;
+ }
+ BM_UPUT( bm, x, y, b );
+ }
+ }
+
+ break;
+
+ case '2':
+ /* read P2 format: PGM ascii */
+
+ max = readnum( f );
+ if( max<1 )
+ {
+ goto format_error;
+ }
+
+ for( y = h - 1; y>=0; y-- )
+ {
+ for( x = 0; x<w; x++ )
+ {
+ b = readnum( f );
+ if( b<0 )
+ {
+ goto eof;
+ }
+ BM_UPUT( bm, x, y, b > threshold * max ? 0 : 1 );
+ }
+ }
+
+ break;
+
+ case '3':
+ /* read P3 format: PPM ascii */
+
+ max = readnum( f );
+ if( max<1 )
+ {
+ goto format_error;
+ }
+
+ for( y = h - 1; y>=0; y-- )
+ {
+ for( x = 0; x<w; x++ )
+ {
+ sum = 0;
+ for( i = 0; i<3; i++ )
+ {
+ b = readnum( f );
+ if( b<0 )
+ {
+ goto eof;
+ }
+ sum += b;
+ }
+
+ BM_UPUT( bm, x, y, sum > 3 * threshold * max ? 0 : 1 );
+ }
+ }
+
+ break;
+
+ case '4':
+ /* read P4 format: PBM raw */
+
+ b = fgetc( f ); /* read single white-space character after height */
+ if( b==EOF )
+ {
+ goto format_error;
+ }
+
+ bpr = (w + 7) / 8;
+
+ for( y = h - 1; y>=0; y-- )
+ {
+ for( i = 0; i<bpr; i++ )
+ {
+ b = fgetc( f );
+ if( b==EOF )
+ {
+ goto eof;
+ }
+ *bm_index( bm, i * 8,
+ y ) |= ( (potrace_word) b ) <<
+ ( 8 * ( BM_WORDSIZE - 1 - (i % BM_WORDSIZE) ) );
+ }
+ }
+
+ break;
+
+ case '5':
+ /* read P5 format: PGM raw */
+
+ max = readnum( f );
+ if( max<1 )
+ {
+ goto format_error;
+ }
+
+ b = fgetc( f ); /* read single white-space character after max */
+ if( b==EOF )
+ {
+ goto format_error;
+ }
+
+ for( y = h - 1; y>=0; y-- )
+ {
+ for( x = 0; x<w; x++ )
+ {
+ b = fgetc( f );
+ if( b==EOF )
+ goto eof;
+ if( max>=256 )
+ {
+ b <<= 8;
+ b1 = fgetc( f );
+ if( b1==EOF )
+ goto eof;
+ b |= b1;
+ }
+ BM_UPUT( bm, x, y, b > threshold * max ? 0 : 1 );
+ }
+ }
+
+ break;
+
+ case '6':
+ /* read P6 format: PPM raw */
+
+ max = readnum( f );
+ if( max<1 )
+ {
+ goto format_error;
+ }
+
+ b = fgetc( f ); /* read single white-space character after max */
+ if( b==EOF )
+ {
+ goto format_error;
+ }
+
+ for( y = h - 1; y>=0; y-- )
+ {
+ for( x = 0; x<w; x++ )
+ {
+ sum = 0;
+ for( i = 0; i<3; i++ )
+ {
+ b = fgetc( f );
+ if( b==EOF )
+ {
+ goto eof;
+ }
+ if( max>=256 )
+ {
+ b <<= 8;
+ b1 = fgetc( f );
+ if( b1==EOF )
+ goto eof;
+ b |= b1;
+ }
+ sum += b;
+ }
+
+ BM_UPUT( bm, x, y, sum > 3 * threshold * max ? 0 : 1 );
+ }
+ }
+
+ break;
+ }
+
+ *bmp = bm;
+ return 0;
+
+eof:
+ *bmp = bm;
+ return 1;
+
+format_error:
+ bm_free( bm );
+ if( magic == '1' || magic == '4' )
+ {
+ bm_read_error = "invalid pbm file";
+ }
+ else if( magic == '2' || magic == '5' )
+ {
+ bm_read_error = "invalid pgm file";
+ }
+ else
+ {
+ bm_read_error = "invalid ppm file";
+ }
+ return -2;
+}
+
+
+/* ---------------------------------------------------------------------- */
+/* read BMP format */
+
+struct bmp_info_s
+{
+ unsigned int FileSize;
+ unsigned int reserved;
+ unsigned int DataOffset;
+ unsigned int InfoSize;
+ unsigned int w; /* width */
+ unsigned int h; /* height */
+ unsigned int Planes;
+ unsigned int bits; /* bits per sample */
+ unsigned int comp; /* compression mode */
+ unsigned int ImageSize;
+ unsigned int XpixelsPerM;
+ unsigned int YpixelsPerM;
+ unsigned int ncolors; /* number of colors in palette */
+ unsigned int ColorsImportant;
+ unsigned int ctbits; /* sample size for color table */
+};
+typedef struct bmp_info_s bmp_info_t;
+
+/* auxiliary */
+
+static int bmp_count = 0; /* counter for byte padding */
+static int bmp_pos = 0; /* counter from start of BMP data */
+
+/* read n-byte little-endian integer. Return 1 on EOF or error, else
+ * 0. Assume n<=4. */
+static int bmp_readint( FILE* f, int n, unsigned int* p )
+{
+ int i;
+ unsigned int sum = 0;
+ int b;
+
+ for( i = 0; i<n; i++ )
+ {
+ b = fgetc( f );
+ if( b==EOF )
+ {
+ return 1;
+ }
+ sum += b << (8 * i);
+ }
+
+ bmp_count += n;
+ bmp_pos += n;
+ *p = sum;
+ return 0;
+}
+
+
+/* reset padding boundary */
+static void bmp_pad_reset( void )
+{
+ bmp_count = 0;
+}
+
+
+/* read padding bytes to 4-byte boundary. Return 1 on EOF or error,
+ * else 0. */
+static int bmp_pad( FILE* f )
+{
+ int c, i, b;
+
+ c = (-bmp_count) & 3;
+ for( i = 0; i<c; i++ )
+ {
+ b = fgetc( f );
+ if( b==EOF )
+ {
+ return 1;
+ }
+ }
+
+ bmp_pos += c;
+ bmp_count = 0;
+ return 0;
+}
+
+
+/* forward to the new file position. Return 1 on EOF or error, else 0 */
+static int bmp_forward( FILE* f, int pos )
+{
+ int b;
+
+ while( bmp_pos < pos )
+ {
+ b = fgetc( f );
+ if( b==EOF )
+ {
+ return 1;
+ }
+ bmp_pos++;
+ bmp_count++;
+ }
+
+ return 0;
+}
+
+
+#define TRY( x ) if( x ) \
+ goto try_error
+#define TRY_EOF( x ) if( x ) \
+ goto eof
+
+/* read BMP stream after magic number. Return values as for bm_read.
+ * We choose to be as permissive as possible, since there are many
+ * programs out there which produce BMP. For instance, ppmtobmp can
+ * produce codings with anywhere from 1-8 or 24 bits per sample,
+ * although most specifications only allow 1,4,8,24,32. We can also
+ * read both the old and new OS/2 BMP formats in addition to the
+ * Windows BMP format. */
+static int bm_readbody_bmp( FILE* f, double threshold, potrace_bitmap_t** bmp )
+{
+ bmp_info_t bmpinfo;
+ int* coltable;
+ unsigned int b, c;
+ unsigned int i;
+ potrace_bitmap_t* bm;
+ int mask;
+ unsigned int x, y;
+ int col[2];
+ unsigned int bitbuf;
+ unsigned int n;
+ int col1[2];
+
+ bm_read_error = NULL;
+ bm = NULL;
+ coltable = NULL;
+
+ bmp_pos = 2; /* set file position */
+
+ /* file header (minus magic number) */
+ TRY( bmp_readint( f, 4, &bmpinfo.FileSize ) );
+ TRY( bmp_readint( f, 4, &bmpinfo.reserved ) );
+ TRY( bmp_readint( f, 4, &bmpinfo.DataOffset ) );
+
+ /* info header */
+ TRY( bmp_readint( f, 4, &bmpinfo.InfoSize ) );
+ if( bmpinfo.InfoSize == 40 || bmpinfo.InfoSize == 64 )
+ {
+ /* Windows or new OS/2 format */
+ bmpinfo.ctbits = 32; /* sample size in color table */
+ TRY( bmp_readint( f, 4, &bmpinfo.w ) );
+ TRY( bmp_readint( f, 4, &bmpinfo.h ) );
+ TRY( bmp_readint( f, 2, &bmpinfo.Planes ) );
+ TRY( bmp_readint( f, 2, &bmpinfo.bits ) );
+ TRY( bmp_readint( f, 4, &bmpinfo.comp ) );
+ TRY( bmp_readint( f, 4, &bmpinfo.ImageSize ) );
+ TRY( bmp_readint( f, 4, &bmpinfo.XpixelsPerM ) );
+ TRY( bmp_readint( f, 4, &bmpinfo.YpixelsPerM ) );
+ TRY( bmp_readint( f, 4, &bmpinfo.ncolors ) );
+ TRY( bmp_readint( f, 4, &bmpinfo.ColorsImportant ) );
+ }
+ else if( bmpinfo.InfoSize == 12 )
+ {
+ /* old OS/2 format */
+ bmpinfo.ctbits = 24; /* sample size in color table */
+ TRY( bmp_readint( f, 2, &bmpinfo.w ) );
+ TRY( bmp_readint( f, 2, &bmpinfo.h ) );
+ TRY( bmp_readint( f, 2, &bmpinfo.Planes ) );
+ TRY( bmp_readint( f, 2, &bmpinfo.bits ) );
+ bmpinfo.comp = 0;
+ bmpinfo.ncolors = 0;
+ }
+ else
+ {
+ goto format_error;
+ }
+
+ /* forward to color table (i.e., if bmpinfo.InfoSize == 64) */
+ TRY( bmp_forward( f, 14 + bmpinfo.InfoSize ) );
+
+ if( bmpinfo.Planes != 1 )
+ {
+ bm_read_error = "cannot handle bmp planes";
+ goto format_error; /* can't handle planes */
+ }
+
+ if( bmpinfo.ncolors == 0 )
+ {
+ bmpinfo.ncolors = 1 << bmpinfo.bits;
+ }
+
+ /* color table, present only if bmpinfo.bits <= 8. */
+ if( bmpinfo.bits <= 8 )
+ {
+ coltable = (int*) malloc( bmpinfo.ncolors * sizeof(int) );
+ if( !coltable )
+ {
+ goto std_error;
+ }
+
+ /* NOTE: since we are reading a bitmap, we can immediately convert
+ * the color table entries to bits. */
+ for( i = 0; i<bmpinfo.ncolors; i++ )
+ {
+ TRY( bmp_readint( f, bmpinfo.ctbits / 8, &c ) );
+ c = ( (c >> 16) & 0xff ) + ( (c >> 8) & 0xff ) + (c & 0xff);
+ coltable[i] = (c > 3 * threshold * 255 ? 0 : 1);
+ if( i<2 )
+ {
+ col1[i] = c;
+ }
+ }
+ }
+
+ /* forward to data */
+ if( bmpinfo.InfoSize != 12 ) /* not old OS/2 format */
+ {
+ TRY( bmp_forward( f, bmpinfo.DataOffset ) );
+ }
+
+ /* allocate bitmap */
+ bm = bm_new( bmpinfo.w, bmpinfo.h );
+ if( !bm )
+ {
+ goto std_error;
+ }
+
+ /* zero it out */
+ bm_clear( bm, 0 );
+
+ switch( bmpinfo.bits + 0x100 * bmpinfo.comp )
+ {
+ default:
+ goto format_error;
+ break;
+
+ case 0x001: /* monochrome palette */
+ if( col1[0] < col1[1] ) /* make the darker color black */
+ {
+ mask = 0xff;
+ }
+ else
+ {
+ mask = 0;
+ }
+
+ /* raster data */
+ for( y = 0; y<bmpinfo.h; y++ )
+ {
+ bmp_pad_reset();
+ for( i = 0; 8 * i<bmpinfo.w; i++ )
+ {
+ TRY_EOF( bmp_readint( f, 1, &b ) );
+ b ^= mask;
+ *bm_index( bm, i * 8,
+ y ) |= ( (potrace_word) b ) <<
+ ( 8 * ( BM_WORDSIZE - 1 - (i % BM_WORDSIZE) ) );
+ }
+
+ TRY( bmp_pad( f ) );
+ }
+
+ break;
+
+ case 0x002: /* 2-bit to 8-bit palettes */
+ case 0x003:
+ case 0x004:
+ case 0x005:
+ case 0x006:
+ case 0x007:
+ case 0x008:
+ for( y = 0; y<bmpinfo.h; y++ )
+ {
+ bmp_pad_reset();
+ bitbuf = 0; /* bit buffer: bits in buffer are high-aligned */
+ n = 0; /* number of bits currently in bitbuffer */
+ for( x = 0; x<bmpinfo.w; x++ )
+ {
+ if( n < bmpinfo.bits )
+ {
+ TRY_EOF( bmp_readint( f, 1, &b ) );
+ bitbuf |= b << (INTBITS - 8 - n);
+ n += 8;
+ }
+ b = bitbuf >> (INTBITS - bmpinfo.bits);
+ bitbuf <<= bmpinfo.bits;
+ n -= bmpinfo.bits;
+ BM_UPUT( bm, x, y, coltable[b] );
+ }
+
+ TRY( bmp_pad( f ) );
+ }
+
+ break;
+
+ case 0x010: /* 16-bit encoding */
+
+ /* can't do this format because it is not well-documented and I
+ * don't have any samples */
+ bm_read_error = "cannot handle bmp 16-bit coding";
+ goto format_error;
+ break;
+
+ case 0x018: /* 24-bit encoding */
+ case 0x020: /* 32-bit encoding */
+ for( y = 0; y<bmpinfo.h; y++ )
+ {
+ bmp_pad_reset();
+ for( x = 0; x<bmpinfo.w; x++ )
+ {
+ TRY_EOF( bmp_readint( f, bmpinfo.bits / 8, &c ) );
+ c = ( (c >> 16) & 0xff ) + ( (c >> 8) & 0xff ) + (c & 0xff);
+ BM_UPUT( bm, x, y, c > 3 * threshold * 255 ? 0 : 1 );
+ }
+
+ TRY( bmp_pad( f ) );
+ }
+
+ break;
+
+ case 0x204: /* 4-bit runlength compressed encoding (RLE4) */
+ x = 0;
+ y = 0;
+ while( 1 )
+ {
+ TRY_EOF( bmp_readint( f, 1, &b ) ); /* opcode */
+ TRY_EOF( bmp_readint( f, 1, &c ) ); /* argument */
+ if( b>0 )
+ {
+ /* repeat count */
+ col[0] = coltable[(c >> 4) & 0xf];
+ col[1] = coltable[c & 0xf];
+ for( i = 0; i<b && x<bmpinfo.w; i++ )
+ {
+ if( x>=bmpinfo.w )
+ {
+ x = 0;
+ y++;
+ }
+ if( y>=bmpinfo.h )
+ {
+ break;
+ }
+ BM_UPUT( bm, x, y, col[i & 1] );
+ x++;
+ }
+ }
+ else if( c == 0 )
+ {
+ /* end of line */
+ y++;
+ x = 0;
+ }
+ else if( c == 1 )
+ {
+ /* end of bitmap */
+ break;
+ }
+ else if( c == 2 )
+ {
+ /* "delta": skip pixels in x and y directions */
+ TRY_EOF( bmp_readint( f, 1, &b ) ); /* x offset */
+ TRY_EOF( bmp_readint( f, 1, &c ) ); /* y offset */
+ x += b;
+ y += c;
+ }
+ else
+ {
+ /* verbatim segment */
+ for( i = 0; i<c; i++ )
+ {
+ if( (i & 1)==0 )
+ {
+ TRY_EOF( bmp_readint( f, 1, &b ) );
+ }
+ if( x>=bmpinfo.w )
+ {
+ x = 0;
+ y++;
+ }
+ if( y>=bmpinfo.h )
+ {
+ break;
+ }
+ BM_PUT( bm, x, y, coltable[( b >> ( 4 - 4 * (i & 1) ) ) & 0xf] );
+ x++;
+ }
+
+ if( (c + 1) & 2 )
+ {
+ /* pad to 16-bit boundary */
+ TRY_EOF( bmp_readint( f, 1, &b ) );
+ }
+ }
+ }
+
+ break;
+
+ case 0x108: /* 8-bit runlength compressed encoding (RLE8) */
+ x = 0;
+ y = 0;
+ while( 1 )
+ {
+ TRY_EOF( bmp_readint( f, 1, &b ) ); /* opcode */
+ TRY_EOF( bmp_readint( f, 1, &c ) ); /* argument */
+ if( b>0 )
+ {
+ /* repeat count */
+ for( i = 0; i<b; i++ )
+ {
+ if( x>=bmpinfo.w )
+ {
+ x = 0;
+ y++;
+ }
+ if( y>=bmpinfo.h )
+ {
+ break;
+ }
+ BM_UPUT( bm, x, y, coltable[c] );
+ x++;
+ }
+ }
+ else if( c == 0 )
+ {
+ /* end of line */
+ y++;
+ x = 0;
+ }
+ else if( c == 1 )
+ {
+ /* end of bitmap */
+ break;
+ }
+ else if( c == 2 )
+ {
+ /* "delta": skip pixels in x and y directions */
+ TRY_EOF( bmp_readint( f, 1, &b ) ); /* x offset */
+ TRY_EOF( bmp_readint( f, 1, &c ) ); /* y offset */
+ x += b;
+ y += c;
+ }
+ else
+ {
+ /* verbatim segment */
+ for( i = 0; i<c; i++ )
+ {
+ TRY_EOF( bmp_readint( f, 1, &b ) );
+ if( x>=bmpinfo.w )
+ {
+ x = 0;
+ y++;
+ }
+ if( y>=bmpinfo.h )
+ {
+ break;
+ }
+ BM_PUT( bm, x, y, coltable[b] );
+ x++;
+ }
+
+ if( c & 1 )
+ {
+ /* pad input to 16-bit boundary */
+ TRY_EOF( bmp_readint( f, 1, &b ) );
+ }
+ }
+ }
+
+ break;
+ } /* switch */
+
+ /* skip any potential junk after the data section, but don't
+ * complain in case EOF is encountered */
+ bmp_forward( f, bmpinfo.FileSize );
+
+ free( coltable );
+ *bmp = bm;
+ return 0;
+
+eof:
+ free( coltable );
+ *bmp = bm;
+ return 1;
+
+format_error:
+try_error:
+ free( coltable );
+ free( bm );
+ if( !bm_read_error )
+ {
+ bm_read_error = "invalid bmp file";
+ }
+ return -2;
+
+std_error:
+ free( coltable );
+ free( bm );
+ return -1;
+}
+
+
+/* ---------------------------------------------------------------------- */
+/* output pbm format */
+
+void bm_writepbm( FILE* f, potrace_bitmap_t* bm )
+{
+ int w, h, bpr, y, i, c;
+
+ w = bm->w;
+ h = bm->h;
+
+ bpr = (w + 7) / 8;
+
+ fprintf( f, "P4\n%d %d\n", w, h );
+ for( y = h - 1; y>=0; y-- )
+ {
+ for( i = 0; i<bpr; i++ )
+ {
+ c =
+ ( *bm_index( bm, i * 8,
+ y ) >> ( 8 * ( BM_WORDSIZE - 1 - (i % BM_WORDSIZE) ) ) ) & 0xff;
+ fputc( c, f );
+ }
+ }
+
+ return;
+}
+
+
+/* ---------------------------------------------------------------------- */
+/* output - for primitive debugging purposes only! */
+
+/* print bitmap to screen */
+int bm_print( FILE* f, potrace_bitmap_t* bm )
+{
+ int x, y;
+ int xx, yy;
+ int d;
+ int sw, sh;
+
+ sw = bm->w < 79 ? bm->w : 79;
+ sh = bm->w < 79 ? bm->h : bm->h * sw * 44 / (79 * bm->w);
+
+ for( yy = sh - 1; yy>=0; yy-- )
+ {
+ for( xx = 0; xx<sw; xx++ )
+ {
+ d = 0;
+ for( x = xx * bm->w / sw; x<(xx + 1) * bm->w / sw; x++ )
+ {
+ for( y = yy * bm->h / sh; y<(yy + 1) * bm->h / sh; y++ )
+ {
+ if( BM_GET( bm, x, y ) )
+ {
+ d++;
+ }
+ }
+ }
+
+ fputc( d ? '*' : ' ', f );
+ }
+
+ fputc( '\n', f );
+ }
+
+ return 0;
+}
diff --git a/potrace/bitmap_io.h b/potrace/bitmap_io.h
new file mode 100644
index 0000000..0590f63
--- /dev/null
+++ b/potrace/bitmap_io.h
@@ -0,0 +1,23 @@
+/* Copyright (C) 2001-2007 Peter Selinger.
+ * This file is part of Potrace. It is free software and it is covered
+ * by the GNU General Public License. See the file COPYING for details. */
+
+/* $Id: bitmap_io.h 147 2007-04-09 00:44:09Z selinger $ */
+/* bitmap input/output functions */
+
+#ifndef BITMAP_IO_H
+#define BITMAP_IO_H
+
+#include <stdio.h>
+#include <bitmap.h>
+
+/* Note that bitmaps are stored bottom to top, i.e., the first
+ * scanline is the bottom-most one */
+
+extern char* bm_read_error;
+
+int bm_read( FILE* f, double blacklevel, potrace_bitmap_t** bmp );
+void bm_writepbm( FILE* f, potrace_bitmap_t* bm );
+int bm_print( FILE* f, potrace_bitmap_t* bm );
+
+#endif /* BITMAP_IO_H */
diff --git a/potrace/bitops.h b/potrace/bitops.h
new file mode 100644
index 0000000..89f4cb0
--- /dev/null
+++ b/potrace/bitops.h
@@ -0,0 +1,50 @@
+/* Copyright (C) 2001-2007 Peter Selinger.
+ This file is part of Potrace. It is free software and it is covered
+ by the GNU General Public License. See the file COPYING for details. */
+
+/* $Id: bitops.h 147 2007-04-09 00:44:09Z selinger $ */
+
+/* bits.h: this file defines some macros for bit manipulations. We
+ provide a generic implementation */
+
+/* lobit: return the position of the rightmost "1" bit of an int, or
+ 32 if none. hibit: return 1 + the position of the leftmost "1" bit
+ of an int, or 0 if none. Note: these functions work on 32-bit
+ integers. */
+
+#ifndef BITOPS_H
+#define BITOPS_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* generic macros */
+
+static inline unsigned int lobit(unsigned int x) {
+ unsigned int res = 32;
+ while (x & 0xffffff) {
+ x <<= 8;
+ res -= 8;
+ }
+ while (x) {
+ x <<= 1;
+ res -= 1;
+ }
+ return res;
+}
+
+static inline unsigned int hibit(unsigned int x) {
+ unsigned int res = 0;
+ while (x > 0xff) {
+ x >>= 8;
+ res += 8;
+ }
+ while (x) {
+ x >>= 1;
+ res += 1;
+ }
+ return res;
+}
+
+#endif /* BITOPS_H */
diff --git a/potrace/curve.cpp b/potrace/curve.cpp
new file mode 100644
index 0000000..1cb9f03
--- /dev/null
+++ b/potrace/curve.cpp
@@ -0,0 +1,122 @@
+/* Copyright (C) 2001-2007 Peter Selinger.
+ * This file is part of Potrace. It is free software and it is covered
+ * by the GNU General Public License. See the file COPYING for details. */
+
+/* $Id: curve.c 147 2007-04-09 00:44:09Z selinger $ */
+/* private part of the path and curve data structures */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <potracelib.h>
+#include <lists.h>
+#include <curve.h>
+
+#define SAFE_MALLOC( var, n, typ ) \
+ if( ( var = (typ*) malloc( (n)* sizeof(typ) ) ) == NULL ) \
+ goto malloc_error
+
+/* ---------------------------------------------------------------------- */
+/* allocate and free path objects */
+
+path_t* path_new( void )
+{
+ path_t* p = NULL;
+ privpath_t* priv = NULL;
+
+ SAFE_MALLOC( p, 1, path_t );
+ memset( p, 0, sizeof(path_t) );
+ SAFE_MALLOC( priv, 1, privpath_t );
+ memset( priv, 0, sizeof(privpath_t) );
+ p->priv = priv;
+ return p;
+
+malloc_error:
+ free( p );
+ free( priv );
+ return NULL;
+}
+
+
+/* free the members of the given curve structure. Leave errno unchanged. */
+static void privcurve_free_members( privcurve_t* curve )
+{
+ free( curve->tag );
+ free( curve->c );
+ free( curve->vertex );
+ free( curve->alpha );
+ free( curve->alpha0 );
+ free( curve->beta );
+}
+
+
+/* free a path. Leave errno untouched. */
+void path_free( path_t* p )
+{
+ if( p )
+ {
+ if( p->priv )
+ {
+ free( p->priv->pt );
+ free( p->priv->lon );
+ free( p->priv->sums );
+ free( p->priv->po );
+ privcurve_free_members( &p->priv->curve );
+ privcurve_free_members( &p->priv->ocurve );
+ }
+ free( p->priv );
+ /* do not free p->fcurve ! */
+ }
+ free( p );
+}
+
+
+/* free a pathlist, leaving errno untouched. */
+void pathlist_free( path_t* plist )
+{
+ path_t* p;
+
+ list_forall_unlink( p, plist ) {
+ path_free( p );
+ }
+}
+
+
+/* ---------------------------------------------------------------------- */
+/* initialize and finalize curve structures */
+
+typedef dpoint_t dpoint3_t[3];
+
+/* initialize the members of the given curve structure to size m.
+ * Return 0 on success, 1 on error with errno set. */
+int privcurve_init( privcurve_t* curve, int n )
+{
+ memset( curve, 0, sizeof(privcurve_t) );
+ curve->n = n;
+ SAFE_MALLOC( curve->tag, n, int );
+ SAFE_MALLOC( curve->c, n, dpoint3_t );
+ SAFE_MALLOC( curve->vertex, n, dpoint_t );
+ SAFE_MALLOC( curve->alpha, n, double );
+ SAFE_MALLOC( curve->alpha0, n, double );
+ SAFE_MALLOC( curve->beta, n, double );
+ return 0;
+
+malloc_error:
+ free( curve->tag );
+ free( curve->c );
+ free( curve->vertex );
+ free( curve->alpha );
+ free( curve->alpha0 );
+ free( curve->beta );
+ return 1;
+}
+
+
+/* copy private to public curve structure */
+void privcurve_to_curve( privcurve_t* pc, potrace_curve_t* c )
+{
+ c->n = pc->n;
+ c->tag = pc->tag;
+ c->c = pc->c;
+}
diff --git a/potrace/curve.h b/potrace/curve.h
new file mode 100644
index 0000000..655b53a
--- /dev/null
+++ b/potrace/curve.h
@@ -0,0 +1,80 @@
+/* Copyright (C) 2001-2007 Peter Selinger.
+ * This file is part of Potrace. It is free software and it is covered
+ * by the GNU General Public License. See the file COPYING for details. */
+
+#ifndef CURVE_H
+#define CURVE_H
+
+#include <auxiliary.h>
+
+/* vertex is c[1] for tag=POTRACE_CORNER, and the intersection of
+ * .c[-1][2]..c[0] and c[1]..c[2] for tag=POTRACE_CURVETO. alpha is only
+ * defined for tag=POTRACE_CURVETO and is the alpha parameter of the curve:
+ * .c[-1][2]..c[0] = alpha*(.c[-1][2]..vertex), and
+ * c[2]..c[1] = alpha*(c[2]..vertex).
+ * Beta is so that (.beta[i])[.vertex[i],.vertex[i+1]] = .c[i][2].
+ */
+
+struct privcurve_s
+{
+ int n; /* number of segments */
+ int* tag; /* tag[n]: POTRACE_CORNER or POTRACE_CURVETO */
+ dpoint_t( * c )[3]; /* c[n][i]: control points.
+ * c[n][0] is unused for tag[n]=POTRACE_CORNER */
+
+ /* the remainder of this structure is special to privcurve, and is
+ * used in EPS debug output and special EPS "short coding". These
+ * fields are valid only if "alphacurve" is set. */
+ int alphacurve; /* have the following fields been initialized? */
+ dpoint_t* vertex; /* for POTRACE_CORNER, this equals c[1] */
+ double* alpha; /* only for POTRACE_CURVETO */
+ double* alpha0; /* "uncropped" alpha parameter - for debug output only */
+ double* beta;
+};
+typedef struct privcurve_s privcurve_t;
+
+struct sums_s
+{
+ double x;
+ double y;
+ double x2;
+ double xy;
+ double y2;
+};
+typedef struct sums_s sums_t;
+
+/* the path structure is filled in with information about a given path
+ * as it is accumulated and passed through the different stages of the
+ * Potrace algorithm. Backends only need to read the fcurve and fm
+ * fields of this data structure, but debugging backends may read
+ * other fields. */
+struct potrace_privpath_s
+{
+ int len;
+ point_t* pt; /* pt[len]: path as extracted from bitmap */
+ int* lon; /* lon[len]: (i,lon[i]) = longest straight line from i */
+
+ int x0, y0; /* origin for sums */
+ sums_t* sums; /* sums[len+1]: cache for fast summing */
+
+ int m; /* length of optimal polygon */
+ int* po; /* po[m]: optimal polygon */
+
+ privcurve_t curve; /* curve[m]: array of curve elements */
+ privcurve_t ocurve; /* ocurve[om]: array of curve elements */
+ privcurve_t* fcurve; /* final curve: this points to either curve or
+ * ocurve. Do not free this separately. */
+};
+typedef struct potrace_privpath_s potrace_privpath_t;
+
+/* shorter names */
+typedef potrace_privpath_t privpath_t;
+typedef potrace_path_t path_t;
+
+path_t* path_new( void );
+void path_free( path_t* p );
+void pathlist_free( path_t* plist );
+int privcurve_init( privcurve_t* curve, int n );
+void privcurve_to_curve( privcurve_t* pc, potrace_curve_t* c );
+
+#endif /* CURVE_H */
diff --git a/potrace/decompose.cpp b/potrace/decompose.cpp
new file mode 100644
index 0000000..76a7cef
--- /dev/null
+++ b/potrace/decompose.cpp
@@ -0,0 +1,600 @@
+/* Copyright (C) 2001-2007 Peter Selinger.
+ * This file is part of Potrace. It is free software and it is covered
+ * by the GNU General Public License. See the file COPYING for details. */
+
+/* $Id: decompose.c 146 2007-04-09 00:43:46Z selinger $ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+#include <potracelib.h>
+#include <curve.h>
+#include <lists.h>
+#include <auxiliary.h>
+#include <bitmap.h>
+#include <decompose.h>
+#include <progress.h>
+
+/* ---------------------------------------------------------------------- */
+/* auxiliary bitmap manipulations */
+
+/* set the excess padding to 0 */
+static void bm_clearexcess( potrace_bitmap_t* bm )
+{
+ potrace_word mask;
+ int y;
+
+ if( bm->w % BM_WORDBITS != 0 )
+ {
+ mask = BM_ALLBITS << ( BM_WORDBITS - (bm->w % BM_WORDBITS) );
+ for( y = 0; y<bm->h; y++ )
+ {
+ *bm_index( bm, bm->w, y ) &= mask;
+ }
+ }
+}
+
+
+struct bbox_s
+{
+ int x0, x1, y0, y1; /* bounding box */
+};
+typedef struct bbox_s bbox_t;
+
+/* clear the bm, assuming the bounding box is set correctly (faster
+ * than clearing the whole bitmap) */
+static void clear_bm_with_bbox( potrace_bitmap_t* bm, bbox_t* bbox )
+{
+ int imin = (bbox->x0 / BM_WORDBITS);
+ int imax = ( (bbox->x1 + BM_WORDBITS - 1) / BM_WORDBITS );
+ int i, y;
+
+ for( y = bbox->y0; y<bbox->y1; y++ )
+ {
+ for( i = imin; i<imax; i++ )
+ {
+ bm_scanline( bm, y )[i] = 0;
+ }
+ }
+}
+
+
+/* ---------------------------------------------------------------------- */
+/* auxiliary functions */
+
+/* deterministically and efficiently hash (x,y) into a pseudo-random bit */
+static inline int detrand( int x, int y )
+{
+ unsigned int z;
+ static const unsigned char t[256] =
+ {
+ /* non-linear sequence: constant term of inverse in GF(8),
+ * mod x^8+x^4+x^3+x+1 */
+ 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1,
+ 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0,
+ 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1,
+ 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1,
+ 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0,
+ 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0,
+ 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1,
+ 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0,
+ 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1,
+ 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
+ };
+
+ /* 0x04b3e375 and 0x05a8ef93 are chosen to contain every possible
+ * 5-bit sequence */
+ z = ( (0x04b3e375 * x) ^ y ) * 0x05a8ef93;
+ z = t[z & 0xff] ^ t[(z >> 8) & 0xff] ^ t[(z >> 16) & 0xff] ^ t[(z >> 24) & 0xff];
+ return z & 1;
+}
+
+
+/* return the "majority" value of bitmap bm at intersection (x,y). We
+ * assume that the bitmap is balanced at "radius" 1. */
+static int majority( potrace_bitmap_t* bm, int x, int y )
+{
+ int i, a, ct;
+
+ for( i = 2; i<5; i++ ) /* check at "radius" i */
+ {
+ ct = 0;
+ for( a = -i + 1; a<=i - 1; a++ )
+ {
+ ct += BM_GET( bm, x + a, y + i - 1 ) ? 1 : -1;
+ ct += BM_GET( bm, x + i - 1, y + a - 1 ) ? 1 : -1;
+ ct += BM_GET( bm, x + a - 1, y - i ) ? 1 : -1;
+ ct += BM_GET( bm, x - i, y + a ) ? 1 : -1;
+ }
+
+ if( ct>0 )
+ {
+ return 1;
+ }
+ else if( ct<0 )
+ {
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+
+/* ---------------------------------------------------------------------- */
+/* decompose image into paths */
+
+/* efficiently invert bits [x,infty) and [xa,infty) in line y. Here xa
+ * must be a multiple of BM_WORDBITS. */
+static void xor_to_ref( potrace_bitmap_t* bm, int x, int y, int xa )
+{
+ int xhi = x & - BM_WORDBITS;
+ int xlo = x & (BM_WORDBITS - 1); /* = x % BM_WORDBITS */
+ int i;
+
+ if( xhi<xa )
+ {
+ for( i = xhi; i < xa; i += BM_WORDBITS )
+ {
+ *bm_index( bm, i, y ) ^= BM_ALLBITS;
+ }
+ }
+ else
+ {
+ for( i = xa; i < xhi; i += BM_WORDBITS )
+ {
+ *bm_index( bm, i, y ) ^= BM_ALLBITS;
+ }
+ }
+
+ /* note: the following "if" is needed because x86 treats a<<b as
+ * a<<(b&31). I spent hours looking for this bug. */
+ if( xlo )
+ {
+ *bm_index( bm, xhi, y ) ^= ( BM_ALLBITS << (BM_WORDBITS - xlo) );
+ }
+}
+
+
+/* a path is represented as an array of points, which are thought to
+ * lie on the corners of pixels (not on their centers). The path point
+ * (x,y) is the lower left corner of the pixel (x,y). Paths are
+ * represented by the len/pt components of a path_t object (which
+ * also stores other information about the path) */
+
+/* xor the given pixmap with the interior of the given path. Note: the
+ * path must be within the dimensions of the pixmap. */
+static void xor_path( potrace_bitmap_t* bm, path_t* p )
+{
+ int xa, x, y, k, y1;
+
+ if( p->priv->len <= 0 ) /* a path of length 0 is silly, but legal */
+ {
+ return;
+ }
+
+ y1 = p->priv->pt[p->priv->len - 1].y;
+
+ xa = p->priv->pt[0].x & - BM_WORDBITS;
+ for( k = 0; k<p->priv->len; k++ )
+ {
+ x = p->priv->pt[k].x;
+ y = p->priv->pt[k].y;
+
+ if( y != y1 )
+ {
+ /* efficiently invert the rectangle [x,xa] x [y,y1] */
+ xor_to_ref( bm, x, min( y, y1 ), xa );
+ y1 = y;
+ }
+ }
+}
+
+
+/* Find the bounding box of a given path. Path is assumed to be of
+ * non-zero length. */
+static void setbbox_path( bbox_t* bbox, path_t* p )
+{
+ int x, y;
+ int k;
+
+ bbox->y0 = INT_MAX;
+ bbox->y1 = 0;
+ bbox->x0 = INT_MAX;
+ bbox->x1 = 0;
+
+ for( k = 0; k<p->priv->len; k++ )
+ {
+ x = p->priv->pt[k].x;
+ y = p->priv->pt[k].y;
+
+ if( x < bbox->x0 )
+ {
+ bbox->x0 = x;
+ }
+ if( x > bbox->x1 )
+ {
+ bbox->x1 = x;
+ }
+ if( y < bbox->y0 )
+ {
+ bbox->y0 = y;
+ }
+ if( y > bbox->y1 )
+ {
+ bbox->y1 = y;
+ }
+ }
+}
+
+
+/* compute a path in the given pixmap, separating black from white.
+ * Start path at the point (x0,x1), which must be an upper left corner
+ * of the path. Also compute the area enclosed by the path. Return a
+ * new path_t object, or NULL on error (note that a legitimate path
+ * cannot have length 0). Sign is required for correct interpretation
+ * of turnpolicies. */
+static path_t* findpath( potrace_bitmap_t* bm, int x0, int y0, int sign, int turnpolicy )
+{
+ int x, y, dirx, diry, len, size, area;
+ int c, d, tmp;
+ point_t* pt, * pt1;
+ path_t* p = NULL;
+
+ x = x0;
+ y = y0;
+ dirx = 0;
+ diry = -1;
+
+ len = size = 0;
+ pt = NULL;
+ area = 0;
+
+ while( 1 )
+ {
+ /* add point to path */
+ if( len>=size )
+ {
+ size += 100;
+ size = (int) ( 1.3 * size );
+ pt1 = (point_t*) realloc( pt, size * sizeof(point_t) );
+ if( !pt1 )
+ {
+ goto error;
+ }
+ pt = pt1;
+ }
+ pt[len].x = x;
+ pt[len].y = y;
+ len++;
+
+ /* move to next point */
+ x += dirx;
+ y += diry;
+ area += x * diry;
+
+ /* path complete? */
+ if( x==x0 && y==y0 )
+ {
+ break;
+ }
+
+ /* determine next direction */
+ c = BM_GET( bm, x + (dirx + diry - 1) / 2, y + (diry - dirx - 1) / 2 );
+ d = BM_GET( bm, x + (dirx - diry - 1) / 2, y + (diry + dirx - 1) / 2 );
+
+ if( c && !d ) /* ambiguous turn */
+ {
+ if( turnpolicy == POTRACE_TURNPOLICY_RIGHT
+ || (turnpolicy == POTRACE_TURNPOLICY_BLACK && sign == '+')
+ || (turnpolicy == POTRACE_TURNPOLICY_WHITE && sign == '-')
+ || ( turnpolicy == POTRACE_TURNPOLICY_RANDOM && detrand( x, y ) )
+ || ( turnpolicy == POTRACE_TURNPOLICY_MAJORITY && majority( bm, x, y ) )
+ || ( turnpolicy == POTRACE_TURNPOLICY_MINORITY && !majority( bm, x, y ) ) )
+ {
+ tmp = dirx; /* right turn */
+ dirx = diry;
+ diry = -tmp;
+ }
+ else
+ {
+ tmp = dirx; /* left turn */
+ dirx = -diry;
+ diry = tmp;
+ }
+ }
+ else if( c ) /* right turn */
+ {
+ tmp = dirx;
+ dirx = diry;
+ diry = -tmp;
+ }
+ else if( !d ) /* left turn */
+ {
+ tmp = dirx;
+ dirx = -diry;
+ diry = tmp;
+ }
+ } /* while this path */
+
+ /* allocate new path object */
+ p = path_new();
+ if( !p )
+ {
+ goto error;
+ }
+
+ p->priv->pt = pt;
+ p->priv->len = len;
+ p->area = area;
+ p->sign = sign;
+
+ return p;
+
+error:
+ free( pt );
+ return NULL;
+}
+
+
+/* Give a tree structure to the given path list, based on "insideness"
+ * testing. I.e., path A is considered "below" path B if it is inside
+ * path B. The input pathlist is assumed to be ordered so that "outer"
+ * paths occur before "inner" paths. The tree structure is stored in
+ * the "childlist" and "sibling" components of the path_t
+ * structure. The linked list structure is also changed so that
+ * negative path components are listed immediately after their
+ * positive parent. Note: some backends may ignore the tree
+ * structure, others may use it e.g. to group path components. We
+ * assume that in the input, point 0 of each path is an "upper left"
+ * corner of the path, as returned by bm_to_pathlist. This makes it
+ * easy to find an "interior" point. The bm argument should be a
+ * bitmap of the correct size (large enough to hold all the paths),
+ * and will be used as scratch space. Return 0 on success or -1 on
+ * error with errno set. */
+
+static void pathlist_to_tree( path_t* plist, potrace_bitmap_t* bm )
+{
+ path_t* p, * p1;
+ path_t* heap, * heap1;
+ path_t* cur;
+ path_t* head;
+ path_t** hook, ** hook_in, ** hook_out; /* for fast appending to linked list */
+ bbox_t bbox;
+
+ bm_clear( bm, 0 );
+
+ /* save original "next" pointers */
+ list_forall( p, plist ) {
+ p->sibling = p->next;
+ p->childlist = NULL;
+ }
+
+ heap = plist;
+
+ /* the heap holds a list of lists of paths. Use "childlist" field
+ * for outer list, "next" field for inner list. Each of the sublists
+ * is to be turned into a tree. This code is messy, but it is
+ * actually fast. Each path is rendered exactly once. We use the
+ * heap to get a tail recursive algorithm: the heap holds a list of
+ * pathlists which still need to be transformed. */
+
+ while( heap )
+ {
+ /* unlink first sublist */
+ cur = heap;
+ heap = heap->childlist;
+ cur->childlist = NULL;
+
+ /* unlink first path */
+ head = cur;
+ cur = cur->next;
+ head->next = NULL;
+
+ /* render path */
+ xor_path( bm, head );
+ setbbox_path( &bbox, head );
+
+ /* now do insideness test for each element of cur; append it to
+ * head->childlist if it's inside head, else append it to
+ * head->next. */
+ hook_in = &head->childlist;
+ hook_out = &head->next;
+ list_forall_unlink( p, cur ) {
+ if( p->priv->pt[0].y <= bbox.y0 )
+ {
+ list_insert_beforehook( p, hook_out );
+ /* append the remainder of the list to hook_out */
+ *hook_out = cur;
+ break;
+ }
+ if( BM_GET( bm, p->priv->pt[0].x, p->priv->pt[0].y - 1 ) )
+ {
+ list_insert_beforehook( p, hook_in );
+ }
+ else
+ {
+ list_insert_beforehook( p, hook_out );
+ }
+ }
+
+ /* clear bm */
+ clear_bm_with_bbox( bm, &bbox );
+
+ /* now schedule head->childlist and head->next for further
+ * processing */
+ if( head->next )
+ {
+ head->next->childlist = heap;
+ heap = head->next;
+ }
+ if( head->childlist )
+ {
+ head->childlist->childlist = heap;
+ heap = head->childlist;
+ }
+ }
+
+ /* copy sibling structure from "next" to "sibling" component */
+ p = plist;
+ while( p )
+ {
+ p1 = p->sibling;
+ p->sibling = p->next;
+ p = p1;
+ }
+
+ /* reconstruct a new linked list ("next") structure from tree
+ * ("childlist", "sibling") structure. This code is slightly messy,
+ * because we use a heap to make it tail recursive: the heap
+ * contains a list of childlists which still need to be
+ * processed. */
+ heap = plist;
+ if( heap )
+ {
+ heap->next = NULL; /* heap is a linked list of childlists */
+ }
+ plist = NULL;
+ hook = &plist;
+ while( heap )
+ {
+ heap1 = heap->next;
+ for( p = heap; p; p = p->sibling )
+ {
+ /* p is a positive path */
+ /* append to linked list */
+ list_insert_beforehook( p, hook );
+
+ /* go through its children */
+ for( p1 = p->childlist; p1; p1 = p1->sibling )
+ {
+ /* append to linked list */
+ list_insert_beforehook( p1, hook );
+ /* append its childlist to heap, if non-empty */
+ if( p1->childlist )
+ {
+ list_append( path_t, heap1, p1->childlist );
+ }
+ }
+ }
+
+ heap = heap1;
+ }
+
+ return;
+}
+
+
+/* find the next set pixel in a row <= y. Pixels are searched first
+ * left-to-right, then top-down. In other words, (x,y)<(x',y') if y>y'
+ * or y=y' and x<x'. If found, return 0 and store pixel in
+ * (*xp,*yp). Else return 1. Note that this function assumes that
+ * excess bytes have been cleared with bm_clearexcess. */
+static int findnext( potrace_bitmap_t* bm, int* xp, int* yp )
+{
+ int x;
+ int y;
+
+ for( y = *yp; y>=0; y-- )
+ {
+ for( x = 0; x<bm->w; x += BM_WORDBITS )
+ {
+ if( *bm_index( bm, x, y ) )
+ {
+ while( !BM_GET( bm, x, y ) )
+ {
+ x++;
+ }
+
+ /* found */
+ *xp = x;
+ *yp = y;
+ return 0;
+ }
+ }
+ }
+
+ /* not found */
+ return 1;
+}
+
+
+/* Decompose the given bitmap into paths. Returns a linked list of
+ * path_t objects with the fields len, pt, area, sign filled
+ * in. Returns 0 on success with plistp set, or -1 on error with errno
+ * set. */
+
+int bm_to_pathlist( const potrace_bitmap_t* bm,
+ path_t** plistp,
+ const potrace_param_t* param,
+ progress_t* progress )
+{
+ int x;
+ int y;
+ path_t* p;
+ path_t* plist = NULL; /* linked list of path objects */
+ path_t** hook = &plist; /* used to speed up appending to linked list */
+ potrace_bitmap_t* bm1 = NULL;
+ int sign;
+
+ bm1 = bm_dup( bm );
+ if( !bm1 )
+ {
+ goto error;
+ }
+
+ /* be sure the byte padding on the right is set to 0, as the fast
+ * pixel search below relies on it */
+ bm_clearexcess( bm1 );
+
+ /* iterate through components */
+ y = bm1->h - 1;
+ while( findnext( bm1, &x, &y ) == 0 )
+ {
+ /* calculate the sign by looking at the original */
+ sign = BM_GET( bm, x, y ) ? '+' : '-';
+
+ /* calculate the path */
+ p = findpath( bm1, x, y + 1, sign, param->turnpolicy );
+ if( p==NULL )
+ {
+ goto error;
+ }
+
+ /* update buffered image */
+ xor_path( bm1, p );
+
+ /* if it's a turd, eliminate it, else append it to the list */
+ if( p->area <= param->turdsize )
+ {
+ path_free( p );
+ }
+ else
+ {
+ list_insert_beforehook( p, hook );
+ }
+
+ if( bm1->h > 0 ) /* to be sure */
+ {
+ progress_update( 1 - y / (double) bm1->h, progress );
+ }
+ }
+
+ pathlist_to_tree( plist, bm1 );
+ bm_free( bm1 );
+ *plistp = plist;
+
+ progress_update( 1.0, progress );
+
+ return 0;
+
+error:
+ bm_free( bm1 );
+ list_forall_unlink( p, plist ) {
+ path_free( p );
+ }
+ return -1;
+}
diff --git a/potrace/decompose.h b/potrace/decompose.h
new file mode 100644
index 0000000..f6148ba
--- /dev/null
+++ b/potrace/decompose.h
@@ -0,0 +1,18 @@
+/* Copyright (C) 2001-2007 Peter Selinger.
+ * This file is part of Potrace. It is free software and it is covered
+ * by the GNU General Public License. See the file COPYING for details. */
+
+/* $Id: decompose.h 147 2007-04-09 00:44:09Z selinger $ */
+
+#ifndef DECOMPOSE_H
+#define DECOMPOSE_H
+
+#include <potracelib.h>
+#include <progress.h>
+
+int bm_to_pathlist( const potrace_bitmap_t* bm,
+ path_t** plistp,
+ const potrace_param_t* param,
+ progress_t* progress );
+
+#endif /* DECOMPOSE_H */
diff --git a/potrace/greymap.cpp b/potrace/greymap.cpp
new file mode 100644
index 0000000..203fa1a
--- /dev/null
+++ b/potrace/greymap.cpp
@@ -0,0 +1,1108 @@
+/* Copyright (C) 2001-2007 Peter Selinger.
+ * This file is part of Potrace. It is free software and it is covered
+ * by the GNU General Public License. See the file COPYING for details. */
+
+/* $Id: greymap.c 147 2007-04-09 00:44:09Z selinger $ */
+
+/* Routines for manipulating greymaps, including reading pgm files. We
+ * only deal with greymaps of depth 8 bits. */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <cmath>
+
+#include <greymap.h>
+
+#define INTBITS ( 8 * sizeof(int) )
+
+#define mod( a, n ) ( (a)>=(n) ? (a) % (n) : (a)>=0 ? (a) : (n) - 1 - ( -1 - (a) ) % (n) )
+
+static int gm_readbody_pnm( FILE* f, greymap_t** gmp, int magic );
+static int gm_readbody_bmp( FILE* f, greymap_t** gmp );
+
+/* ---------------------------------------------------------------------- */
+/* basic greymap routines */
+
+/* return new un-initialized greymap. NULL with errno on error */
+
+greymap_t* gm_new( int w, int h )
+{
+ greymap_t* gm;
+ int errno_save;
+
+ gm = (greymap_t*) malloc( sizeof(greymap_t) );
+ if( !gm )
+ {
+ return NULL;
+ }
+ gm->w = w;
+ gm->h = h;
+ gm->map = (signed short int*) malloc( w * h * sizeof(signed short int) );
+ if( !gm->map )
+ {
+ errno_save = errno;
+ free( gm );
+ errno = errno_save;
+ return NULL;
+ }
+ return gm;
+}
+
+
+/* free the given greymap */
+void gm_free( greymap_t* gm )
+{
+ if( gm )
+ {
+ free( gm->map );
+ }
+ free( gm );
+}
+
+
+/* duplicate the given greymap. Return NULL on error with errno set. */
+greymap_t* gm_dup( greymap_t* gm )
+{
+ greymap_t* gm1 = gm_new( gm->w, gm->h );
+
+ if( !gm1 )
+ {
+ return NULL;
+ }
+ memcpy( gm1->map, gm->map, gm->w * gm->h * 2 );
+ return gm1;
+}
+
+
+/* clear the given greymap to color b. */
+void gm_clear( greymap_t* gm, int b )
+{
+ int i;
+
+ if( b==0 )
+ {
+ memset( gm->map, 0, gm->w * gm->h * 2 );
+ }
+ else
+ {
+ for( i = 0; i<gm->w * gm->h; i++ )
+ {
+ gm->map[i] = b;
+ }
+ }
+}
+
+
+/* ---------------------------------------------------------------------- */
+/* routines for reading pnm streams */
+
+/* read next character after whitespace and comments. Return EOF on
+ * end of file or error. */
+static int fgetc_ws( FILE* f )
+{
+ int c;
+
+ while( 1 )
+ {
+ c = fgetc( f );
+ if( c=='#' )
+ {
+ while( 1 )
+ {
+ c = fgetc( f );
+ if( c=='\n' || c==EOF )
+ {
+ break;
+ }
+ }
+ }
+ /* space, tab, line feed, carriage return, form-feed */
+ if( c!=' ' && c!='\t' && c!='\r' && c!='\n' && c!=12 )
+ {
+ return c;
+ }
+ }
+}
+
+
+/* skip whitespace and comments, then read a non-negative decimal
+ * number from a stream. Return -1 on EOF. Tolerate other errors (skip
+ * bad characters). Do not the read any characters following the
+ * number (put next character back into the stream) */
+
+static int readnum( FILE* f )
+{
+ int c;
+ int acc;
+
+ /* skip whitespace and comments */
+ while( 1 )
+ {
+ c = fgetc_ws( f );
+ if( c==EOF )
+ {
+ return -1;
+ }
+ if( c>='0' && c<='9' )
+ {
+ break;
+ }
+ }
+
+ /* first digit is already in c */
+ acc = c - '0';
+ while( 1 )
+ {
+ c = fgetc( f );
+ if( c==EOF )
+ {
+ break;
+ }
+ if( c<'0' || c>'9' )
+ {
+ ungetc( c, f );
+ break;
+ }
+ acc *= 10;
+ acc += c - '0';
+ }
+
+ return acc;
+}
+
+
+/* similar to readnum, but read only a single 0 or 1, and do not read
+ * any characters after it. */
+
+static int readbit( FILE* f )
+{
+ int c;
+
+ /* skip whitespace and comments */
+ while( 1 )
+ {
+ c = fgetc_ws( f );
+ if( c==EOF )
+ {
+ return -1;
+ }
+ if( c>='0' && c<='1' )
+ {
+ break;
+ }
+ }
+
+ return c - '0';
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+/* read a PNM stream: P1-P6 format (see pnm(5)), or a BMP stream, and
+ * convert the output to a greymap. Return greymap in *gmp. Return 0
+ * on success, -1 on error with errno set, -2 on bad file format (with
+ * error message in gm_read_error), and 1 on premature end of file, -3
+ * on empty file (including files with only whitespace and comments),
+ * -4 if wrong magic number. If the return value is >=0, *gmp is
+ * valid. */
+
+const char* gm_read_error = NULL;
+
+int gm_read( FILE* f, greymap_t** gmp )
+{
+ int magic[2];
+
+ /* read magic number. We ignore whitespace and comments before the
+ * magic, for the benefit of concatenated files in P1-P3 format.
+ * Multiple P1-P3 images in a single file are not formally allowed
+ * by the PNM standard, but there is no harm in being lenient. */
+
+ magic[0] = fgetc_ws( f );
+ if( magic[0] == EOF )
+ {
+ /* files which contain only comments and whitespace count as "empty" */
+ return -3;
+ }
+ magic[1] = fgetc( f );
+ if( magic[0] == 'P' && magic[1] >= '1' && magic[1] <= '6' )
+ {
+ return gm_readbody_pnm( f, gmp, magic[1] );
+ }
+ if( magic[0] == 'B' && magic[1] == 'M' )
+ {
+ return gm_readbody_bmp( f, gmp );
+ }
+ return -4;
+}
+
+
+/* ---------------------------------------------------------------------- */
+/* read PNM format */
+
+/* read PNM stream after magic number. Return values as for gm_read */
+static int gm_readbody_pnm( FILE* f, greymap_t** gmp, int magic )
+{
+ greymap_t* gm;
+ int x, y, i, j, b, b1, sum;
+ int bpr; /* bytes per row (as opposed to 4*gm->c) */
+ int w, h, max;
+
+ gm = NULL;
+
+ w = readnum( f );
+ if( w<0 )
+ {
+ goto format_error;
+ }
+
+ h = readnum( f );
+ if( h<0 )
+ {
+ goto format_error;
+ }
+
+ /* allocate greymap */
+ gm = gm_new( w, h );
+ if( !gm )
+ {
+ return -1;
+ }
+
+ /* zero it out */
+ gm_clear( gm, 0 );
+
+ switch( magic )
+ {
+ default:
+ /* not reached */
+ goto format_error;
+
+ case '1':
+ /* read P1 format: PBM ascii */
+
+ for( y = h - 1; y>=0; y-- )
+ {
+ for( x = 0; x<w; x++ )
+ {
+ b = readbit( f );
+ if( b<0 )
+ {
+ goto eof;
+ }
+ GM_UPUT( gm, x, y, b ? 0 : 255 );
+ }
+ }
+
+ break;
+
+ case '2':
+ /* read P2 format: PGM ascii */
+
+ max = readnum( f );
+ if( max<1 )
+ {
+ goto format_error;
+ }
+
+ for( y = h - 1; y>=0; y-- )
+ {
+ for( x = 0; x<w; x++ )
+ {
+ b = readnum( f );
+ if( b<0 )
+ {
+ goto eof;
+ }
+ GM_UPUT( gm, x, y, b * 255 / max );
+ }
+ }
+
+ break;
+
+ case '3':
+ /* read P3 format: PPM ascii */
+
+ max = readnum( f );
+ if( max<1 )
+ {
+ goto format_error;
+ }
+
+ for( y = h - 1; y>=0; y-- )
+ {
+ for( x = 0; x<w; x++ )
+ {
+ sum = 0;
+ for( i = 0; i<3; i++ )
+ {
+ b = readnum( f );
+ if( b<0 )
+ {
+ goto eof;
+ }
+ sum += b;
+ }
+
+ GM_UPUT( gm, x, y, sum * (255 / 3) / max );
+ }
+ }
+
+ break;
+
+ case '4':
+ /* read P4 format: PBM raw */
+
+ b = fgetc( f ); /* read single white-space character after height */
+ if( b==EOF )
+ {
+ goto format_error;
+ }
+
+ bpr = (w + 7) / 8;
+
+ for( y = h - 1; y>=0; y-- )
+ {
+ for( i = 0; i<bpr; i++ )
+ {
+ b = fgetc( f );
+ if( b==EOF )
+ {
+ goto eof;
+ }
+ for( j = 0; j<8; j++ )
+ {
+ GM_PUT( gm, i * 8 + j, y, b & (0x80 >> j) ? 0 : 255 );
+ }
+ }
+ }
+
+ break;
+
+ case '5':
+ /* read P5 format: PGM raw */
+
+ max = readnum( f );
+ if( max<1 )
+ {
+ goto format_error;
+ }
+
+ b = fgetc( f ); /* read single white-space character after max */
+ if( b==EOF )
+ {
+ goto format_error;
+ }
+
+ for( y = h - 1; y>=0; y-- )
+ {
+ for( x = 0; x<w; x++ )
+ {
+ b = fgetc( f );
+ if( b==EOF )
+ goto eof;
+ if( max>=256 )
+ {
+ b <<= 8;
+ b1 = fgetc( f );
+ if( b1==EOF )
+ goto eof;
+ b |= b1;
+ }
+ GM_UPUT( gm, x, y, b * 255 / max );
+ }
+ }
+
+ break;
+
+ case '6':
+ /* read P6 format: PPM raw */
+
+ max = readnum( f );
+ if( max<1 )
+ {
+ goto format_error;
+ }
+
+ b = fgetc( f ); /* read single white-space character after max */
+ if( b==EOF )
+ {
+ goto format_error;
+ }
+
+ for( y = h - 1; y>=0; y-- )
+ {
+ for( x = 0; x<w; x++ )
+ {
+ sum = 0;
+ for( i = 0; i<3; i++ )
+ {
+ b = fgetc( f );
+ if( b==EOF )
+ {
+ goto eof;
+ }
+ if( max>=256 )
+ {
+ b <<= 8;
+ b1 = fgetc( f );
+ if( b1==EOF )
+ goto eof;
+ b |= b1;
+ }
+ sum += b;
+ }
+
+ GM_UPUT( gm, x, y, sum * (255 / 3) / max );
+ }
+ }
+
+ break;
+ }
+
+ *gmp = gm;
+ return 0;
+
+eof:
+ *gmp = gm;
+ return 1;
+
+format_error:
+ gm_free( gm );
+ if( magic == '1' || magic == '4' )
+ {
+ gm_read_error = "invalid pbm file";
+ }
+ else if( magic == '2' || magic == '5' )
+ {
+ gm_read_error = "invalid pgm file";
+ }
+ else
+ {
+ gm_read_error = "invalid ppm file";
+ }
+ return -2;
+}
+
+
+/* ---------------------------------------------------------------------- */
+/* read BMP format */
+
+struct bmp_info_s
+{
+ unsigned int FileSize;
+ unsigned int reserved;
+ unsigned int DataOffset;
+ unsigned int InfoSize;
+ unsigned int w; /* width */
+ unsigned int h; /* height */
+ unsigned int Planes;
+ unsigned int bits; /* bits per sample */
+ unsigned int comp; /* compression mode */
+ unsigned int ImageSize;
+ unsigned int XpixelsPerM;
+ unsigned int YpixelsPerM;
+ unsigned int ncolors; /* number of colors in palette */
+ unsigned int ColorsImportant;
+ unsigned int ctbits; /* sample size for color table */
+};
+typedef struct bmp_info_s bmp_info_t;
+
+/* auxiliary */
+
+static int bmp_count = 0; /* counter for byte padding */
+static int bmp_pos = 0; /* counter from start of BMP data */
+
+/* read n-byte little-endian integer. Return 1 on EOF or error, else
+ * 0. Assume n<=4. */
+static int bmp_readint( FILE* f, int n, unsigned int* p )
+{
+ int i;
+ unsigned int sum = 0;
+ int b;
+
+ for( i = 0; i<n; i++ )
+ {
+ b = fgetc( f );
+ if( b==EOF )
+ {
+ return 1;
+ }
+ sum += b << (8 * i);
+ }
+
+ bmp_count += n;
+ bmp_pos += n;
+ *p = sum;
+ return 0;
+}
+
+
+/* reset padding boundary */
+static void bmp_pad_reset( void )
+{
+ bmp_count = 0;
+}
+
+
+/* read padding bytes to 4-byte boundary. Return 1 on EOF or error,
+ * else 0. */
+static int bmp_pad( FILE* f )
+{
+ int c, i, b;
+
+ c = (-bmp_count) & 3;
+ for( i = 0; i<c; i++ )
+ {
+ b = fgetc( f );
+ if( b==EOF )
+ {
+ return 1;
+ }
+ }
+
+ bmp_pos += c;
+ bmp_count = 0;
+ return 0;
+}
+
+
+/* forward to the new file position. Return 1 on EOF or error, else 0 */
+static int bmp_forward( FILE* f, int pos )
+{
+ int b;
+
+ while( bmp_pos < pos )
+ {
+ b = fgetc( f );
+ if( b==EOF )
+ {
+ return 1;
+ }
+ bmp_pos++;
+ bmp_count++;
+ }
+
+ return 0;
+}
+
+
+#define TRY( x ) if( x ) \
+ goto try_error
+#define TRY_EOF( x ) if( x ) \
+ goto eof
+
+/* read BMP stream after magic number. Return values as for gm_read.
+ * We choose to be as permissive as possible, since there are many
+ * programs out there which produce BMP. For instance, ppmtobmp can
+ * produce codings with anywhere from 1-8 or 24 bits per sample,
+ * although most specifications only allow 1,4,8,24,32. We can also
+ * read both the old and new OS/2 BMP formats in addition to the
+ * Windows BMP format. */
+static int gm_readbody_bmp( FILE* f, greymap_t** gmp )
+{
+ bmp_info_t bmpinfo;
+ int* coltable;
+ unsigned int b, c;
+ unsigned int i, j;
+ greymap_t* gm;
+ unsigned int x, y;
+ int col[2];
+ unsigned int bitbuf;
+ unsigned int n;
+
+ gm_read_error = NULL;
+ gm = NULL;
+ coltable = NULL;
+
+ bmp_pos = 2; /* set file position */
+
+ /* file header (minus magic number) */
+ TRY( bmp_readint( f, 4, &bmpinfo.FileSize ) );
+ TRY( bmp_readint( f, 4, &bmpinfo.reserved ) );
+ TRY( bmp_readint( f, 4, &bmpinfo.DataOffset ) );
+
+ /* info header */
+ TRY( bmp_readint( f, 4, &bmpinfo.InfoSize ) );
+ if( bmpinfo.InfoSize == 40 || bmpinfo.InfoSize == 64 )
+ {
+ /* Windows or new OS/2 format */
+ bmpinfo.ctbits = 32; /* sample size in color table */
+ TRY( bmp_readint( f, 4, &bmpinfo.w ) );
+ TRY( bmp_readint( f, 4, &bmpinfo.h ) );
+ TRY( bmp_readint( f, 2, &bmpinfo.Planes ) );
+ TRY( bmp_readint( f, 2, &bmpinfo.bits ) );
+ TRY( bmp_readint( f, 4, &bmpinfo.comp ) );
+ TRY( bmp_readint( f, 4, &bmpinfo.ImageSize ) );
+ TRY( bmp_readint( f, 4, &bmpinfo.XpixelsPerM ) );
+ TRY( bmp_readint( f, 4, &bmpinfo.YpixelsPerM ) );
+ TRY( bmp_readint( f, 4, &bmpinfo.ncolors ) );
+ TRY( bmp_readint( f, 4, &bmpinfo.ColorsImportant ) );
+ }
+ else if( bmpinfo.InfoSize == 12 )
+ {
+ /* old OS/2 format */
+ bmpinfo.ctbits = 24; /* sample size in color table */
+ TRY( bmp_readint( f, 2, &bmpinfo.w ) );
+ TRY( bmp_readint( f, 2, &bmpinfo.h ) );
+ TRY( bmp_readint( f, 2, &bmpinfo.Planes ) );
+ TRY( bmp_readint( f, 2, &bmpinfo.bits ) );
+ bmpinfo.comp = 0;
+ bmpinfo.ncolors = 0;
+ }
+ else
+ {
+ goto format_error;
+ }
+
+ /* forward to color table (i.e., if bmpinfo.InfoSize == 64) */
+ TRY( bmp_forward( f, 14 + bmpinfo.InfoSize ) );
+
+ if( bmpinfo.Planes != 1 )
+ {
+ gm_read_error = "cannot handle bmp planes";
+ goto format_error; /* can't handle planes */
+ }
+
+ if( bmpinfo.ncolors == 0 )
+ {
+ bmpinfo.ncolors = 1 << bmpinfo.bits;
+ }
+
+ /* color table, present only if bmpinfo.bits <= 8. */
+ if( bmpinfo.bits <= 8 )
+ {
+ coltable = (int*) malloc( bmpinfo.ncolors * sizeof(int) );
+ if( !coltable )
+ {
+ goto std_error;
+ }
+
+ /* NOTE: since we are reading a greymap, we can immediately convert
+ * the color table entries to grey values. */
+ for( i = 0; i<bmpinfo.ncolors; i++ )
+ {
+ TRY( bmp_readint( f, bmpinfo.ctbits / 8, &c ) );
+ c = ( (c >> 16) & 0xff ) + ( (c >> 8) & 0xff ) + (c & 0xff);
+ coltable[i] = c / 3;
+ }
+ }
+
+ /* forward to data */
+ if( bmpinfo.InfoSize != 12 ) /* not old OS/2 format */
+ {
+ TRY( bmp_forward( f, bmpinfo.DataOffset ) );
+ }
+
+ /* allocate greymap */
+ gm = gm_new( bmpinfo.w, bmpinfo.h );
+ if( !gm )
+ {
+ goto std_error;
+ }
+
+ /* zero it out */
+ gm_clear( gm, 0 );
+
+ switch( bmpinfo.bits + 0x100 * bmpinfo.comp )
+ {
+ default:
+ goto format_error;
+ break;
+
+ case 0x001: /* monochrome palette */
+
+ /* raster data */
+ for( y = 0; y<bmpinfo.h; y++ )
+ {
+ bmp_pad_reset();
+ for( i = 0; 8 * i<bmpinfo.w; i++ )
+ {
+ TRY_EOF( bmp_readint( f, 1, &b ) );
+ for( j = 0; j<8; j++ )
+ {
+ GM_PUT( gm, i * 8 + j, y, b & (0x80 >> j) ? coltable[1] : coltable[0] );
+ }
+ }
+
+ TRY( bmp_pad( f ) );
+ }
+
+ break;
+
+ case 0x002: /* 2-bit to 8-bit palettes */
+ case 0x003:
+ case 0x004:
+ case 0x005:
+ case 0x006:
+ case 0x007:
+ case 0x008:
+ for( y = 0; y<bmpinfo.h; y++ )
+ {
+ bmp_pad_reset();
+ bitbuf = 0; /* bit buffer: bits in buffer are high-aligned */
+ n = 0; /* number of bits currently in bitbuffer */
+ for( x = 0; x<bmpinfo.w; x++ )
+ {
+ if( n < bmpinfo.bits )
+ {
+ TRY_EOF( bmp_readint( f, 1, &b ) );
+ bitbuf |= b << (INTBITS - 8 - n);
+ n += 8;
+ }
+ b = bitbuf >> (INTBITS - bmpinfo.bits);
+ bitbuf <<= bmpinfo.bits;
+ n -= bmpinfo.bits;
+ GM_UPUT( gm, x, y, coltable[b] );
+ }
+
+ TRY( bmp_pad( f ) );
+ }
+
+ break;
+
+ case 0x010: /* 16-bit encoding */
+
+ /* can't do this format because it is not well-documented and I
+ * don't have any samples */
+ gm_read_error = "cannot handle bmp 16-bit coding";
+ goto format_error;
+ break;
+
+ case 0x018: /* 24-bit encoding */
+ case 0x020: /* 32-bit encoding */
+ for( y = 0; y<bmpinfo.h; y++ )
+ {
+ bmp_pad_reset();
+ for( x = 0; x<bmpinfo.w; x++ )
+ {
+ TRY_EOF( bmp_readint( f, bmpinfo.bits / 8, &c ) );
+ c = ( (c >> 16) & 0xff ) + ( (c >> 8) & 0xff ) + (c & 0xff);
+ GM_UPUT( gm, x, y, c / 3 );
+ }
+
+ TRY( bmp_pad( f ) );
+ }
+
+ break;
+
+ case 0x204: /* 4-bit runlength compressed encoding (RLE4) */
+ x = 0;
+ y = 0;
+ while( 1 )
+ {
+ TRY_EOF( bmp_readint( f, 1, &b ) ); /* opcode */
+ TRY_EOF( bmp_readint( f, 1, &c ) ); /* argument */
+ if( b>0 )
+ {
+ /* repeat count */
+ col[0] = coltable[(c >> 4) & 0xf];
+ col[1] = coltable[c & 0xf];
+ for( i = 0; i<b && x<bmpinfo.w; i++ )
+ {
+ if( x>=bmpinfo.w )
+ {
+ x = 0;
+ y++;
+ }
+ if( y>=bmpinfo.h )
+ {
+ break;
+ }
+ GM_UPUT( gm, x, y, col[i & 1] );
+ x++;
+ }
+ }
+ else if( c == 0 )
+ {
+ /* end of line */
+ y++;
+ x = 0;
+ }
+ else if( c == 1 )
+ {
+ /* end of greymap */
+ break;
+ }
+ else if( c == 2 )
+ {
+ /* "delta": skip pixels in x and y directions */
+ TRY_EOF( bmp_readint( f, 1, &b ) ); /* x offset */
+ TRY_EOF( bmp_readint( f, 1, &c ) ); /* y offset */
+ x += b;
+ y += c;
+ }
+ else
+ {
+ /* verbatim segment */
+ for( i = 0; i<c; i++ )
+ {
+ if( (i & 1)==0 )
+ {
+ TRY_EOF( bmp_readint( f, 1, &b ) );
+ }
+ if( x>=bmpinfo.w )
+ {
+ x = 0;
+ y++;
+ }
+ if( y>=bmpinfo.h )
+ {
+ break;
+ }
+ GM_PUT( gm, x, y, coltable[( b >> ( 4 - 4 * (i & 1) ) ) & 0xf] );
+ x++;
+ }
+
+ if( (c + 1) & 2 )
+ {
+ /* pad to 16-bit boundary */
+ TRY_EOF( bmp_readint( f, 1, &b ) );
+ }
+ }
+ }
+
+ break;
+
+ case 0x108: /* 8-bit runlength compressed encoding (RLE8) */
+ x = 0;
+ y = 0;
+ while( 1 )
+ {
+ TRY_EOF( bmp_readint( f, 1, &b ) ); /* opcode */
+ TRY_EOF( bmp_readint( f, 1, &c ) ); /* argument */
+ if( b>0 )
+ {
+ /* repeat count */
+ for( i = 0; i<b; i++ )
+ {
+ if( x>=bmpinfo.w )
+ {
+ x = 0;
+ y++;
+ }
+ if( y>=bmpinfo.h )
+ {
+ break;
+ }
+ GM_UPUT( gm, x, y, coltable[c] );
+ x++;
+ }
+ }
+ else if( c == 0 )
+ {
+ /* end of line */
+ y++;
+ x = 0;
+ }
+ else if( c == 1 )
+ {
+ /* end of greymap */
+ break;
+ }
+ else if( c == 2 )
+ {
+ /* "delta": skip pixels in x and y directions */
+ TRY_EOF( bmp_readint( f, 1, &b ) ); /* x offset */
+ TRY_EOF( bmp_readint( f, 1, &c ) ); /* y offset */
+ x += b;
+ y += c;
+ }
+ else
+ {
+ /* verbatim segment */
+ for( i = 0; i<c; i++ )
+ {
+ TRY_EOF( bmp_readint( f, 1, &b ) );
+ if( x>=bmpinfo.w )
+ {
+ x = 0;
+ y++;
+ }
+ if( y>=bmpinfo.h )
+ {
+ break;
+ }
+ GM_PUT( gm, x, y, coltable[b] );
+ x++;
+ }
+
+ if( c & 1 )
+ {
+ /* pad input to 16-bit boundary */
+ TRY_EOF( bmp_readint( f, 1, &b ) );
+ }
+ }
+ }
+
+ break;
+ } /* switch */
+
+ /* skip any potential junk after the data section, but don't
+ * complain in case EOF is encountered */
+ bmp_forward( f, bmpinfo.FileSize );
+
+ free( coltable );
+ *gmp = gm;
+ return 0;
+
+eof:
+ free( coltable );
+ *gmp = gm;
+ return 1;
+
+format_error:
+try_error:
+ free( coltable );
+ free( gm );
+ if( !gm_read_error )
+ {
+ gm_read_error = "invalid bmp file";
+ }
+ return -2;
+
+std_error:
+ free( coltable );
+ free( gm );
+ return -1;
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+/* write a pgm stream, either P2 or (if raw != 0) P5 format. Include
+ * one-line comment if non-NULL. Mode determines how out-of-range
+ * color values are converted. Gamma is the desired gamma correction,
+ * if any (set to 2.2 if the image is to look optimal on a CRT monitor,
+ * 2.8 for LCD). Set to 1.0 for no gamma correction */
+
+int gm_writepgm( FILE* f, greymap_t* gm, char* comment, int raw, int mode, double gamma )
+{
+ int x, y, v;
+ int gammatable[256];
+
+ /* prepare gamma correction lookup table */
+ if( gamma != 1.0 )
+ {
+ gammatable[0] = 0;
+ for( v = 1; v<256; v++ )
+ {
+ gammatable[v] = (int) ( 255 * exp( log( v / 255.0 ) / gamma ) + 0.5 );
+ }
+ }
+ else
+ {
+ for( v = 0; v<256; v++ )
+ {
+ gammatable[v] = v;
+ }
+ }
+
+ fprintf( f, raw ? "P5\n" : "P2\n" );
+ if( comment && *comment )
+ {
+ fprintf( f, "# %s\n", comment );
+ }
+ fprintf( f, "%d %d 255\n", gm->w, gm->h );
+ for( y = gm->h - 1; y>=0; y-- )
+ {
+ for( x = 0; x<gm->w; x++ )
+ {
+ v = GM_UGET( gm, x, y );
+ if( mode == GM_MODE_NONZERO )
+ {
+ if( v > 255 )
+ {
+ v = 510 - v;
+ }
+ if( v < 0 )
+ {
+ v = 0;
+ }
+ }
+ else if( mode == GM_MODE_ODD )
+ {
+ v = mod( v, 510 );
+ if( v > 255 )
+ {
+ v = 510 - v;
+ }
+ }
+ else if( mode == GM_MODE_POSITIVE )
+ {
+ if( v < 0 )
+ {
+ v = 0;
+ }
+ else if( v > 255 )
+ {
+ v = 255;
+ }
+ }
+ else if( mode == GM_MODE_NEGATIVE )
+ {
+ v = 510 - v;
+ if( v < 0 )
+ {
+ v = 0;
+ }
+ else if( v > 255 )
+ {
+ v = 255;
+ }
+ }
+ v = gammatable[v];
+
+ if( raw )
+ {
+ fputc( v, f );
+ }
+ else
+ {
+ fprintf( f, x == gm->w - 1 ? "%d\n" : "%d ", v );
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+/* ---------------------------------------------------------------------- */
+/* output - for primitive debugging purposes only! */
+
+/* print greymap to screen */
+int gm_print( FILE* f, greymap_t* gm )
+{
+ int x, y;
+ int xx, yy;
+ int d, t;
+ int sw, sh;
+
+ sw = gm->w < 79 ? gm->w : 79;
+ sh = gm->w < 79 ? gm->h : gm->h * sw * 44 / (79 * gm->w);
+
+ for( yy = sh - 1; yy>=0; yy-- )
+ {
+ for( xx = 0; xx<sw; xx++ )
+ {
+ d = 0;
+ t = 0;
+ for( x = xx * gm->w / sw; x<(xx + 1) * gm->w / sw; x++ )
+ {
+ for( y = yy * gm->h / sh; y<(yy + 1) * gm->h / sh; y++ )
+ {
+ d += GM_GET( gm, x, y );
+ t += 256;
+ }
+ }
+
+ if( t )
+ fputc( "*#=- "[5 * d / t], f ); /* what a cute trick :) */
+ }
+
+ fputc( '\n', f );
+ }
+
+ return 0;
+}
diff --git a/potrace/greymap.h b/potrace/greymap.h
new file mode 100644
index 0000000..dcb926a
--- /dev/null
+++ b/potrace/greymap.h
@@ -0,0 +1,58 @@
+/* Copyright (C) 2001-2007 Peter Selinger.
+ This file is part of Potrace. It is free software and it is covered
+ by the GNU General Public License. See the file COPYING for details. */
+
+/* $Id: greymap.h 147 2007-04-09 00:44:09Z selinger $ */
+
+#ifndef PGM_H
+#define PGM_H
+
+#include <stdio.h>
+
+/* internal format for greymaps. Note: in this format, rows are
+ ordered from bottom to top. The pixels in each row are given from
+ left to right. */
+
+struct greymap_s {
+ int w; /* width, in pixels */
+ int h; /* height, in pixels */
+ signed short int *map; /* raw data, w*h values */
+};
+typedef struct greymap_s greymap_t;
+
+/* macros for accessing pixel at index (x,y). Note that the origin is
+ in the *lower* left corner. U* macros omit the bounds check. */
+
+#define gm_index(gm, x, y) (&(gm)->map[(x)+(y)*(gm)->w])
+#define gm_safe(gm, x, y) ((int)(x)>=0 && (int)(x)<(gm)->w && (int)(y)>=0 && (int)(y)<(gm)->h)
+#define gm_bound(x, m) ((x)<0 ? 0 : (x)>=(m) ? (m)-1 : (x))
+#define GM_UGET(gm, x, y) (*gm_index(gm, x, y))
+#define GM_UINC(gm, x, y, b) (*gm_index(gm, x, y) += (short int)(b))
+#define GM_UINV(gm, x, y) (*gm_index(gm, x, y) = 255 - *gm_index(gm, x, y))
+#define GM_UPUT(gm, x, y, b) (*gm_index(gm, x, y) = (short int)(b))
+#define GM_GET(gm, x, y) (gm_safe(gm, x, y) ? GM_UGET(gm, x, y) : 0)
+#define GM_INC(gm, x, y, b) (gm_safe(gm, x, y) ? GM_UINC(gm, x, y, b) : 0)
+#define GM_INV(gm, x, y) (gm_safe(gm, x, y) ? GM_UINV(gm, x, y) : 0)
+#define GM_PUT(gm, x, y, b) (gm_safe(gm, x, y) ? GM_UPUT(gm, x, y, b) : 0)
+#define GM_BGET(gm, x, y) GM_UGET(gm, gm_bound(x, gm->w), gm_bound(y, gm->h))
+
+/* modes for cutting off out-of-range values. The following names
+ refer to winding numbers. I.e., make a pixel black if winding
+ number is nonzero, odd, or positive, respectively. We assume that 0
+ winding number corresponds to white (255). */
+#define GM_MODE_NONZERO 1
+#define GM_MODE_ODD 2
+#define GM_MODE_POSITIVE 3
+#define GM_MODE_NEGATIVE 4
+
+extern const char *gm_read_error;
+
+greymap_t *gm_new(int w, int h);
+greymap_t *gm_dup(greymap_t *gm);
+void gm_free(greymap_t *gm);
+void gm_clear(greymap_t *gm, int b);
+int gm_read(FILE *f, greymap_t **gmp);
+int gm_writepgm(FILE *f, greymap_t *gm, char *comment, int raw, int mode, double gamma);
+int gm_print(FILE *f, greymap_t *gm);
+
+#endif /* PGM_H */
diff --git a/potrace/lists.h b/potrace/lists.h
new file mode 100644
index 0000000..5347820
--- /dev/null
+++ b/potrace/lists.h
@@ -0,0 +1,293 @@
+/* Copyright (C) 2001-2007 Peter Selinger.
+ * This file is part of Potrace. It is free software and it is covered
+ * by the GNU General Public License. See the file COPYING for details. */
+
+/* $Id: lists.h 147 2007-04-09 00:44:09Z selinger $ */
+
+#ifndef _PS_LISTS_H
+#define _PS_LISTS_H
+
+/* here we define some general list macros. Because they are macros,
+ * they should work on any datatype with a "->next" component. Some of
+ * them use a "hook". If elt and list are of type t* then hook is of
+ * type t**. A hook stands for an insertion point in the list, i.e.,
+ * either before the first element, or between two elements, or after
+ * the last element. If an operation "sets the hook" for an element,
+ * then the hook is set to just before the element. One can insert
+ * something at a hook. One can also unlink at a hook: this means,
+ * unlink the element just after the hook. By "to unlink", we mean the
+ * element is removed from the list, but not deleted. Thus, it and its
+ * components still need to be freed. */
+
+/* Note: these macros are somewhat experimental. Only the ones that
+ * are actually *used* have been tested. So be careful to test any
+ * that you use. Looking at the output of the preprocessor, "gcc -E"
+ * (possibly piped though "indent"), might help too. Also: these
+ * macros define some internal (local) variables that start with
+ * "_". */
+
+/* we enclose macro definitions whose body consists of more than one
+ * statement in MACRO_BEGIN and MACRO_END, rather than '{' and '}'. The
+ * reason is that we want to be able to use the macro in a context
+ * such as "if (...) macro(...); else ...". If we didn't use this obscure
+ * trick, we'd have to omit the ";" in such cases. */
+
+#define MACRO_BEGIN do {
+#define MACRO_END } while( 0 )
+
+/* ---------------------------------------------------------------------- */
+/* macros for singly-linked lists */
+
+/* traverse list. At the end, elt is set to NULL. */
+#define list_forall( elt, list ) for( elt = list; elt!=NULL; elt = elt->next )
+
+/* set elt to the first element of list satisfying boolean condition
+ * c, or NULL if not found */
+#define list_find( elt, list, c ) \
+ MACRO_BEGIN list_forall( elt, list ) if( c ) \
+ break;MACRO_END
+
+/* like forall, except also set hook for elt. */
+#define list_forall2( elt, list, hook ) \
+ for( elt = list, hook = &list; elt!=NULL; hook = &elt->next, elt = elt->next )
+
+/* same as list_find, except also set hook for elt. */
+#define list_find2( elt, list, c, hook ) \
+ MACRO_BEGIN list_forall2( elt, list, hook ) if( c ) \
+ break;MACRO_END
+
+/* same, except only use hook. */
+#define _list_forall_hook( list, hook ) \
+ for( hook = &list; *hook!=NULL; hook = &(*hook)->next )
+
+/* same, except only use hook. Note: c may only refer to *hook, not elt. */
+#define _list_find_hook( list, c, hook ) \
+ MACRO_BEGIN _list_forall_hook( list, hook ) if( c ) \
+ break;MACRO_END
+
+/* insert element after hook */
+#define list_insert_athook( elt, hook ) \
+ MACRO_BEGIN elt->next = *hook; *hook = elt; MACRO_END
+
+/* insert element before hook */
+#define list_insert_beforehook( elt, hook ) \
+ MACRO_BEGIN elt->next = *hook; *hook = elt; hook = &elt->next; MACRO_END
+
+/* unlink element after hook, let elt be unlinked element, or NULL.
+ * hook remains. */
+#define list_unlink_athook( list, elt, hook ) \
+ MACRO_BEGIN \
+ elt = hook ? *hook : NULL; if( elt ) { *hook = elt->next; elt->next = NULL; } \
+ MACRO_END
+
+/* unlink the specific element, if it is in the list. Otherwise, set
+ * elt to NULL */
+#define list_unlink( listtype, list, elt ) \
+ MACRO_BEGIN \
+ listtype * *_hook; \
+ _list_find_hook( list, *_hook==elt, _hook ); \
+ list_unlink_athook( list, elt, _hook ); \
+ MACRO_END
+
+/* prepend elt to list */
+#define list_prepend( list, elt ) \
+ MACRO_BEGIN elt->next = list; list = elt; MACRO_END
+
+/* append elt to list. */
+#define list_append( listtype, list, elt ) \
+ MACRO_BEGIN \
+ listtype * *_hook; \
+ _list_forall_hook( list, _hook ) {} \
+ list_insert_athook( elt, _hook ); \
+ MACRO_END
+
+/* unlink the first element that satisfies the condition. */
+#define list_unlink_cond( listtype, list, elt, c ) \
+ MACRO_BEGIN \
+ listtype * *_hook; \
+ list_find2( elt, list, c, _hook ); \
+ list_unlink_athook( list, elt, _hook ); \
+ MACRO_END
+
+/* let elt be the nth element of the list, starting to count from 0.
+ * Return NULL if out of bounds. */
+#define list_nth( elt, list, n ) \
+ MACRO_BEGIN \
+ int _x; /* only evaluate n once */ \
+ for( _x = (n), elt = list; _x && elt; _x--, elt = elt->next ) {} \
+ MACRO_END
+
+/* let elt be the nth element of the list, starting to count from 0.
+ * Return NULL if out of bounds. */
+#define list_nth_hook( elt, list, n, hook ) \
+ MACRO_BEGIN \
+ int _x; /* only evaluate n once */ \
+ for( _x = (n), elt = list, hook = &list; _x && elt; _x--, hook = &elt->next, elt =\
+ elt->next ) {} \
+ MACRO_END
+
+/* set n to the length of the list */
+#define list_length( listtype, list, n ) \
+ MACRO_BEGIN \
+ listtype * _elt; \
+ n = 0; \
+ list_forall( _elt, list ) \
+ n++; \
+ MACRO_END
+
+/* set n to the index of the first element satisfying cond, or -1 if
+ * none found. Also set elt to the element, or NULL if none found. */
+#define list_index( list, n, elt, c ) \
+ MACRO_BEGIN \
+ n = 0; \
+ list_forall( elt, list ) { \
+ if( c ) \
+ break;\
+ n++; \
+ } \
+ if( !elt ) \
+ n = -1;\
+ MACRO_END
+
+/* set n to the number of elements in the list that satisfy condition c */
+#define list_count( list, n, elt, c ) \
+ MACRO_BEGIN \
+ n = 0; \
+ list_forall( elt, list ) { \
+ if( c ) \
+ n++;\
+ } \
+ MACRO_END
+
+/* let elt be each element of the list, unlinked. At the end, set list=NULL. */
+#define list_forall_unlink( elt, list ) \
+ for( elt = list; elt ? (list = elt->next, elt->next = NULL), 1 : 0; elt = list )
+
+/* reverse a list (efficient) */
+#define list_reverse( listtype, list ) \
+ MACRO_BEGIN \
+ listtype * _list1 = NULL, *elt; \
+ list_forall_unlink( elt, list ) \
+ list_prepend( _list1, elt ); \
+ list = _list1; \
+ MACRO_END
+
+/* insert the element ELT just before the first element TMP of the
+ * list for which COND holds. Here COND must be a condition of ELT and
+ * TMP. Typical usage is to insert an element into an ordered list:
+ * for instance, list_insert_ordered(listtype, list, elt, tmp,
+ * elt->size <= tmp->size). Note: if we give a "less than or equal"
+ * condition, the new element will be inserted just before a sequence
+ * of equal elements. If we give a "less than" condition, the new
+ * element will be inserted just after a list of equal elements.
+ * Note: it is much more efficient to construct a list with
+ * list_prepend and then order it with list_merge_sort, than to
+ * construct it with list_insert_ordered. */
+#define list_insert_ordered( listtype, list, elt, tmp, cond ) \
+ MACRO_BEGIN \
+ listtype * *_hook; \
+ _list_find_hook( list, ( tmp = *_hook, (cond) ), _hook ); \
+ list_insert_athook( elt, _hook ); \
+ MACRO_END
+
+/* sort the given list, according to the comparison condition.
+ * Typical usage is list_sort(listtype, list, a, b, a->size <
+ * b->size). Note: if we give "less than or equal" condition, each
+ * segment of equal elements will be reversed in order. If we give a
+ * "less than" condition, each segment of equal elements will retain
+ * the original order. The latter is slower but sometimes
+ * prettier. Average running time: n*n/2. */
+#define list_sort( listtype, list, a, b, cond ) \
+ MACRO_BEGIN \
+ listtype * _newlist = NULL; \
+ list_forall_unlink( a, list ) \
+ list_insert_ordered( listtype, _newlist, a, b, cond ); \
+ list = _newlist; \
+ MACRO_END
+
+/* a much faster sort algorithm (merge sort, n log n worst case). It
+ * is required that the list type has an additional, unused next1
+ * component. Note there is no curious reversal of order of equal
+ * elements as for list_sort. */
+
+#define list_mergesort( listtype, list, a, b, cond ) \
+ MACRO_BEGIN \
+ listtype * _elt, **_hook1; \
+ \
+ for( _elt = list; _elt; _elt = _elt->next1 ) { \
+ _elt->next1 = _elt->next; \
+ _elt->next = NULL; \
+ } \
+ do { \
+ _hook1 = &(list); \
+ while( (a = *_hook1) != NULL && (b = a->next1) != NULL ) { \
+ _elt = b->next1; \
+ _list_merge_cond( listtype, a, b, cond, *_hook1 ); \
+ _hook1 = &( (*_hook1)->next1 ); \
+ *_hook1 = _elt; \
+ } \
+ } while( _hook1 != &(list) ); \
+ MACRO_END
+
+/* merge two sorted lists. Store result at &result */
+#define _list_merge_cond( listtype, a, b, cond, result ) \
+ MACRO_BEGIN \
+ listtype * *_hook; \
+ _hook = &(result); \
+ while( 1 ) { \
+ if( a==NULL ) { \
+ *_hook = b; \
+ break; \
+ } else if( b==NULL ) { \
+ *_hook = a; \
+ break; \
+ } else if( cond ) { \
+ *_hook = a; \
+ _hook = &(a->next); \
+ a = a->next; \
+ } else { \
+ *_hook = b; \
+ _hook = &(b->next); \
+ b = b->next; \
+ } \
+ } \
+ MACRO_END
+
+/* ---------------------------------------------------------------------- */
+/* macros for doubly-linked lists */
+
+#define dlist_append( head, end, elt ) \
+ MACRO_BEGIN \
+ elt->prev = end; \
+ elt->next = NULL; \
+ if( end ) { \
+ end->next = elt; \
+ } else { \
+ head = elt; \
+ } \
+ end = elt; \
+ MACRO_END
+
+/* let elt be each element of the list, unlinked. At the end, set list=NULL. */
+#define dlist_forall_unlink( elt, head, end ) \
+ for( elt = head;\
+ elt ? (head = elt->next, elt->next = NULL, elt->prev = NULL), 1 : (end = NULL, 0); \
+ elt = head )
+
+/* unlink the first element of the list */
+#define dlist_unlink_first( head, end, elt ) \
+ MACRO_BEGIN \
+ elt = head; \
+ if( head ) { \
+ head = head->next; \
+ if( head ) { \
+ head->prev = NULL; \
+ } else { \
+ end = NULL; \
+ } \
+ elt->prev = NULL; \
+ elt->next = NULL; \
+ } \
+ MACRO_END
+
+#endif /* _PS_LISTS_H */
diff --git a/potrace/platform.h b/potrace/platform.h
new file mode 100644
index 0000000..1f8dd53
--- /dev/null
+++ b/potrace/platform.h
@@ -0,0 +1,39 @@
+/* Copyright (C) 2001-2007 Peter Selinger.
+ * This file is part of Potrace. It is free software and it is covered
+ * by the GNU General Public License. See the file COPYING for details. */
+
+/* this header file contains some platform dependent stuff */
+
+#ifndef PLATFORM_H
+#define PLATFORM_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* in Windows, set all file i/o to binary */
+#ifdef __MINGW32__
+#include <fcntl.h>
+unsigned int _CRT_fmode = _O_BINARY;
+#endif
+
+#ifdef __CYGWIN__
+#include <fcntl.h>
+#include <io.h>
+static inline void platform_init( void )
+{
+ setmode( 0, O_BINARY );
+ setmode( 1, O_BINARY );
+}
+
+
+#else
+static inline void platform_init( void )
+{
+ /* NOP */
+}
+
+
+#endif
+
+#endif /* PLATFORM_H */
diff --git a/potrace/potrace_version.h b/potrace/potrace_version.h
new file mode 100644
index 0000000..a45db0f
--- /dev/null
+++ b/potrace/potrace_version.h
@@ -0,0 +1 @@
+#define POTRACELIB_VERSION "potracelib 1.8"
diff --git a/potrace/potracelib.cpp b/potrace/potracelib.cpp
new file mode 100644
index 0000000..26c1aa7
--- /dev/null
+++ b/potrace/potracelib.cpp
@@ -0,0 +1,129 @@
+/* Copyright (C) 2001-2007 Peter Selinger.
+ * This file is part of Potrace. It is free software and it is covered
+ * by the GNU General Public License. See the file COPYING for details.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <potracelib.h>
+#include <curve.h>
+#include <decompose.h>
+#include <trace.h>
+#include <progress.h>
+#include <potrace_version.h>
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* default parameters */
+static const potrace_param_t param_default =
+{
+ 2, /* turdsize */
+ POTRACE_TURNPOLICY_MINORITY, /* turnpolicy */
+ 1.0, /* alphamax */
+ 1, /* opticurve */
+ 0.2, /* opttolerance */
+ {
+ NULL, /* callback function */
+ NULL, /* callback data */
+ 0.0, 1.0, /* progress range */
+ 0.0, /* granularity */
+ },
+};
+
+/* Return a fresh copy of the set of default parameters, or NULL on
+ * failure with errno set. */
+potrace_param_t* potrace_param_default( void )
+{
+ potrace_param_t* p;
+
+ p = (potrace_param_t*) malloc( sizeof(potrace_param_t) );
+ if( !p )
+ {
+ return NULL;
+ }
+ memcpy( p, &param_default, sizeof(potrace_param_t) );
+ return p;
+}
+
+
+/* On success, returns a Potrace state st with st->status ==
+ * POTRACE_STATUS_OK. On failure, returns NULL if no Potrace state
+ * could be created (with errno set), or returns an incomplete Potrace
+ * state (with st->status == POTRACE_STATUS_INCOMPLETE). Complete or
+ * incomplete Potrace state can be freed with potrace_state_free(). */
+potrace_state_t* potrace_trace( const potrace_param_t* param, const potrace_bitmap_t* bm )
+{
+ int r;
+ path_t* plist = NULL;
+ potrace_state_t* st;
+ progress_t prog;
+ progress_t subprog;
+
+ /* prepare private progress bar state */
+ prog.callback = param->progress.callback;
+ prog.data = param->progress.data;
+ prog.min = param->progress.min;
+ prog.max = param->progress.max;
+ prog.epsilon = param->progress.epsilon;
+ prog.d_prev = param->progress.min;
+
+ /* allocate state object */
+ st = (potrace_state_t*) malloc( sizeof(potrace_state_t) );
+ if( !st )
+ {
+ return NULL;
+ }
+
+ progress_subrange_start( 0.0, 0.1, &prog, &subprog );
+
+ /* process the image */
+ r = bm_to_pathlist( bm, &plist, param, &subprog );
+ if( r )
+ {
+ free( st );
+ return NULL;
+ }
+
+ st->status = POTRACE_STATUS_OK;
+ st->plist = plist;
+ st->priv = NULL; /* private state currently unused */
+
+ progress_subrange_end( &prog, &subprog );
+
+ progress_subrange_start( 0.1, 1.0, &prog, &subprog );
+
+ /* partial success. */
+ r = process_path( plist, param, &subprog );
+ if( r )
+ {
+ st->status = POTRACE_STATUS_INCOMPLETE;
+ }
+
+ progress_subrange_end( &prog, &subprog );
+
+ return st;
+}
+
+
+/* free a Potrace state, without disturbing errno. */
+void potrace_state_free( potrace_state_t* st )
+{
+ pathlist_free( st->plist );
+ free( st );
+}
+
+
+/* free a parameter list, without disturbing errno. */
+void potrace_param_free( potrace_param_t* p )
+{
+ free( p );
+}
+
+
+const char* potrace_version( void )
+{
+ return POTRACELIB_VERSION;
+}
diff --git a/potrace/potracelib.h b/potrace/potracelib.h
new file mode 100644
index 0000000..6a554e5
--- /dev/null
+++ b/potrace/potracelib.h
@@ -0,0 +1,139 @@
+/* Copyright (C) 2001-2007 Peter Selinger.
+ * This file is part of Potrace. It is free software and it is covered
+ * by the GNU General Public License. See the file COPYING for details. */
+
+#ifndef POTRACELIB_H
+#define POTRACELIB_H
+
+/* this file defines the API for the core Potrace library. For a more
+ * detailed description of the API, see doc/potracelib.txt */
+
+/* ---------------------------------------------------------------------- */
+/* tracing parameters */
+
+/* turn policies */
+#define POTRACE_TURNPOLICY_BLACK 0
+#define POTRACE_TURNPOLICY_WHITE 1
+#define POTRACE_TURNPOLICY_LEFT 2
+#define POTRACE_TURNPOLICY_RIGHT 3
+#define POTRACE_TURNPOLICY_MINORITY 4
+#define POTRACE_TURNPOLICY_MAJORITY 5
+#define POTRACE_TURNPOLICY_RANDOM 6
+
+/* structure to hold progress bar callback data */
+struct potrace_progress_s
+{
+ void (* callback)( double progress, void* privdata ); /* callback fn */
+ void* data; /* callback function's private data */
+ double min, max; /* desired range of progress, e.g. 0.0 to 1.0 */
+ double epsilon; /* granularity: can skip smaller increments */
+};
+typedef struct potrace_progress_s potrace_progress_t;
+
+/* structure to hold tracing parameters */
+struct potrace_param_s
+{
+ int turdsize; /* area of largest path to be ignored */
+ int turnpolicy; /* resolves ambiguous turns in path decomposition */
+ double alphamax; /* corner threshold */
+ int opticurve; /* use curve optimization? */
+ double opttolerance; /* curve optimization tolerance */
+ potrace_progress_t progress; /* progress callback function */
+};
+typedef struct potrace_param_s potrace_param_t;
+
+/* ---------------------------------------------------------------------- */
+/* bitmaps */
+
+/* native word size */
+typedef unsigned long potrace_word;
+
+/* Internal bitmap format. The n-th scanline starts at scanline(n) =
+ * (map + n*dy). Raster data is stored as a sequence of potrace_words
+ * (NOT bytes). The leftmost bit of scanline n is the most significant
+ * bit of scanline(n)[0]. */
+struct potrace_bitmap_s
+{
+ int w, h; /* width and height, in pixels */
+ int dy; /* words per scanline (not bytes) */
+ potrace_word* map; /* raw data, dy*h words */
+};
+typedef struct potrace_bitmap_s potrace_bitmap_t;
+
+/* ---------------------------------------------------------------------- */
+/* curves */
+
+/* point */
+struct potrace_dpoint_s
+{
+ double x, y;
+};
+typedef struct potrace_dpoint_s potrace_dpoint_t;
+
+/* segment tags */
+#define POTRACE_CURVETO 1
+#define POTRACE_CORNER 2
+
+/* closed curve segment */
+struct potrace_curve_s
+{
+ int n; // number of segments
+ int* tag; // tag[n]: POTRACE_CURVETO or POTRACE_CORNER
+ potrace_dpoint_t( * c )[3]; /* c[n][3]: control points.
+ * c[n][0] is unused for tag[n]=POTRACE_CORNER
+ */
+};
+typedef struct potrace_curve_s potrace_curve_t;
+
+/* Linked list of signed curve segments. Also carries a tree structure. */
+struct potrace_path_s
+{
+ int area; /* area of the bitmap path */
+ int sign; /* '+' or '-', depending on orientation */
+ potrace_curve_t curve; /* this path's vector data */
+
+ struct potrace_path_s* next; /* linked list structure */
+
+ struct potrace_path_s* childlist; /* tree structure */
+ struct potrace_path_s* sibling; /* tree structure */
+
+ struct potrace_privpath_s* priv; /* private state */
+};
+typedef struct potrace_path_s potrace_path_t;
+
+/* ---------------------------------------------------------------------- */
+/* Potrace state */
+
+#define POTRACE_STATUS_OK 0
+#define POTRACE_STATUS_INCOMPLETE 1
+
+struct potrace_state_s
+{
+ int status;
+ potrace_path_t* plist; /* vector data */
+
+ struct potrace_privstate_s* priv; /* private state */
+};
+typedef struct potrace_state_s potrace_state_t;
+
+/* ---------------------------------------------------------------------- */
+/* API functions */
+
+/* get default parameters */
+potrace_param_t* potrace_param_default( void );
+
+/* free parameter set */
+void potrace_param_free( potrace_param_t* p );
+
+/* trace a bitmap*/
+potrace_state_t* potrace_trace( const potrace_param_t* param,
+ const potrace_bitmap_t* bm );
+
+/* free a Potrace state */
+void potrace_state_free( potrace_state_t* st );
+
+/* return a static plain text version string identifying this version
+ * of potracelib */
+const char* potrace_version( void );
+
+#endif /* POTRACELIB_H */
diff --git a/potrace/progress.h b/potrace/progress.h
new file mode 100644
index 0000000..5010481
--- /dev/null
+++ b/potrace/progress.h
@@ -0,0 +1,96 @@
+/* Copyright (C) 2001-2007 Peter Selinger.
+ * This file is part of Potrace. It is free software and it is covered
+ * by the GNU General Public License. See the file COPYING for details. */
+
+/* operations on potrace_progress_t objects, which are defined in
+ * potracelib.h. Note: the code attempts to minimize runtime overhead
+ * when no progress monitoring was requested. It also tries to
+ * minimize excessive progress calculations beneath the "epsilon"
+ * threshold. */
+
+#ifndef PROGRESS_H
+#define PROGRESS_H
+
+/* structure to hold progress bar callback data */
+struct progress_s
+{
+ void (* callback)( double progress, void* privdata ); /* callback fn */
+ void* data; /* callback function's private data */
+ double min, max; /* desired range of progress, e.g. 0.0 to 1.0 */
+ double epsilon; /* granularity: can skip smaller increments */
+ double b; /* upper limit of subrange in superrange units */
+ double d_prev; /* previous value of d */
+};
+typedef struct progress_s progress_t;
+
+/* notify given progress object of current progress. Note that d is
+ * given in the 0.0-1.0 range, which will be scaled and translated to
+ * the progress object's range. */
+static inline void progress_update( double d, progress_t* prog )
+{
+ double d_scaled;
+
+ if( prog->callback != NULL )
+ {
+ d_scaled = prog->min * (1 - d) + prog->max * d;
+ if( d == 1.0 || d_scaled >= prog->d_prev + prog->epsilon )
+ {
+ prog->callback( prog->min * (1 - d) + prog->max * d, prog->data );
+ prog->d_prev = d_scaled;
+ }
+ }
+}
+
+
+/* start a subrange of the given progress object. The range is
+ * narrowed to [a..b], relative to 0.0-1.0 coordinates. If new range
+ * is below granularity threshold, disable further subdivisions. */
+static inline void progress_subrange_start( double a,
+ double b,
+ const progress_t* prog,
+ progress_t* sub )
+{
+ double min, max;
+
+ if( prog->callback == NULL )
+ {
+ sub->callback = NULL;
+ return;
+ }
+
+ min = prog->min * (1 - a) + prog->max * a;
+ max = prog->min * (1 - b) + prog->max * b;
+
+ if( max - min < prog->epsilon )
+ {
+ sub->callback = NULL; /* no further progress info in subrange */
+ sub->b = b;
+ return;
+ }
+ sub->callback = prog->callback;
+ sub->data = prog->data;
+ sub->epsilon = prog->epsilon;
+ sub->min = min;
+ sub->max = max;
+ sub->d_prev = prog->d_prev;
+ return;
+}
+
+
+static inline void progress_subrange_end( progress_t* prog, progress_t* sub )
+{
+ if( prog->callback != NULL )
+ {
+ if( sub->callback == NULL )
+ {
+ progress_update( sub->b, prog );
+ }
+ else
+ {
+ prog->d_prev = sub->d_prev;
+ }
+ }
+}
+
+
+#endif /* PROGRESS_H */
diff --git a/potrace/render.cpp b/potrace/render.cpp
new file mode 100644
index 0000000..833f6b0
--- /dev/null
+++ b/potrace/render.cpp
@@ -0,0 +1,294 @@
+/* Copyright (C) 2001-2007 Peter Selinger.
+ * This file is part of Potrace. It is free software and it is covered
+ * by the GNU General Public License. See the file COPYING for details. */
+
+/* $Id: render.c 147 2007-04-09 00:44:09Z selinger $ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <cmath>
+#include <string.h>
+
+#include <render.h>
+#include <greymap.h>
+#include <auxiliary.h>
+
+/* ---------------------------------------------------------------------- */
+/* routines for anti-aliased rendering of curves */
+
+/* we use the following method. Given a point (x,y) (with real-valued
+ * coordinates) in the plane, let (xi,yi) be the integer part of the
+ * coordinates, i.e., xi=floor(x), yi=floor(y). Define a path from
+ * (x,y) to infinity as follows: path(x,y) =
+ * (x,y)--(xi+1,y)--(xi+1,yi)--(+infty,yi). Now as the point (x,y)
+ * moves smoothly across the plane, the path path(x,y) sweeps
+ * (non-smoothly) across a certain area. We proportionately blacken
+ * the area as the path moves "downward", and we whiten the area as
+ * the path moves "upward". This way, after the point has traversed a
+ * closed curve, the interior of the curve has been darkened
+ * (counterclockwise movement) or lightened (clockwise movement). (The
+ * "grey shift" is actually proportional to the winding number). By
+ * choosing the above path with mostly integer coordinates, we achieve
+ * that only pixels close to (x,y) receive grey values and are subject
+ * to round-off errors. The grey value of pixels far away from (x,y)
+ * is always in "integer" (where 0=black, 1=white). As a special
+ * trick, we keep an accumulator rm->a1, which holds a double value to
+ * be added to the grey value to be added to the current pixel
+ * (xi,yi). Only when changing "current" pixels, we convert this
+ * double value to an integer. This way we avoid round-off errors at
+ * the meeting points of line segments. Another speedup measure is
+ * that we sometimes use the rm->incrow_buf array to postpone
+ * incrementing or decrementing an entire row. If incrow_buf[y]=x+1!=0,
+ * then all the pixels (x,y),(x+1,y),(x+2,y),... are scheduled to be
+ * incremented/decremented (which one is the case will be clear from
+ * context). This keeps the greymap operations reasonably local. */
+
+/* allocate a new rendering state */
+render_t* render_new( greymap_t* gm )
+{
+ render_t* rm;
+
+ rm = (render_t*) malloc( sizeof(render_t) );
+ if( !rm )
+ {
+ return NULL;
+ }
+ memset( rm, 0, sizeof(render_t) );
+ rm->gm = gm;
+ rm->incrow_buf = (int*) malloc( gm->h * sizeof(int) );
+ if( !rm->incrow_buf )
+ {
+ free( rm );
+ return NULL;
+ }
+ memset( rm->incrow_buf, 0, gm->h * sizeof(int) );
+ return rm;
+}
+
+
+/* free a given rendering state. Note: this does not free the
+ * underlying greymap. */
+void render_free( render_t* rm )
+{
+ free( rm->incrow_buf );
+ free( rm );
+}
+
+
+/* close path */
+void render_close( render_t* rm )
+{
+ if( rm->x0 != rm->x1 || rm->y0 != rm->y1 )
+ {
+ render_lineto( rm, rm->x0, rm->y0 );
+ }
+ GM_INC( rm->gm, rm->x0i, rm->y0i, (rm->a0 + rm->a1) * 255 );
+
+ /* assert (rm->x0i != rm->x1i || rm->y0i != rm->y1i); */
+
+ /* the persistent state is now undefined */
+}
+
+
+/* move point */
+void render_moveto( render_t* rm, double x, double y )
+{
+ /* close the previous path */
+ render_close( rm );
+
+ rm->x0 = rm->x1 = x;
+ rm->y0 = rm->y1 = y;
+ rm->x0i = (int) floor( rm->x0 );
+ rm->x1i = (int) floor( rm->x1 );
+ rm->y0i = (int) floor( rm->y0 );
+ rm->y1i = (int) floor( rm->y1 );
+ rm->a0 = rm->a1 = 0;
+}
+
+
+/* add b to pixels (x,y) and all pixels to the right of it. However,
+ * use rm->incrow_buf as a buffer to economize on multiple calls */
+static void incrow( render_t* rm, int x, int y, int b )
+{
+ int i, x0;
+
+ if( y < 0 || y >= rm->gm->h )
+ {
+ return;
+ }
+
+ if( x < 0 )
+ {
+ x = 0;
+ }
+ else if( x > rm->gm->w )
+ {
+ x = rm->gm->w;
+ }
+ if( rm->incrow_buf[y] == 0 )
+ {
+ rm->incrow_buf[y] = x + 1; /* store x+1 so that we can use 0 for "vacant" */
+ return;
+ }
+ x0 = rm->incrow_buf[y] - 1;
+ rm->incrow_buf[y] = 0;
+ if( x0 < x )
+ {
+ for( i = x0; i<x; i++ )
+ {
+ GM_INC( rm->gm, i, y, -b );
+ }
+ }
+ else
+ {
+ for( i = x; i<x0; i++ )
+ {
+ GM_INC( rm->gm, i, y, b );
+ }
+ }
+}
+
+
+/* render a straight line */
+void render_lineto( render_t* rm, double x2, double y2 )
+{
+ int x2i, y2i;
+ double t0 = 2, s0 = 2;
+ int sn, tn;
+ double ss = 2, ts = 2;
+ double r0, r1;
+ int i, j;
+ int rxi, ryi;
+ int s;
+
+ x2i = (int) floor( x2 );
+ y2i = (int) floor( y2 );
+
+ sn = abs( x2i - rm->x1i );
+ tn = abs( y2i - rm->y1i );
+
+ if( sn )
+ {
+ s0 = ( (x2>rm->x1 ? rm->x1i + 1 : rm->x1i) - rm->x1 ) / (x2 - rm->x1);
+ ss = fabs( 1.0 / (x2 - rm->x1) );
+ }
+ if( tn )
+ {
+ t0 = ( (y2>rm->y1 ? rm->y1i + 1 : rm->y1i) - rm->y1 ) / (y2 - rm->y1);
+ ts = fabs( 1.0 / (y2 - rm->y1) );
+ }
+
+ r0 = 0;
+
+ i = 0;
+ j = 0;
+
+ rxi = rm->x1i;
+ ryi = rm->y1i;
+
+ while( i<sn || j<tn )
+ {
+ if( j>=tn || (i<sn && s0 + i * ss < t0 + j * ts) )
+ {
+ r1 = s0 + i * ss;
+ i++;
+ s = 1;
+ }
+ else
+ {
+ r1 = t0 + j * ts;
+ j++;
+ s = 0;
+ }
+ /* render line from r0 to r1 segment of (rm->x1,rm->y1)..(x2,y2) */
+
+ /* move point to r1 */
+ rm->a1 +=
+ (r1 - r0) * (y2 - rm->y1) * ( rxi + 1 - ( (r0 + r1) / 2.0 * (x2 - rm->x1) + rm->x1 ) );
+
+ /* move point across pixel boundary */
+ if( s && x2>rm->x1 )
+ {
+ GM_INC( rm->gm, rxi, ryi, rm->a1 * 255 );
+ rm->a1 = 0;
+ rxi++;
+ rm->a1 += rm->y1 + r1 * (y2 - rm->y1) - ryi;
+ }
+ else if( !s && y2>rm->y1 )
+ {
+ GM_INC( rm->gm, rxi, ryi, rm->a1 * 255 );
+ rm->a1 = 0;
+ incrow( rm, rxi + 1, ryi, 255 );
+ ryi++;
+ }
+ else if( s && x2<=rm->x1 )
+ {
+ rm->a1 -= rm->y1 + r1 * (y2 - rm->y1) - ryi;
+ GM_INC( rm->gm, rxi, ryi, rm->a1 * 255 );
+ rm->a1 = 0;
+ rxi--;
+ }
+ else if( !s && y2<=rm->y1 )
+ {
+ GM_INC( rm->gm, rxi, ryi, rm->a1 * 255 );
+ rm->a1 = 0;
+ ryi--;
+ incrow( rm, rxi + 1, ryi, -255 );
+ }
+
+ r0 = r1;
+ }
+
+ /* move point to (x2,y2) */
+
+ r1 = 1;
+ rm->a1 += (r1 - r0) * (y2 - rm->y1) * ( rxi + 1 - ( (r0 + r1) / 2.0 * (x2 - rm->x1) + rm->x1 ) );
+
+ rm->x1i = x2i;
+ rm->y1i = y2i;
+ rm->x1 = x2;
+ rm->y1 = y2;
+
+ /* assert (rxi != rm->x1i || ryi != rm->y1i); */
+}
+
+
+/* render a Bezier curve. */
+void render_curveto( render_t* rm,
+ double x2,
+ double y2,
+ double x3,
+ double y3,
+ double x4,
+ double y4 )
+{
+ double x1, y1, dd0, dd1, dd, delta, e2, epsilon, t;
+
+ x1 = rm->x1; /* starting point */
+ y1 = rm->y1;
+
+ /* we approximate the curve by small line segments. The interval
+ * size, epsilon, is determined on the fly so that the distance
+ * between the true curve and its approximation does not exceed the
+ * desired accuracy delta. */
+
+ delta = .1; /* desired accuracy, in pixels */
+
+ /* let dd = maximal value of 2nd derivative over curve - this must
+ * occur at an endpoint. */
+ dd0 = sq( x1 - 2 * x2 + x3 ) + sq( y1 - 2 * y2 + y3 );
+ dd1 = sq( x2 - 2 * x3 + x4 ) + sq( y2 - 2 * y3 + y4 );
+ dd = 6 * sqrt( max( dd0, dd1 ) );
+ e2 = 8 * delta <= dd ? 8 * delta / dd : 1;
+ epsilon = sqrt( e2 ); /* necessary interval size */
+
+ for( t = epsilon; t<1; t += epsilon )
+ {
+ render_lineto( rm, x1 * cu( 1 - t ) + 3 * x2 * sq( 1 - t ) * t + 3 * x3 * (1 - t) * sq(
+ t ) + x4 * cu( t ),
+ y1 * cu( 1 - t ) + 3 * y2 * sq( 1 - t ) * t + 3 * y3 * (1 - t) * sq(
+ t ) + y4 * cu( t ) );
+ }
+
+ render_lineto( rm, x4, y4 );
+}
diff --git a/potrace/render.h b/potrace/render.h
new file mode 100644
index 0000000..5458df0
--- /dev/null
+++ b/potrace/render.h
@@ -0,0 +1,35 @@
+/* Copyright (C) 2001-2007 Peter Selinger.
+ * This file is part of Potrace. It is free software and it is covered
+ * by the GNU General Public License. See the file COPYING for details. */
+
+/* $Id: render.h 147 2007-04-09 00:44:09Z selinger $ */
+
+#ifndef RENDER_H
+#define RENDER_H
+
+#include <greymap.h>
+
+struct render_s
+{
+ greymap_t* gm;
+ double x0, y0, x1, y1;
+ int x0i, y0i, x1i, y1i;
+ double a0, a1;
+ int* incrow_buf;
+};
+typedef struct render_s render_t;
+
+render_t* render_new( greymap_t* gm );
+void render_free( render_t* rm );
+void render_close( render_t* rm );
+void render_moveto( render_t* rm, double x, double y );
+void render_lineto( render_t* rm, double x, double y );
+void render_curveto( render_t* rm,
+ double x2,
+ double y2,
+ double x3,
+ double y3,
+ double x4,
+ double y4 );
+
+#endif /* RENDER_H */
diff --git a/potrace/trace.cpp b/potrace/trace.cpp
new file mode 100644
index 0000000..fdf4afa
--- /dev/null
+++ b/potrace/trace.cpp
@@ -0,0 +1,1464 @@
+/* Copyright (C) 2001-2007 Peter Selinger.
+ * This file is part of Potrace. It is free software and it is covered
+ * by the GNU General Public License. See the file COPYING for details. */
+
+/* $Id: trace.c 147 2007-04-09 00:44:09Z selinger $ */
+/* transform jaggy paths into smooth curves */
+
+#include <stdio.h>
+#include <cmath>
+#include <stdlib.h>
+#include <string.h>
+
+#include <potracelib.h>
+#include <curve.h>
+#include <lists.h>
+#include <auxiliary.h>
+#include <trace.h>
+#include <progress.h>
+
+#define INFTY 10000000 /* it suffices that this is longer than any
+ * path; it need not be really infinite */
+#define COS179 -0.999847695156 /* the cosine of 179 degrees */
+
+/* ---------------------------------------------------------------------- */
+#define SAFE_MALLOC( var, n, typ ) \
+ if( ( var = (typ*) malloc( (n) * sizeof(typ) ) ) == NULL ) \
+ goto malloc_error
+
+/* ---------------------------------------------------------------------- */
+/* auxiliary functions */
+
+/* return a direction that is 90 degrees counterclockwise from p2-p0,
+ * but then restricted to one of the major wind directions (n, nw, w, etc) */
+static inline point_t dorth_infty( dpoint_t p0, dpoint_t p2 )
+{
+ point_t r;
+
+ r.y = sign( p2.x - p0.x );
+ r.x = -sign( p2.y - p0.y );
+
+ return r;
+}
+
+
+/* return (p1-p0)x(p2-p0), the area of the parallelogram */
+static inline double dpara( dpoint_t p0, dpoint_t p1, dpoint_t p2 )
+{
+ double x1, y1, x2, y2;
+
+ x1 = p1.x - p0.x;
+ y1 = p1.y - p0.y;
+ x2 = p2.x - p0.x;
+ y2 = p2.y - p0.y;
+
+ return x1 * y2 - x2 * y1;
+}
+
+
+/* ddenom/dpara have the property that the square of radius 1 centered
+ * at p1 intersects the line p0p2 iff |dpara(p0,p1,p2)| <= ddenom(p0,p2) */
+static inline double ddenom( dpoint_t p0, dpoint_t p2 )
+{
+ point_t r = dorth_infty( p0, p2 );
+
+ return r.y * (p2.x - p0.x) - r.x * (p2.y - p0.y);
+}
+
+
+/* return 1 if a <= b < c < a, in a cyclic sense (mod n) */
+static inline int cyclic( int a, int b, int c )
+{
+ if( a<=c )
+ {
+ return a<=b && b<c;
+ }
+ else
+ {
+ return a<=b || b<c;
+ }
+}
+
+
+/* determine the center and slope of the line i..j. Assume i<j. Needs
+ * "sum" components of p to be set. */
+static void pointslope( privpath_t* pp, int i, int j, dpoint_t* ctr, dpoint_t* dir )
+{
+ /* assume i<j */
+
+ int n = pp->len;
+ sums_t* sums = pp->sums;
+
+ double x, y, x2, xy, y2;
+ double k;
+ double a, b, c, lambda2, l;
+ int r = 0; /* rotations from i to j */
+
+ while( j>=n )
+ {
+ j -= n;
+ r += 1;
+ }
+
+ while( i>=n )
+ {
+ i -= n;
+ r -= 1;
+ }
+
+ while( j<0 )
+ {
+ j += n;
+ r -= 1;
+ }
+
+ while( i<0 )
+ {
+ i += n;
+ r += 1;
+ }
+
+ x = sums[j + 1].x - sums[i].x + r * sums[n].x;
+ y = sums[j + 1].y - sums[i].y + r * sums[n].y;
+ x2 = sums[j + 1].x2 - sums[i].x2 + r * sums[n].x2;
+ xy = sums[j + 1].xy - sums[i].xy + r * sums[n].xy;
+ y2 = sums[j + 1].y2 - sums[i].y2 + r * sums[n].y2;
+ k = j + 1 - i + r * n;
+
+ ctr->x = x / k;
+ ctr->y = y / k;
+
+ a = (x2 - (double) x * x / k) / k;
+ b = (xy - (double) x * y / k) / k;
+ c = (y2 - (double) y * y / k) / k;
+
+ lambda2 = ( a + c + sqrt( (a - c) * (a - c) + 4 * b * b ) ) / 2; /* larger e.value */
+
+ /* now find e.vector for lambda2 */
+ a -= lambda2;
+ c -= lambda2;
+
+ if( fabs( a ) >= fabs( c ) )
+ {
+ l = sqrt( a * a + b * b );
+ if( l!=0 )
+ {
+ dir->x = -b / l;
+ dir->y = a / l;
+ }
+ }
+ else
+ {
+ l = sqrt( c * c + b * b );
+ if( l!=0 )
+ {
+ dir->x = -c / l;
+ dir->y = b / l;
+ }
+ }
+ if( l==0 )
+ {
+ dir->x = dir->y = 0; /* sometimes this can happen when k=4:
+ * the two eigenvalues coincide */
+ }
+}
+
+
+/* the type of (affine) quadratic forms, represented as symmetric 3x3
+ * matrices. The value of the quadratic form at a vector (x,y) is v^t
+ * Q v, where v = (x,y,1)^t. */
+typedef double quadform_t[3][3];
+
+/* Apply quadratic form Q to vector w = (w.x,w.y) */
+static inline double quadform( quadform_t Q, dpoint_t w )
+{
+ double v[3];
+ int i, j;
+ double sum;
+
+ v[0] = w.x;
+ v[1] = w.y;
+ v[2] = 1;
+ sum = 0.0;
+
+ for( i = 0; i<3; i++ )
+ {
+ for( j = 0; j<3; j++ )
+ {
+ sum += v[i] *Q[i][j] *v[j];
+ }
+ }
+
+ return sum;
+}
+
+
+/* calculate p1 x p2 */
+static inline int xprod( point_t p1, point_t p2 )
+{
+ return p1.x * p2.y - p1.y * p2.x;
+}
+
+
+/* calculate (p1-p0)x(p3-p2) */
+static inline double cprod( dpoint_t p0, dpoint_t p1, dpoint_t p2, dpoint_t p3 )
+{
+ double x1, y1, x2, y2;
+
+ x1 = p1.x - p0.x;
+ y1 = p1.y - p0.y;
+ x2 = p3.x - p2.x;
+ y2 = p3.y - p2.y;
+
+ return x1 * y2 - x2 * y1;
+}
+
+
+/* calculate (p1-p0)*(p2-p0) */
+static inline double iprod( dpoint_t p0, dpoint_t p1, dpoint_t p2 )
+{
+ double x1, y1, x2, y2;
+
+ x1 = p1.x - p0.x;
+ y1 = p1.y - p0.y;
+ x2 = p2.x - p0.x;
+ y2 = p2.y - p0.y;
+
+ return x1 * x2 + y1 * y2;
+}
+
+
+/* calculate (p1-p0)*(p3-p2) */
+static inline double iprod1( dpoint_t p0, dpoint_t p1, dpoint_t p2, dpoint_t p3 )
+{
+ double x1, y1, x2, y2;
+
+ x1 = p1.x - p0.x;
+ y1 = p1.y - p0.y;
+ x2 = p3.x - p2.x;
+ y2 = p3.y - p2.y;
+
+ return x1 * x2 + y1 * y2;
+}
+
+
+/* calculate distance between two points */
+static inline double ddist( dpoint_t p, dpoint_t q )
+{
+ return sqrt( sq( p.x - q.x ) + sq( p.y - q.y ) );
+}
+
+
+/* calculate point of a bezier curve */
+static inline dpoint_t bezier( double t, dpoint_t p0, dpoint_t p1, dpoint_t p2, dpoint_t p3 )
+{
+ double s = 1 - t;
+ dpoint_t res;
+
+ /* Note: a good optimizing compiler (such as gcc-3) reduces the
+ * following to 16 multiplications, using common subexpression
+ * elimination. */
+
+ res.x = s * s * s * p0.x + 3 * (s * s * t) * p1.x + 3 * (t * t * s) * p2.x + t * t * t * p3.x;
+ res.y = s * s * s * p0.y + 3 * (s * s * t) * p1.y + 3 * (t * t * s) * p2.y + t * t * t * p3.y;
+
+ return res;
+}
+
+
+/* calculate the point t in [0..1] on the (convex) bezier curve
+ * (p0,p1,p2,p3) which is tangent to q1-q0. Return -1.0 if there is no
+ * solution in [0..1]. */
+static double tangent( dpoint_t p0,
+ dpoint_t p1,
+ dpoint_t p2,
+ dpoint_t p3,
+ dpoint_t q0,
+ dpoint_t q1 )
+{
+ double A, B, C; /* (1-t)^2 A + 2(1-t)t B + t^2 C = 0 */
+ double a, b, c; /* a t^2 + b t + c = 0 */
+ double d, s, r1, r2;
+
+ A = cprod( p0, p1, q0, q1 );
+ B = cprod( p1, p2, q0, q1 );
+ C = cprod( p2, p3, q0, q1 );
+
+ a = A - 2 * B + C;
+ b = -2 * A + 2 * B;
+ c = A;
+
+ d = b * b - 4 * a * c;
+
+ if( a==0 || d<0 )
+ {
+ return -1.0;
+ }
+
+ s = sqrt( d );
+
+ r1 = (-b + s) / (2 * a);
+ r2 = (-b - s) / (2 * a);
+
+ if( r1 >= 0 && r1 <= 1 )
+ {
+ return r1;
+ }
+ else if( r2 >= 0 && r2 <= 1 )
+ {
+ return r2;
+ }
+ else
+ {
+ return -1.0;
+ }
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+/* Preparation: fill in the sum* fields of a path (used for later
+ * rapid summing). Return 0 on success, 1 with errno set on
+ * failure. */
+static int calc_sums( privpath_t* pp )
+{
+ int i, x, y;
+ int n = pp->len;
+
+ SAFE_MALLOC( pp->sums, pp->len + 1, sums_t );
+
+ /* origin */
+ pp->x0 = pp->pt[0].x;
+ pp->y0 = pp->pt[0].y;
+
+ /* preparatory computation for later fast summing */
+ pp->sums[0].x2 = pp->sums[0].xy = pp->sums[0].y2 = pp->sums[0].x = pp->sums[0].y = 0;
+ for( i = 0; i<n; i++ )
+ {
+ x = pp->pt[i].x - pp->x0;
+ y = pp->pt[i].y - pp->y0;
+ pp->sums[i + 1].x = pp->sums[i].x + x;
+ pp->sums[i + 1].y = pp->sums[i].y + y;
+ pp->sums[i + 1].x2 = pp->sums[i].x2 + x * x;
+ pp->sums[i + 1].xy = pp->sums[i].xy + x * y;
+ pp->sums[i + 1].y2 = pp->sums[i].y2 + y * y;
+ }
+
+ return 0;
+
+malloc_error:
+ return 1;
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+/* Stage 1: determine the straight subpaths (Sec. 2.2.1). Fill in the
+ * "lon" component of a path object (based on pt/len). For each i,
+ * lon[i] is the furthest index such that a straight line can be drawn
+ * from i to lon[i]. Return 1 on error with errno set, else 0. */
+
+/* this algorithm depends on the fact that the existence of straight
+ * subpaths is a triplewise property. I.e., there exists a straight
+ * line through squares i0,...,in iff there exists a straight line
+ * through i,j,k, for all i0<=i<j<k<=in. (Proof?) */
+
+/* this implementation of calc_lon is O(n^2). It replaces an older
+ * O(n^3) version. A "constraint" means that future points must
+ * satisfy xprod(constraint[0], cur) >= 0 and xprod(constraint[1],
+ * cur) <= 0. */
+
+/* Remark for Potrace 1.1: the current implementation of calc_lon is
+ * more complex than the implementation found in Potrace 1.0, but it
+ * is considerably faster. The introduction of the "nc" data structure
+ * means that we only have to test the constraints for "corner"
+ * points. On a typical input file, this speeds up the calc_lon
+ * function by a factor of 31.2, thereby decreasing its time share
+ * within the overall Potrace algorithm from 72.6% to 7.82%, and
+ * speeding up the overall algorithm by a factor of 3.36. On another
+ * input file, calc_lon was sped up by a factor of 6.7, decreasing its
+ * time share from 51.4% to 13.61%, and speeding up the overall
+ * algorithm by a factor of 1.78. In any case, the savings are
+ * substantial. */
+
+/* returns 0 on success, 1 on error with errno set */
+static int calc_lon( privpath_t* pp )
+{
+ point_t* pt = pp->pt;
+ int n = pp->len;
+ int i, j, k, k1;
+ int ct[4], dir;
+ point_t constraint[2];
+ point_t cur;
+ point_t off;
+ int* pivk = NULL; /* pivk[n] */
+ int* nc = NULL; /* nc[n]: next corner */
+ point_t dk; /* direction of k-k1 */
+ int a, b, c, d;
+
+ SAFE_MALLOC( pivk, n, int );
+ SAFE_MALLOC( nc, n, int );
+
+ /* initialize the nc data structure. Point from each point to the
+ * furthest future point to which it is connected by a vertical or
+ * horizontal segment. We take advantage of the fact that there is
+ * always a direction change at 0 (due to the path decomposition
+ * algorithm). But even if this were not so, there is no harm, as
+ * in practice, correctness does not depend on the word "furthest"
+ * above. */
+ k = 0;
+ for( i = n - 1; i>=0; i-- )
+ {
+ if( pt[i].x != pt[k].x && pt[i].y != pt[k].y )
+ {
+ k = i + 1; /* necessarily i<n-1 in this case */
+ }
+ nc[i] = k;
+ }
+
+ SAFE_MALLOC( pp->lon, n, int );
+
+ /* determine pivot points: for each i, let pivk[i] be the furthest k
+ * such that all j with i<j<k lie on a line connecting i,k. */
+
+ for( i = n - 1; i>=0; i-- )
+ {
+ ct[0] = ct[1] = ct[2] = ct[3] = 0;
+
+ /* keep track of "directions" that have occurred */
+ dir =
+ ( 3 + 3 * (pt[mod( i + 1, n )].x - pt[i].x) + (pt[mod( i + 1, n )].y - pt[i].y) ) / 2;
+ ct[dir]++;
+
+ constraint[0].x = 0;
+ constraint[0].y = 0;
+ constraint[1].x = 0;
+ constraint[1].y = 0;
+
+ /* find the next k such that no straight line from i to k */
+ k = nc[i];
+ k1 = i;
+ while( 1 )
+ {
+ dir = ( 3 + 3 * sign( pt[k].x - pt[k1].x ) + sign( pt[k].y - pt[k1].y ) ) / 2;
+ ct[dir]++;
+
+ /* if all four "directions" have occurred, cut this path */
+ if( ct[0] && ct[1] && ct[2] && ct[3] )
+ {
+ pivk[i] = k1;
+ goto foundk;
+ }
+
+ cur.x = pt[k].x - pt[i].x;
+ cur.y = pt[k].y - pt[i].y;
+
+ /* see if current constraint is violated */
+ if( xprod( constraint[0], cur ) < 0 || xprod( constraint[1], cur ) > 0 )
+ {
+ goto constraint_viol;
+ }
+
+ /* else, update constraint */
+ if( abs( cur.x ) <= 1 && abs( cur.y ) <= 1 )
+ {
+ /* no constraint */
+ }
+ else
+ {
+ off.x = cur.x + ( ( cur.y>=0 && (cur.y>0 || cur.x<0) ) ? 1 : -1 );
+ off.y = cur.y + ( ( cur.x<=0 && (cur.x<0 || cur.y<0) ) ? 1 : -1 );
+ if( xprod( constraint[0], off ) >= 0 )
+ {
+ constraint[0] = off;
+ }
+ off.x = cur.x + ( ( cur.y<=0 && (cur.y<0 || cur.x<0) ) ? 1 : -1 );
+ off.y = cur.y + ( ( cur.x>=0 && (cur.x>0 || cur.y<0) ) ? 1 : -1 );
+ if( xprod( constraint[1], off ) <= 0 )
+ {
+ constraint[1] = off;
+ }
+ }
+ k1 = k;
+ k = nc[k1];
+ if( !cyclic( k, i, k1 ) )
+ {
+ break;
+ }
+ }
+
+constraint_viol:
+
+ /* k1 was the last "corner" satisfying the current constraint, and
+ * k is the first one violating it. We now need to find the last
+ * point along k1..k which satisfied the constraint. */
+ dk.x = sign( pt[k].x - pt[k1].x );
+ dk.y = sign( pt[k].y - pt[k1].y );
+ cur.x = pt[k1].x - pt[i].x;
+ cur.y = pt[k1].y - pt[i].y;
+
+ /* find largest integer j such that xprod(constraint[0], cur+j*dk)
+ * >= 0 and xprod(constraint[1], cur+j*dk) <= 0. Use bilinearity
+ * of xprod. */
+ a = xprod( constraint[0], cur );
+ b = xprod( constraint[0], dk );
+ c = xprod( constraint[1], cur );
+ d = xprod( constraint[1], dk );
+
+ /* find largest integer j such that a+j*b>=0 and c+j*d<=0. This
+ * can be solved with integer arithmetic. */
+ j = INFTY;
+ if( b<0 )
+ {
+ j = floordiv( a, -b );
+ }
+ if( d>0 )
+ {
+ j = min( j, floordiv( -c, d ) );
+ }
+ pivk[i] = mod( k1 + j, n );
+foundk:
+ ;
+ } /* for i */
+
+ /* clean up: for each i, let lon[i] be the largest k such that for
+ * all i' with i<=i'<k, i'<k<=pivk[i']. */
+
+ j = pivk[n - 1];
+ pp->lon[n - 1] = j;
+ for( i = n - 2; i>=0; i-- )
+ {
+ if( cyclic( i + 1, pivk[i], j ) )
+ {
+ j = pivk[i];
+ }
+ pp->lon[i] = j;
+ }
+
+ for( i = n - 1; cyclic( mod( i + 1, n ), j, pp->lon[i] ); i-- )
+ {
+ pp->lon[i] = j;
+ }
+
+ free( pivk );
+ free( nc );
+ return 0;
+
+malloc_error:
+ free( pivk );
+ free( nc );
+ return 1;
+}
+
+
+/* ---------------------------------------------------------------------- */
+/* Stage 2: calculate the optimal polygon (Sec. 2.2.2-2.2.4). */
+
+/* Auxiliary function: calculate the penalty of an edge from i to j in
+ * the given path. This needs the "lon" and "sum*" data. */
+
+static double penalty3( privpath_t* pp, int i, int j )
+{
+ int n = pp->len;
+ point_t* pt = pp->pt;
+ sums_t* sums = pp->sums;
+
+ /* assume 0<=i<j<=n */
+ double x, y, x2, xy, y2;
+ double k;
+ double a, b, c, s;
+ double px, py, ex, ey;
+
+ int r = 0; /* rotations from i to j */
+
+ if( j>=n )
+ {
+ j -= n;
+ r += 1;
+ }
+
+ x = sums[j + 1].x - sums[i].x + r * sums[n].x;
+ y = sums[j + 1].y - sums[i].y + r * sums[n].y;
+ x2 = sums[j + 1].x2 - sums[i].x2 + r * sums[n].x2;
+ xy = sums[j + 1].xy - sums[i].xy + r * sums[n].xy;
+ y2 = sums[j + 1].y2 - sums[i].y2 + r * sums[n].y2;
+ k = j + 1 - i + r * n;
+
+ px = (pt[i].x + pt[j].x) / 2.0 - pt[0].x;
+ py = (pt[i].y + pt[j].y) / 2.0 - pt[0].y;
+ ey = (pt[j].x - pt[i].x);
+ ex = -(pt[j].y - pt[i].y);
+
+ a = ( (x2 - 2 * x * px) / k + px * px );
+ b = ( (xy - x * py - y * px) / k + px * py );
+ c = ( (y2 - 2 * y * py) / k + py * py );
+
+ s = ex * ex * a + 2 * ex * ey * b + ey * ey * c;
+
+ return sqrt( s );
+}
+
+
+/* find the optimal polygon. Fill in the m and po components. Return 1
+ * on failure with errno set, else 0. Non-cyclic version: assumes i=0
+ * is in the polygon. Fixme: ### implement cyclic version. */
+static int bestpolygon( privpath_t* pp )
+{
+ int i, j, m, k;
+ int n = pp->len;
+ double* pen = NULL; /* pen[n+1]: penalty vector */
+ int* prev = NULL; /* prev[n+1]: best path pointer vector */
+ int* clip0 = NULL; /* clip0[n]: longest segment pointer, non-cyclic */
+ int* clip1 = NULL; /* clip1[n+1]: backwards segment pointer, non-cyclic */
+ int* seg0 = NULL; /* seg0[m+1]: forward segment bounds, m<=n */
+ int* seg1 = NULL; /* seg1[m+1]: backward segment bounds, m<=n */
+ double thispen;
+ double best;
+ int c;
+
+ SAFE_MALLOC( pen, n + 1, double );
+ SAFE_MALLOC( prev, n + 1, int );
+ SAFE_MALLOC( clip0, n, int );
+ SAFE_MALLOC( clip1, n + 1, int );
+ SAFE_MALLOC( seg0, n + 1, int );
+ SAFE_MALLOC( seg1, n + 1, int );
+
+ /* calculate clipped paths */
+ for( i = 0; i<n; i++ )
+ {
+ c = mod( pp->lon[mod( i - 1, n )] - 1, n );
+ if( c == i )
+ {
+ c = mod( i + 1, n );
+ }
+ if( c < i )
+ {
+ clip0[i] = n;
+ }
+ else
+ {
+ clip0[i] = c;
+ }
+ }
+
+ /* calculate backwards path clipping, non-cyclic. j <= clip0[i] iff
+ * clip1[j] <= i, for i,j=0..n. */
+ j = 1;
+ for( i = 0; i<n; i++ )
+ {
+ while( j <= clip0[i] )
+ {
+ clip1[j] = i;
+ j++;
+ }
+ }
+
+ /* calculate seg0[j] = longest path from 0 with j segments */
+ i = 0;
+ for( j = 0; i<n; j++ )
+ {
+ seg0[j] = i;
+ i = clip0[i];
+ }
+
+ seg0[j] = n;
+ m = j;
+
+ /* calculate seg1[j] = longest path to n with m-j segments */
+ i = n;
+ for( j = m; j>0; j-- )
+ {
+ seg1[j] = i;
+ i = clip1[i];
+ }
+
+ seg1[0] = 0;
+
+ /* now find the shortest path with m segments, based on penalty3 */
+
+ /* note: the outer 2 loops jointly have at most n interations, thus
+ * the worst-case behavior here is quadratic. In practice, it is
+ * close to linear since the inner loop tends to be short. */
+ pen[0] = 0;
+ for( j = 1; j<=m; j++ )
+ {
+ for( i = seg1[j]; i<=seg0[j]; i++ )
+ {
+ best = -1;
+ for( k = seg0[j - 1]; k>=clip1[i]; k-- )
+ {
+ thispen = penalty3( pp, k, i ) + pen[k];
+ if( best < 0 || thispen < best )
+ {
+ prev[i] = k;
+ best = thispen;
+ }
+ }
+
+ pen[i] = best;
+ }
+ }
+
+ pp->m = m;
+ SAFE_MALLOC( pp->po, m, int );
+
+ /* read off shortest path */
+ for( i = n, j = m - 1; i>0; j-- )
+ {
+ i = prev[i];
+ pp->po[j] = i;
+ }
+
+ free( pen );
+ free( prev );
+ free( clip0 );
+ free( clip1 );
+ free( seg0 );
+ free( seg1 );
+ return 0;
+
+malloc_error:
+ free( pen );
+ free( prev );
+ free( clip0 );
+ free( clip1 );
+ free( seg0 );
+ free( seg1 );
+ return 1;
+}
+
+
+/* ---------------------------------------------------------------------- */
+/* Stage 3: vertex adjustment (Sec. 2.3.1). */
+
+/* Adjust vertices of optimal polygon: calculate the intersection of
+ * the two "optimal" line segments, then move it into the unit square
+ * if it lies outside. Return 1 with errno set on error; 0 on
+ * success. */
+
+static int adjust_vertices( privpath_t* pp )
+{
+ int m = pp->m;
+ int* po = pp->po;
+ int n = pp->len;
+ point_t* pt = pp->pt;
+ int x0 = pp->x0;
+ int y0 = pp->y0;
+
+ dpoint_t* ctr = NULL; /* ctr[m] */
+ dpoint_t* dir = NULL; /* dir[m] */
+ quadform_t* q = NULL; /* q[m] */
+ double v[3];
+ double d;
+ int i, j, k, l;
+ dpoint_t s;
+ int r;
+
+ SAFE_MALLOC( ctr, m, dpoint_t );
+ SAFE_MALLOC( dir, m, dpoint_t );
+ SAFE_MALLOC( q, m, quadform_t );
+
+ r = privcurve_init( &pp->curve, m );
+ if( r )
+ {
+ goto malloc_error;
+ }
+
+ /* calculate "optimal" point-slope representation for each line
+ * segment */
+ for( i = 0; i<m; i++ )
+ {
+ j = po[mod( i + 1, m )];
+ j = mod( j - po[i], n ) + po[i];
+ pointslope( pp, po[i], j, &ctr[i], &dir[i] );
+ }
+
+ /* represent each line segment as a singular quadratic form; the
+ * distance of a point (x,y) from the line segment will be
+ * (x,y,1)Q(x,y,1)^t, where Q=q[i]. */
+ for( i = 0; i<m; i++ )
+ {
+ d = sq( dir[i].x ) + sq( dir[i].y );
+ if( d == 0.0 )
+ {
+ for( j = 0; j<3; j++ )
+ {
+ for( k = 0; k<3; k++ )
+ {
+ q[i][j][k] = 0;
+ }
+ }
+ }
+ else
+ {
+ v[0] = dir[i].y;
+ v[1] = -dir[i].x;
+ v[2] = -v[1] *ctr[i].y - v[0] *ctr[i].x;
+ for( l = 0; l<3; l++ )
+ {
+ for( k = 0; k<3; k++ )
+ {
+ q[i][l][k] = v[l] *v[k] / d;
+ }
+ }
+ }
+ }
+
+ /* now calculate the "intersections" of consecutive segments.
+ * Instead of using the actual intersection, we find the point
+ * within a given unit square which minimizes the square distance to
+ * the two lines. */
+ for( i = 0; i<m; i++ )
+ {
+ quadform_t Q;
+ dpoint_t w;
+ double dx, dy;
+ double det;
+ double min, cand; /* minimum and candidate for minimum of quad. form */
+ double xmin, ymin; /* coordinates of minimum */
+ int z;
+
+ /* let s be the vertex, in coordinates relative to x0/y0 */
+ s.x = pt[po[i]].x - x0;
+ s.y = pt[po[i]].y - y0;
+
+ /* intersect segments i-1 and i */
+
+ j = mod( i - 1, m );
+
+ /* add quadratic forms */
+ for( l = 0; l<3; l++ )
+ {
+ for( k = 0; k<3; k++ )
+ {
+ Q[l][k] = q[j][l][k] + q[i][l][k];
+ }
+ }
+
+ while( 1 )
+ {
+ /* minimize the quadratic form Q on the unit square */
+ /* find intersection */
+
+#ifdef HAVE_GCC_LOOP_BUG
+ /* work around gcc bug #12243 */
+ free( NULL );
+#endif
+
+ det = Q[0][0] *Q[1][1] - Q[0][1] *Q[1][0];
+ if( det != 0.0 )
+ {
+ w.x = (-Q[0][2] *Q[1][1] + Q[1][2] *Q[0][1]) / det;
+ w.y = ( Q[0][2] *Q[1][0] - Q[1][2] *Q[0][0]) / det;
+ break;
+ }
+
+ /* matrix is singular - lines are parallel. Add another,
+ * orthogonal axis, through the center of the unit square */
+ if( Q[0][0]>Q[1][1] )
+ {
+ v[0] = -Q[0][1];
+ v[1] = Q[0][0];
+ }
+ else if( Q[1][1] )
+ {
+ v[0] = -Q[1][1];
+ v[1] = Q[1][0];
+ }
+ else
+ {
+ v[0] = 1;
+ v[1] = 0;
+ }
+ d = sq( v[0] ) + sq( v[1] );
+ v[2] = -v[1] *s.y - v[0] *s.x;
+ for( l = 0; l<3; l++ )
+ {
+ for( k = 0; k<3; k++ )
+ {
+ Q[l][k] += v[l] *v[k] / d;
+ }
+ }
+ }
+
+ dx = fabs( w.x - s.x );
+ dy = fabs( w.y - s.y );
+ if( dx <= .5 && dy <= .5 )
+ {
+ pp->curve.vertex[i].x = w.x + x0;
+ pp->curve.vertex[i].y = w.y + y0;
+ continue;
+ }
+
+ /* the minimum was not in the unit square; now minimize quadratic
+ * on boundary of square */
+ min = quadform( Q, s );
+ xmin = s.x;
+ ymin = s.y;
+
+ if( Q[0][0] == 0.0 )
+ {
+ goto fixx;
+ }
+ for( z = 0; z<2; z++ ) /* value of the y-coordinate */
+ {
+ w.y = s.y - 0.5 + z;
+ w.x = -(Q[0][1] *w.y + Q[0][2]) / Q[0][0];
+ dx = fabs( w.x - s.x );
+ cand = quadform( Q, w );
+ if( dx <= .5 && cand < min )
+ {
+ min = cand;
+ xmin = w.x;
+ ymin = w.y;
+ }
+ }
+
+fixx:
+ if( Q[1][1] == 0.0 )
+ {
+ goto corners;
+ }
+ for( z = 0; z<2; z++ ) /* value of the x-coordinate */
+ {
+ w.x = s.x - 0.5 + z;
+ w.y = -(Q[1][0] *w.x + Q[1][2]) / Q[1][1];
+ dy = fabs( w.y - s.y );
+ cand = quadform( Q, w );
+ if( dy <= .5 && cand < min )
+ {
+ min = cand;
+ xmin = w.x;
+ ymin = w.y;
+ }
+ }
+
+corners:
+ /* check four corners */
+ for( l = 0; l<2; l++ )
+ {
+ for( k = 0; k<2; k++ )
+ {
+ w.x = s.x - 0.5 + l;
+ w.y = s.y - 0.5 + k;
+ cand = quadform( Q, w );
+ if( cand < min )
+ {
+ min = cand;
+ xmin = w.x;
+ ymin = w.y;
+ }
+ }
+ }
+
+ pp->curve.vertex[i].x = xmin + x0;
+ pp->curve.vertex[i].y = ymin + y0;
+ continue;
+ }
+
+ free( ctr );
+ free( dir );
+ free( q );
+ return 0;
+
+malloc_error:
+ free( ctr );
+ free( dir );
+ free( q );
+ return 1;
+}
+
+
+/* ---------------------------------------------------------------------- */
+/* Stage 4: smoothing and corner analysis (Sec. 2.3.3) */
+
+/* Always succeeds and returns 0 */
+static int smooth( privcurve_t* curve, int sign, double alphamax )
+{
+ int m = curve->n;
+
+ int i, j, k;
+ double dd, denom, alpha;
+ dpoint_t p2, p3, p4;
+
+ if( sign == '-' )
+ {
+ /* reverse orientation of negative paths */
+ for( i = 0, j = m - 1; i<j; i++, j-- )
+ {
+ dpoint_t tmp;
+ tmp = curve->vertex[i];
+ curve->vertex[i] = curve->vertex[j];
+ curve->vertex[j] = tmp;
+ }
+ }
+
+ /* examine each vertex and find its best fit */
+ for( i = 0; i<m; i++ )
+ {
+ j = mod( i + 1, m );
+ k = mod( i + 2, m );
+ p4 = interval( 1 / 2.0, curve->vertex[k], curve->vertex[j] );
+
+ denom = ddenom( curve->vertex[i], curve->vertex[k] );
+ if( denom != 0.0 )
+ {
+ dd = dpara( curve->vertex[i], curve->vertex[j], curve->vertex[k] ) / denom;
+ dd = fabs( dd );
+ alpha = dd>1 ? (1 - 1.0 / dd) : 0;
+ alpha = alpha / 0.75;
+ }
+ else
+ {
+ alpha = 4 / 3.0;
+ }
+ curve->alpha0[j] = alpha; /* remember "original" value of alpha */
+
+ if( alpha > alphamax ) /* pointed corner */
+ {
+ curve->tag[j] = POTRACE_CORNER;
+ curve->c[j][1] = curve->vertex[j];
+ curve->c[j][2] = p4;
+ }
+ else
+ {
+ if( alpha < 0.55 )
+ {
+ alpha = 0.55;
+ }
+ else if( alpha > 1 )
+ {
+ alpha = 1;
+ }
+ p2 = interval( .5 + .5 * alpha, curve->vertex[i], curve->vertex[j] );
+ p3 = interval( .5 + .5 * alpha, curve->vertex[k], curve->vertex[j] );
+ curve->tag[j] = POTRACE_CURVETO;
+ curve->c[j][0] = p2;
+ curve->c[j][1] = p3;
+ curve->c[j][2] = p4;
+ }
+ curve->alpha[j] = alpha; /* store the "cropped" value of alpha */
+ curve->beta[j] = 0.5;
+ }
+
+ curve->alphacurve = 1;
+
+ return 0;
+}
+
+
+/* ---------------------------------------------------------------------- */
+/* Stage 5: Curve optimization (Sec. 2.4) */
+
+/* a private type for the result of opti_penalty */
+struct opti_s
+{
+ double pen; /* penalty */
+ dpoint_t c[2]; /* curve parameters */
+ double t, s; /* curve parameters */
+ double alpha; /* curve parameter */
+};
+typedef struct opti_s opti_t;
+
+/* calculate best fit from i+.5 to j+.5. Assume i<j (cyclically).
+ * Return 0 and set badness and parameters (alpha, beta), if
+ * possible. Return 1 if impossible. */
+static int opti_penalty( privpath_t* pp,
+ int i,
+ int j,
+ opti_t* res,
+ double opttolerance,
+ int* convc,
+ double* areac )
+{
+ int m = pp->curve.n;
+ int k, k1, k2, conv, i1;
+ double area, alpha, d, d1, d2;
+ dpoint_t p0, p1, p2, p3, pt;
+ double A, R, A1, A2, A3, A4;
+ double s, t;
+
+ /* check convexity, corner-freeness, and maximum bend < 179 degrees */
+
+ if( i==j ) /* sanity - a full loop can never be an opticurve */
+ {
+ return 1;
+ }
+
+ k = i;
+ i1 = mod( i + 1, m );
+ k1 = mod( k + 1, m );
+ conv = convc[k1];
+ if( conv == 0 )
+ {
+ return 1;
+ }
+ d = ddist( pp->curve.vertex[i], pp->curve.vertex[i1] );
+ for( k = k1; k!=j; k = k1 )
+ {
+ k1 = mod( k + 1, m );
+ k2 = mod( k + 2, m );
+ if( convc[k1] != conv )
+ {
+ return 1;
+ }
+ if( sign( cprod( pp->curve.vertex[i], pp->curve.vertex[i1], pp->curve.vertex[k1],
+ pp->curve.vertex[k2] ) ) != conv )
+ {
+ return 1;
+ }
+ if( iprod1( pp->curve.vertex[i], pp->curve.vertex[i1], pp->curve.vertex[k1],
+ pp->curve.vertex[k2] ) < d *
+ ddist( pp->curve.vertex[k1], pp->curve.vertex[k2] ) * COS179 )
+ {
+ return 1;
+ }
+ }
+
+ /* the curve we're working in: */
+ p0 = pp->curve.c[mod( i, m )][2];
+ p1 = pp->curve.vertex[mod( i + 1, m )];
+ p2 = pp->curve.vertex[mod( j, m )];
+ p3 = pp->curve.c[mod( j, m )][2];
+
+ /* determine its area */
+ area = areac[j] - areac[i];
+ area -= dpara( pp->curve.vertex[0], pp->curve.c[i][2], pp->curve.c[j][2] ) / 2;
+ if( i>=j )
+ {
+ area += areac[m];
+ }
+
+ /* find intersection o of p0p1 and p2p3. Let t,s such that o =
+ * interval(t,p0,p1) = interval(s,p3,p2). Let A be the area of the
+ * triangle (p0,o,p3). */
+
+ A1 = dpara( p0, p1, p2 );
+ A2 = dpara( p0, p1, p3 );
+ A3 = dpara( p0, p2, p3 );
+ /* A4 = dpara(p1, p2, p3); */
+ A4 = A1 + A3 - A2;
+
+ if( A2 == A1 ) /* this should never happen */
+ {
+ return 1;
+ }
+
+ t = A3 / (A3 - A4);
+ s = A2 / (A2 - A1);
+ A = A2 * t / 2.0;
+
+ if( A == 0.0 ) /* this should never happen */
+ {
+ return 1;
+ }
+
+ R = area / A; /* relative area */
+ alpha = 2 - sqrt( 4 - R / 0.3 ); /* overall alpha for p0-o-p3 curve */
+
+ res->c[0] = interval( t * alpha, p0, p1 );
+ res->c[1] = interval( s * alpha, p3, p2 );
+ res->alpha = alpha;
+ res->t = t;
+ res->s = s;
+
+ p1 = res->c[0];
+ p2 = res->c[1]; /* the proposed curve is now (p0,p1,p2,p3) */
+
+ res->pen = 0;
+
+ /* calculate penalty */
+ /* check tangency with edges */
+ for( k = mod( i + 1, m ); k!=j; k = k1 )
+ {
+ k1 = mod( k + 1, m );
+ t = tangent( p0, p1, p2, p3, pp->curve.vertex[k], pp->curve.vertex[k1] );
+ if( t<-.5 )
+ {
+ return 1;
+ }
+ pt = bezier( t, p0, p1, p2, p3 );
+ d = ddist( pp->curve.vertex[k], pp->curve.vertex[k1] );
+ if( d == 0.0 ) /* this should never happen */
+ {
+ return 1;
+ }
+ d1 = dpara( pp->curve.vertex[k], pp->curve.vertex[k1], pt ) / d;
+ if( fabs( d1 ) > opttolerance )
+ {
+ return 1;
+ }
+ if( iprod( pp->curve.vertex[k], pp->curve.vertex[k1],
+ pt ) < 0 || iprod( pp->curve.vertex[k1], pp->curve.vertex[k], pt ) < 0 )
+ {
+ return 1;
+ }
+ res->pen += sq( d1 );
+ }
+
+ /* check corners */
+ for( k = i; k!=j; k = k1 )
+ {
+ k1 = mod( k + 1, m );
+ t = tangent( p0, p1, p2, p3, pp->curve.c[k][2], pp->curve.c[k1][2] );
+ if( t<-.5 )
+ {
+ return 1;
+ }
+ pt = bezier( t, p0, p1, p2, p3 );
+ d = ddist( pp->curve.c[k][2], pp->curve.c[k1][2] );
+ if( d == 0.0 ) /* this should never happen */
+ {
+ return 1;
+ }
+ d1 = dpara( pp->curve.c[k][2], pp->curve.c[k1][2], pt ) / d;
+ d2 = dpara( pp->curve.c[k][2], pp->curve.c[k1][2], pp->curve.vertex[k1] ) / d;
+ d2 *= 0.75 * pp->curve.alpha[k1];
+ if( d2 < 0 )
+ {
+ d1 = -d1;
+ d2 = -d2;
+ }
+ if( d1 < d2 - opttolerance )
+ {
+ return 1;
+ }
+ if( d1 < d2 )
+ {
+ res->pen += sq( d1 - d2 );
+ }
+ }
+
+ return 0;
+}
+
+
+/* optimize the path p, replacing sequences of Bezier segments by a
+ * single segment when possible. Return 0 on success, 1 with errno set
+ * on failure. */
+static int opticurve( privpath_t* pp, double opttolerance )
+{
+ int seg_count = pp->curve.n; // segment count in pp->curve
+ int* pt = NULL; /* pt[m+1] */
+ double* pen = NULL; /* pen[m+1] */
+ int* len = NULL; /* len[m+1] */
+ opti_t* opt = NULL; /* opt[m+1] */
+ int om;
+ int j, r;
+ opti_t curve_prms;
+ dpoint_t p0;
+ int i1;
+ double area;
+ double alpha;
+ double* s = NULL;
+ double* t = NULL;
+
+ int* convc = NULL; /* conv[m]: pre-computed convexities */
+ double* areac = NULL; /* cumarea[m+1]: cache for fast area computation */
+
+ SAFE_MALLOC( pt, seg_count + 1, int );
+ SAFE_MALLOC( pen, seg_count + 1, double );
+ SAFE_MALLOC( len, seg_count + 1, int );
+ SAFE_MALLOC( opt, seg_count + 1, opti_t );
+ SAFE_MALLOC( convc, seg_count, int );
+ SAFE_MALLOC( areac, seg_count + 1, double );
+
+ /* pre-calculate convexity: +1 = right turn, -1 = left turn, 0 = corner */
+ for( int i = 0; i<seg_count; i++ )
+ {
+ if( pp->curve.tag[i] == POTRACE_CURVETO )
+ {
+ convc[i] =
+ sign( dpara( pp->curve.vertex[mod( i - 1, seg_count )],
+ pp->curve.vertex[i],
+ pp->curve.vertex[mod( i + 1, seg_count )] ) );
+ }
+ else
+ {
+ convc[i] = 0;
+ }
+ }
+
+ /* pre-calculate areas */
+ area = 0.0;
+ areac[0] = 0.0;
+ p0 = pp->curve.vertex[0];
+ for( int i = 0; i<seg_count; i++ )
+ {
+ i1 = mod( i + 1, seg_count );
+ if( pp->curve.tag[i1] == POTRACE_CURVETO )
+ {
+ alpha = pp->curve.alpha[i1];
+ area += 0.3 * alpha * (4 - alpha) * dpara( pp->curve.c[i][2],
+ pp->curve.vertex[i1],
+ pp->curve.c[i1][2] ) / 2;
+ area += dpara( p0, pp->curve.c[i][2], pp->curve.c[i1][2] ) / 2;
+ }
+ areac[i + 1] = area;
+ }
+
+ pt[0] = -1;
+ pen[0] = 0;
+ len[0] = 0;
+
+ // Avoid not initialized value for opt[j] (should not occur, but...)
+ for( j = 0; j<=seg_count; j++ )
+ {
+ opt[j].pen = 0.0; // penalty
+ opt[j].c[0].x = opt[j].c[1].x = opt[j].c[0].y = opt[j].c[1].y = 0;
+ opt[j].t = opt[j].s = opt[j].alpha = 0.0; // curve parameters
+ }
+
+
+ /* Fixme: we always start from a fixed point -- should find the best
+ * curve cyclically ### */
+
+ for( j = 1; j<=seg_count; j++ )
+ {
+ /* calculate best path from 0 to j */
+ pt[j] = j - 1;
+ pen[j] = pen[j - 1];
+ len[j] = len[j - 1] + 1;
+
+ for( int i = j - 2; i>=0; i-- )
+ {
+ r = opti_penalty( pp, i, mod( j, seg_count ), &curve_prms, opttolerance, convc, areac );
+ if( r )
+ {
+ break;
+ }
+ if( len[j] > len[i] + 1 || (len[j] == len[i] + 1 && pen[j] > pen[i] + curve_prms.pen) )
+ {
+ pt[j] = i;
+ pen[j] = pen[i] + curve_prms.pen;
+ len[j] = len[i] + 1;
+ opt[j] = curve_prms;
+ }
+ }
+ }
+
+ om = len[seg_count];
+ r = privcurve_init( &pp->ocurve, om );
+ if( r )
+ {
+ goto malloc_error;
+ }
+ SAFE_MALLOC( s, om, double );
+ SAFE_MALLOC( t, om, double );
+
+ j = seg_count;
+ for( int i = om - 1; i>=0; i-- )
+ {
+ if( pt[j]==j - 1 )
+ {
+ pp->ocurve.tag[i] = pp->curve.tag[mod( j, seg_count )];
+ pp->ocurve.c[i][0] = pp->curve.c[mod( j, seg_count )][0];
+ pp->ocurve.c[i][1] = pp->curve.c[mod( j, seg_count )][1];
+ pp->ocurve.c[i][2] = pp->curve.c[mod( j, seg_count )][2];
+ pp->ocurve.vertex[i] = pp->curve.vertex[mod( j, seg_count )];
+ pp->ocurve.alpha[i] = pp->curve.alpha[mod( j, seg_count )];
+ pp->ocurve.alpha0[i] = pp->curve.alpha0[mod( j, seg_count )];
+ pp->ocurve.beta[i] = pp->curve.beta[mod( j, seg_count )];
+ s[i] = t[i] = 1.0;
+ }
+ else
+ {
+ pp->ocurve.tag[i] = POTRACE_CURVETO;
+ pp->ocurve.c[i][0] = opt[j].c[0];
+ pp->ocurve.c[i][1] = opt[j].c[1];
+ pp->ocurve.c[i][2] = pp->curve.c[mod( j, seg_count )][2];
+ pp->ocurve.vertex[i] = interval( opt[j].s, pp->curve.c[mod( j, seg_count )][2],
+ pp->curve.vertex[mod( j, seg_count )] );
+ pp->ocurve.alpha[i] = opt[j].alpha;
+ pp->ocurve.alpha0[i] = opt[j].alpha;
+ s[i] = opt[j].s;
+ t[i] = opt[j].t;
+ }
+ j = pt[j];
+ }
+
+ /* calculate beta parameters */
+ for( int i = 0; i<om; i++ )
+ {
+ i1 = mod( i + 1, om );
+ pp->ocurve.beta[i] = s[i] / (s[i] + t[i1]);
+ }
+
+ pp->ocurve.alphacurve = 1;
+
+ free( pt );
+ free( pen );
+ free( len );
+ free( opt );
+ free( s );
+ free( t );
+ free( convc );
+ free( areac );
+ return 0;
+
+malloc_error:
+ free( pt );
+ free( pen );
+ free( len );
+ free( opt );
+ free( s );
+ free( t );
+ free( convc );
+ free( areac );
+ return 1;
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+#define TRY( x ) if( x ) \
+ goto try_error
+
+/* return 0 on success, 1 on error with errno set. */
+int process_path( path_t* plist, const potrace_param_t* param, progress_t* progress )
+{
+ path_t* p;
+ double nn = 0, cn = 0;
+
+ if( progress->callback )
+ {
+ /* precompute task size for progress estimates */
+ nn = 0;
+ list_forall( p, plist ) {
+ nn += p->priv->len;
+ }
+ cn = 0;
+ }
+
+ /* call downstream function with each path */
+ list_forall( p, plist ) {
+ TRY( calc_sums( p->priv ) );
+ TRY( calc_lon( p->priv ) );
+ TRY( bestpolygon( p->priv ) );
+ TRY( adjust_vertices( p->priv ) );
+ TRY( smooth( &p->priv->curve, p->sign, param->alphamax ) );
+ if( param->opticurve )
+ {
+ TRY( opticurve( p->priv, param->opttolerance ) );
+ p->priv->fcurve = &p->priv->ocurve;
+ }
+ else
+ {
+ p->priv->fcurve = &p->priv->curve;
+ }
+ privcurve_to_curve( p->priv->fcurve, &p->curve );
+
+ if( progress->callback )
+ {
+ cn += p->priv->len;
+ progress_update( cn / nn, progress );
+ }
+ }
+
+ progress_update( 1.0, progress );
+
+ return 0;
+
+try_error:
+ return 1;
+}
diff --git a/potrace/trace.h b/potrace/trace.h
new file mode 100644
index 0000000..829bc90
--- /dev/null
+++ b/potrace/trace.h
@@ -0,0 +1,15 @@
+/* Copyright (C) 2001-2007 Peter Selinger.
+ * This file is part of Potrace. It is free software and it is covered
+ * by the GNU General Public License. See the file COPYING for details. */
+
+/* $Id: trace.h 147 2007-04-09 00:44:09Z selinger $ */
+
+#ifndef TRACE_H
+#define TRACE_H
+
+#include <potracelib.h>
+#include <progress.h>
+
+int process_path( path_t* plist, const potrace_param_t* param, progress_t* progress );
+
+#endif /* TRACE_H */
diff --git a/qa/CMakeLists.txt b/qa/CMakeLists.txt
new file mode 100644
index 0000000..796575d
--- /dev/null
+++ b/qa/CMakeLists.txt
@@ -0,0 +1,15 @@
+if( KICAD_SCRIPTING_MODULES )
+
+ if( APPLE AND ( KICAD_BUILD_STATIC OR KICAD_BUILD_DYNAMIC ) )
+ set( PYTHON_QA_PATH :${LIBWXPYTHON_ROOT}/wxPython/lib/python2.6/site-packages )
+ endif()
+
+ # build target that runs the QA tests through scripting
+ add_custom_target( qa
+ COMMAND PYTHONPATH=${CMAKE_BINARY_DIR}/pcbnew${PYTHON_QA_PATH} ${PYTHON_EXECUTABLE} test.py
+
+ COMMENT "running qa"
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+ )
+
+endif()
diff --git a/qa/data/complex_hierarchy.kicad_pcb b/qa/data/complex_hierarchy.kicad_pcb
new file mode 100644
index 0000000..66dbd63
--- /dev/null
+++ b/qa/data/complex_hierarchy.kicad_pcb
@@ -0,0 +1,3795 @@
+(kicad_pcb (version 4) (host pcbnew "(2014-06-30 BZR 4958)-product")
+
+ (general
+ (links 112)
+ (no_connects 0)
+ (area 84.518499 48.069499 192.960172 137.5918)
+ (thickness 1.6002)
+ (drawings 6)
+ (tracks 361)
+ (zones 0)
+ (modules 72)
+ (nets 51)
+ )
+
+ (page A4)
+ (title_block
+ (title Actionneur_piezo)
+ )
+
+ (layers
+ (0 Composant power)
+ (31 Cuivre signal)
+ (34 B.Paste user)
+ (35 F.Paste user)
+ (36 B.SilkS user)
+ (37 F.SilkS user)
+ (38 B.Mask user)
+ (39 F.Mask user)
+ (40 Dwgs.User user)
+ (41 Cmts.User user)
+ (42 Eco1.User user)
+ (43 Eco2.User user)
+ (44 Edge.Cuts user)
+ )
+
+ (setup
+ (last_trace_width 0.4318)
+ (trace_clearance 0.2794)
+ (zone_clearance 0.508)
+ (zone_45_only no)
+ (trace_min 0.2032)
+ (segment_width 0.381)
+ (edge_width 0.2032)
+ (via_size 1.651)
+ (via_drill 0.635)
+ (via_min_size 0.889)
+ (via_min_drill 0.508)
+ (uvia_size 0.508)
+ (uvia_drill 0.2032)
+ (uvias_allowed no)
+ (uvia_min_size 0.508)
+ (uvia_min_drill 0.2032)
+ (pcb_text_width 0.3048)
+ (pcb_text_size 1.524 2.032)
+ (mod_edge_width 0.381)
+ (mod_text_size 1.524 1.524)
+ (mod_text_width 0.3048)
+ (pad_size 2.286 2.286)
+ (pad_drill 0.8128)
+ (pad_to_mask_clearance 0.254)
+ (aux_axis_origin 0 0)
+ (visible_elements 7FFFFFFF)
+ (pcbplotparams
+ (layerselection 0x000f0_80000001)
+ (usegerberextensions false)
+ (usegerberextensions false)
+ (excludeedgelayer true)
+ (linewidth 0.150000)
+ (plotframeref false)
+ (viasonmask false)
+ (mode 1)
+ (useauxorigin false)
+ (hpglpennumber 1)
+ (hpglpenspeed 20)
+ (hpglpendiameter 15)
+ (hpglpenoverlay 2)
+ (psnegative false)
+ (psa4output false)
+ (plotreference true)
+ (plotvalue true)
+ (plotinvisibletext false)
+ (padsonsilk false)
+ (subtractmaskfromsilk false)
+ (outputformat 1)
+ (mirror false)
+ (drillshape 0)
+ (scaleselection 1)
+ (outputdirectory ""))
+ )
+
+ (net 0 "")
+ (net 1 +12V)
+ (net 2 -VAA)
+ (net 3 /12Vext)
+ (net 4 /ampli_h1)
+ (net 5 /ampli_h2)
+ (net 6 /ampli_h3)
+ (net 7 /ampli_h4)
+ (net 8 /ampli_h5)
+ (net 9 /ampli_h6)
+ (net 10 /ampli_h7)
+ (net 11 /ampli_h8)
+ (net 12 GND)
+ (net 13 HT)
+ (net 14 N-000005)
+ (net 15 N-000010)
+ (net 16 N-000011)
+ (net 17 N-000012)
+ (net 18 N-000013)
+ (net 19 N-000014)
+ (net 20 N-000015)
+ (net 21 N-000016)
+ (net 22 N-000017)
+ (net 23 N-000018)
+ (net 24 N-000019)
+ (net 25 N-000020)
+ (net 26 N-000021)
+ (net 27 N-000022)
+ (net 28 N-000023)
+ (net 29 N-000024)
+ (net 30 N-000025)
+ (net 31 N-000026)
+ (net 32 N-000027)
+ (net 33 N-000032)
+ (net 34 N-000033)
+ (net 35 N-000034)
+ (net 36 N-000035)
+ (net 37 N-000036)
+ (net 38 N-000037)
+ (net 39 N-000038)
+ (net 40 N-000039)
+ (net 41 N-000040)
+ (net 42 N-000041)
+ (net 43 N-000042)
+ (net 44 N-000043)
+ (net 45 N-000044)
+ (net 46 N-000045)
+ (net 47 N-000046)
+ (net 48 N-000047)
+ (net 49 N-000048)
+ (net 50 VCC)
+
+ (net_class Default "Ceci est la Netclass par défaut"
+ (clearance 0.2794)
+ (trace_width 0.4318)
+ (via_dia 1.651)
+ (via_drill 0.635)
+ (uvia_dia 0.508)
+ (uvia_drill 0.2032)
+ (add_net +12V)
+ (add_net /ampli_h1)
+ (add_net /ampli_h2)
+ (add_net /ampli_h3)
+ (add_net /ampli_h4)
+ (add_net /ampli_h5)
+ (add_net /ampli_h6)
+ (add_net /ampli_h7)
+ (add_net /ampli_h8)
+ (add_net N-000005)
+ (add_net N-000010)
+ (add_net N-000011)
+ (add_net N-000012)
+ (add_net N-000013)
+ (add_net N-000014)
+ (add_net N-000015)
+ (add_net N-000016)
+ (add_net N-000017)
+ (add_net N-000018)
+ (add_net N-000019)
+ (add_net N-000020)
+ (add_net N-000021)
+ (add_net N-000022)
+ (add_net N-000023)
+ (add_net N-000024)
+ (add_net N-000025)
+ (add_net N-000026)
+ (add_net N-000027)
+ (add_net N-000032)
+ (add_net N-000033)
+ (add_net N-000034)
+ (add_net N-000035)
+ (add_net N-000036)
+ (add_net N-000037)
+ (add_net N-000038)
+ (add_net N-000039)
+ (add_net N-000040)
+ (add_net N-000041)
+ (add_net N-000042)
+ (add_net N-000043)
+ (add_net N-000044)
+ (add_net N-000045)
+ (add_net N-000046)
+ (add_net N-000047)
+ (add_net N-000048)
+ )
+
+ (net_class power ""
+ (clearance 0.381)
+ (trace_width 0.6096)
+ (via_dia 1.651)
+ (via_drill 0.635)
+ (uvia_dia 0.508)
+ (uvia_drill 0.2032)
+ (add_net -VAA)
+ (add_net /12Vext)
+ (add_net GND)
+ (add_net HT)
+ (add_net VCC)
+ )
+
+ (module bornier2 (layer Composant) (tedit 3EC0ED69) (tstamp 4AE19637)
+ (at 95.885 59.436 270)
+ (descr "Bornier d'alimentation 2 pins")
+ (tags DEV)
+ (path /4B3A12F4)
+ (fp_text reference P1 (at 0 -5.08 270) (layer F.SilkS)
+ (effects (font (thickness 0.3048)))
+ )
+ (fp_text value CONN_2 (at 0 5.08 270) (layer F.SilkS)
+ (effects (font (thickness 0.3048)))
+ )
+ (fp_line (start 5.08 2.54) (end -5.08 2.54) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 5.08 3.81) (end 5.08 -3.81) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 5.08 -3.81) (end -5.08 -3.81) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -5.08 -3.81) (end -5.08 3.81) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -5.08 3.81) (end 5.08 3.81) (layer F.SilkS) (width 0.3048))
+ (pad 1 thru_hole rect (at -2.54 0 270) (size 2.54 2.54) (drill 1.524) (layers *.Cu *.Mask F.SilkS)
+ (net 13 HT))
+ (pad 2 thru_hole circle (at 2.54 0 270) (size 2.54 2.54) (drill 1.524) (layers *.Cu *.Mask F.SilkS)
+ (net 12 GND))
+ (model device/bornier_2.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 1 1 1))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module bornier2 (layer Composant) (tedit 3EC0ED69) (tstamp 4AE19639)
+ (at 182.245 120.396 90)
+ (descr "Bornier d'alimentation 2 pins")
+ (tags DEV)
+ (path /4B3A1333/4B3A136C)
+ (fp_text reference P3 (at 0 -5.08 90) (layer F.SilkS)
+ (effects (font (thickness 0.3048)))
+ )
+ (fp_text value CONN_2 (at 0 5.08 90) (layer F.SilkS)
+ (effects (font (thickness 0.3048)))
+ )
+ (fp_line (start 5.08 2.54) (end -5.08 2.54) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 5.08 3.81) (end 5.08 -3.81) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 5.08 -3.81) (end -5.08 -3.81) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -5.08 -3.81) (end -5.08 3.81) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -5.08 3.81) (end 5.08 3.81) (layer F.SilkS) (width 0.3048))
+ (pad 1 thru_hole rect (at -2.54 0 90) (size 2.54 2.54) (drill 1.524) (layers *.Cu *.Mask F.SilkS)
+ (net 6 /ampli_h3))
+ (pad 2 thru_hole circle (at 2.54 0 90) (size 2.54 2.54) (drill 1.524) (layers *.Cu *.Mask F.SilkS)
+ (net 12 GND))
+ (model device/bornier_2.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 1 1 1))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module CP6 (layer Composant) (tedit 200000) (tstamp 4AE1963F)
+ (at 133.985 60.706 180)
+ (descr "Condensateur polarise")
+ (tags CP)
+ (path /4AE173CF)
+ (fp_text reference C2 (at 0 0 180) (layer F.SilkS)
+ (effects (font (thickness 0.3048)))
+ )
+ (fp_text value 47uF/20V (at 0.635 0 180) (layer F.SilkS) hide
+ (effects (font (thickness 0.3048)))
+ )
+ (fp_line (start -7.62 0) (end -6.604 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -6.096 0.508) (end -6.604 0.508) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -6.604 0.508) (end -6.604 -0.508) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -6.604 -0.508) (end -6.096 -0.508) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 7.62 0) (end 6.604 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 6.604 0) (end 6.604 -1.524) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 6.604 -1.524) (end -6.096 -1.524) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -6.096 -1.524) (end -6.096 1.524) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -6.096 1.524) (end 6.604 1.524) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 6.604 1.524) (end 6.604 0) (layer F.SilkS) (width 0.3048))
+ (pad 1 thru_hole rect (at -7.62 0 180) (size 2.159 2.159) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 1 +12V))
+ (pad 2 thru_hole circle (at 7.62 0 180) (size 2.159 2.159) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 12 GND))
+ (model discret/c_pol.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 0.6 0.6 0.6))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module CP6 (layer Composant) (tedit 200000) (tstamp 4AE19649)
+ (at 109.855 74.676 180)
+ (descr "Condensateur polarise")
+ (tags CP)
+ (path /4B03CEC2)
+ (fp_text reference C1 (at 0 0 180) (layer F.SilkS)
+ (effects (font (thickness 0.3048)))
+ )
+ (fp_text value 47uF (at 0.635 0 180) (layer F.SilkS) hide
+ (effects (font (thickness 0.3048)))
+ )
+ (fp_line (start -7.62 0) (end -6.604 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -6.096 0.508) (end -6.604 0.508) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -6.604 0.508) (end -6.604 -0.508) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -6.604 -0.508) (end -6.096 -0.508) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 7.62 0) (end 6.604 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 6.604 0) (end 6.604 -1.524) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 6.604 -1.524) (end -6.096 -1.524) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -6.096 -1.524) (end -6.096 1.524) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -6.096 1.524) (end 6.604 1.524) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 6.604 1.524) (end 6.604 0) (layer F.SilkS) (width 0.3048))
+ (pad 1 thru_hole rect (at -7.62 0 180) (size 2.159 2.159) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 50 VCC))
+ (pad 2 thru_hole circle (at 7.62 0 180) (size 2.159 2.159) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 12 GND))
+ (model discret/c_pol.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 0.6 0.6 0.6))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module D5 (layer Composant) (tedit 4AE6E2DB) (tstamp 4AE19662)
+ (at 109.855 68.326)
+ (descr "Diode 5 pas")
+ (tags "DIODE DEV")
+ (path /4AE172F4)
+ (fp_text reference D1 (at 0 0) (layer F.SilkS)
+ (effects (font (size 1.524 1.016) (thickness 0.254)))
+ )
+ (fp_text value 1N4007 (at -0.254 0) (layer F.SilkS) hide
+ (effects (font (size 1.524 1.016) (thickness 0.254)))
+ )
+ (fp_line (start 6.35 0) (end 5.08 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 5.08 0) (end 5.08 -1.27) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 5.08 -1.27) (end -5.08 -1.27) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -5.08 -1.27) (end -5.08 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -5.08 0) (end -6.35 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -5.08 0) (end -5.08 1.27) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -5.08 1.27) (end 5.08 1.27) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 5.08 1.27) (end 5.08 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.81 -1.27) (end 3.81 1.27) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 4.064 -1.27) (end 4.064 1.27) (layer F.SilkS) (width 0.3048))
+ (pad 1 thru_hole circle (at -6.35 0) (size 2.032 2.032) (drill 1.143) (layers *.Cu *.Mask F.SilkS)
+ (net 3 /12Vext))
+ (pad 2 thru_hole rect (at 6.35 0) (size 2.032 2.032) (drill 1.143) (layers *.Cu *.Mask F.SilkS)
+ (net 1 +12V))
+ (model discret/diode.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 0.5 0.5 0.5))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module TO92-CBE (layer Composant) (tedit 4718C200) (tstamp 4AE196A3)
+ (at 149.225 124.206 180)
+ (descr "Transistor TO92 brochage type BC237")
+ (tags "TR TO92")
+ (path /4B3A1333/4B3A137C)
+ (fp_text reference Q3 (at 0.762 0.635 180) (layer F.SilkS)
+ (effects (font (size 1.016 1.016) (thickness 0.2032)))
+ )
+ (fp_text value MPAS92 (at 0.254 -3.048 180) (layer F.SilkS)
+ (effects (font (size 1.016 1.016) (thickness 0.2032)))
+ )
+ (fp_line (start -1.27 2.54) (end 2.54 -1.27) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 2.54 -1.27) (end 2.54 -2.54) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 2.54 -2.54) (end 1.27 -3.81) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 1.27 -3.81) (end -1.27 -3.81) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -1.27 -3.81) (end -3.81 -1.27) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.81 -1.27) (end -3.81 1.27) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.81 1.27) (end -2.54 2.54) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -2.54 2.54) (end -1.27 2.54) (layer F.SilkS) (width 0.3048))
+ (pad E thru_hole rect (at -1.27 1.27 180) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 22 N-000017))
+ (pad B thru_hole circle (at -1.27 -1.27 180) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 30 N-000025))
+ (pad C thru_hole circle (at 1.27 -1.27 180) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 12 GND))
+ (model discret/to98.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 1 1 1))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module TO92-CBE (layer Composant) (tedit 4718C200) (tstamp 4AE196A5)
+ (at 131.445 88.646)
+ (descr "Transistor TO92 brochage type BC237")
+ (tags "TR TO92")
+ (path /4B3A13A4/4B3A1360)
+ (fp_text reference Q5 (at 0.762 0.635) (layer F.SilkS)
+ (effects (font (size 1.016 1.016) (thickness 0.2032)))
+ )
+ (fp_text value MPAS92 (at 0.254 -3.048) (layer F.SilkS)
+ (effects (font (size 1.016 1.016) (thickness 0.2032)))
+ )
+ (fp_line (start -1.27 2.54) (end 2.54 -1.27) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 2.54 -1.27) (end 2.54 -2.54) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 2.54 -2.54) (end 1.27 -3.81) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 1.27 -3.81) (end -1.27 -3.81) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -1.27 -3.81) (end -3.81 -1.27) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.81 -1.27) (end -3.81 1.27) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.81 1.27) (end -2.54 2.54) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -2.54 2.54) (end -1.27 2.54) (layer F.SilkS) (width 0.3048))
+ (pad E thru_hole rect (at -1.27 1.27) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 49 N-000048))
+ (pad B thru_hole circle (at -1.27 -1.27) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 41 N-000040))
+ (pad C thru_hole circle (at 1.27 -1.27) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 48 N-000047))
+ (model discret/to98.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 1 1 1))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module TO92-CBE (layer Composant) (tedit 4718C200) (tstamp 4AE196A7)
+ (at 149.225 99.441 180)
+ (descr "Transistor TO92 brochage type BC237")
+ (tags "TR TO92")
+ (path /4B3A13A4/4B3A137C)
+ (fp_text reference Q7 (at 0.762 0.635 180) (layer F.SilkS)
+ (effects (font (size 1.016 1.016) (thickness 0.2032)))
+ )
+ (fp_text value MPAS92 (at 0.254 -3.048 180) (layer F.SilkS)
+ (effects (font (size 1.016 1.016) (thickness 0.2032)))
+ )
+ (fp_line (start -1.27 2.54) (end 2.54 -1.27) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 2.54 -1.27) (end 2.54 -2.54) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 2.54 -2.54) (end 1.27 -3.81) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 1.27 -3.81) (end -1.27 -3.81) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -1.27 -3.81) (end -3.81 -1.27) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.81 -1.27) (end -3.81 1.27) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.81 1.27) (end -2.54 2.54) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -2.54 2.54) (end -1.27 2.54) (layer F.SilkS) (width 0.3048))
+ (pad E thru_hole rect (at -1.27 1.27 180) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 39 N-000038))
+ (pad B thru_hole circle (at -1.27 -1.27 180) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 47 N-000046))
+ (pad C thru_hole circle (at 1.27 -1.27 180) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 12 GND))
+ (model discret/to98.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 1 1 1))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module TO92-CBE (layer Composant) (tedit 4718C200) (tstamp 4AE196A9)
+ (at 150.495 89.916)
+ (descr "Transistor TO92 brochage type BC237")
+ (tags "TR TO92")
+ (path /4B3A13A4/4B3A137D)
+ (fp_text reference Q6 (at 0.762 0.635) (layer F.SilkS)
+ (effects (font (size 1.016 1.016) (thickness 0.2032)))
+ )
+ (fp_text value MPAS42 (at 0.254 -3.048) (layer F.SilkS)
+ (effects (font (size 1.016 1.016) (thickness 0.2032)))
+ )
+ (fp_line (start -1.27 2.54) (end 2.54 -1.27) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 2.54 -1.27) (end 2.54 -2.54) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 2.54 -2.54) (end 1.27 -3.81) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 1.27 -3.81) (end -1.27 -3.81) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -1.27 -3.81) (end -3.81 -1.27) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.81 -1.27) (end -3.81 1.27) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.81 1.27) (end -2.54 2.54) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -2.54 2.54) (end -1.27 2.54) (layer F.SilkS) (width 0.3048))
+ (pad E thru_hole rect (at -1.27 1.27) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 38 N-000037))
+ (pad B thru_hole circle (at -1.27 -1.27) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 48 N-000047))
+ (pad C thru_hole circle (at 1.27 -1.27) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 13 HT))
+ (model discret/to98.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 1 1 1))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module TO92-CBE (layer Composant) (tedit 4718C200) (tstamp 4AE196AB)
+ (at 131.445 115.316)
+ (descr "Transistor TO92 brochage type BC237")
+ (tags "TR TO92")
+ (path /4B3A1333/4B3A1360)
+ (fp_text reference Q1 (at 0.762 0.635) (layer F.SilkS)
+ (effects (font (size 1.016 1.016) (thickness 0.2032)))
+ )
+ (fp_text value MPAS92 (at 0.254 -3.048) (layer F.SilkS)
+ (effects (font (size 1.016 1.016) (thickness 0.2032)))
+ )
+ (fp_line (start -1.27 2.54) (end 2.54 -1.27) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 2.54 -1.27) (end 2.54 -2.54) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 2.54 -2.54) (end 1.27 -3.81) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 1.27 -3.81) (end -1.27 -3.81) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -1.27 -3.81) (end -3.81 -1.27) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.81 -1.27) (end -3.81 1.27) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.81 1.27) (end -2.54 2.54) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -2.54 2.54) (end -1.27 2.54) (layer F.SilkS) (width 0.3048))
+ (pad E thru_hole rect (at -1.27 1.27) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 32 N-000027))
+ (pad B thru_hole circle (at -1.27 -1.27) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 24 N-000019))
+ (pad C thru_hole circle (at 1.27 -1.27) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 31 N-000026))
+ (model discret/to98.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 1 1 1))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module TO92-CBE (layer Composant) (tedit 4718C200) (tstamp 4AE196AD)
+ (at 150.495 116.586)
+ (descr "Transistor TO92 brochage type BC237")
+ (tags "TR TO92")
+ (path /4B3A1333/4B3A137D)
+ (fp_text reference Q2 (at 0.762 0.635) (layer F.SilkS)
+ (effects (font (size 1.016 1.016) (thickness 0.2032)))
+ )
+ (fp_text value MPAS42 (at 0.254 -3.048) (layer F.SilkS)
+ (effects (font (size 1.016 1.016) (thickness 0.2032)))
+ )
+ (fp_line (start -1.27 2.54) (end 2.54 -1.27) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 2.54 -1.27) (end 2.54 -2.54) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 2.54 -2.54) (end 1.27 -3.81) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 1.27 -3.81) (end -1.27 -3.81) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -1.27 -3.81) (end -3.81 -1.27) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.81 -1.27) (end -3.81 1.27) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.81 1.27) (end -2.54 2.54) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -2.54 2.54) (end -1.27 2.54) (layer F.SilkS) (width 0.3048))
+ (pad E thru_hole rect (at -1.27 1.27) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 21 N-000016))
+ (pad B thru_hole circle (at -1.27 -1.27) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 31 N-000026))
+ (pad C thru_hole circle (at 1.27 -1.27) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 13 HT))
+ (model discret/to98.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 1 1 1))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module TO92-CBE (layer Composant) (tedit 4718C200) (tstamp 4AE196AF)
+ (at 132.715 125.476)
+ (descr "Transistor TO92 brochage type BC237")
+ (tags "TR TO92")
+ (path /4B3A1333/4B3A1379)
+ (fp_text reference Q4 (at 0.762 0.635) (layer F.SilkS)
+ (effects (font (size 1.016 1.016) (thickness 0.2032)))
+ )
+ (fp_text value MPAS42 (at 0.254 -3.048) (layer F.SilkS)
+ (effects (font (size 1.016 1.016) (thickness 0.2032)))
+ )
+ (fp_line (start -1.27 2.54) (end 2.54 -1.27) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 2.54 -1.27) (end 2.54 -2.54) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 2.54 -2.54) (end 1.27 -3.81) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 1.27 -3.81) (end -1.27 -3.81) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -1.27 -3.81) (end -3.81 -1.27) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.81 -1.27) (end -3.81 1.27) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.81 1.27) (end -2.54 2.54) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -2.54 2.54) (end -1.27 2.54) (layer F.SilkS) (width 0.3048))
+ (pad E thru_hole rect (at -1.27 1.27) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 16 N-000011))
+ (pad B thru_hole circle (at -1.27 -1.27) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 25 N-000020))
+ (pad C thru_hole circle (at 1.27 -1.27) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 30 N-000025))
+ (model discret/to98.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 1 1 1))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module TO92-CBE (layer Composant) (tedit 4718C200) (tstamp 4AE196B1)
+ (at 131.445 98.806)
+ (descr "Transistor TO92 brochage type BC237")
+ (tags "TR TO92")
+ (path /4B3A13A4/4B3A1379)
+ (fp_text reference Q8 (at 0.762 0.635) (layer F.SilkS)
+ (effects (font (size 1.016 1.016) (thickness 0.2032)))
+ )
+ (fp_text value MPAS42 (at 0.254 -3.048) (layer F.SilkS)
+ (effects (font (size 1.016 1.016) (thickness 0.2032)))
+ )
+ (fp_line (start -1.27 2.54) (end 2.54 -1.27) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 2.54 -1.27) (end 2.54 -2.54) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 2.54 -2.54) (end 1.27 -3.81) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 1.27 -3.81) (end -1.27 -3.81) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -1.27 -3.81) (end -3.81 -1.27) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.81 -1.27) (end -3.81 1.27) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.81 1.27) (end -2.54 2.54) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -2.54 2.54) (end -1.27 2.54) (layer F.SilkS) (width 0.3048))
+ (pad E thru_hole rect (at -1.27 1.27) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 33 N-000032))
+ (pad B thru_hole circle (at -1.27 -1.27) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 42 N-000041))
+ (pad C thru_hole circle (at 1.27 -1.27) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 47 N-000046))
+ (model discret/to98.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 1 1 1))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module bornier2 (layer Composant) (tedit 3EC0ED69) (tstamp 4AE6C393)
+ (at 95.885 72.136 270)
+ (descr "Bornier d'alimentation 2 pins")
+ (tags DEV)
+ (path /4AD71B06)
+ (fp_text reference P2 (at 0 -5.08 270) (layer F.SilkS)
+ (effects (font (thickness 0.3048)))
+ )
+ (fp_text value CONN_2 (at 0 5.08 270) (layer F.SilkS)
+ (effects (font (thickness 0.3048)))
+ )
+ (fp_line (start 5.08 2.54) (end -5.08 2.54) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 5.08 3.81) (end 5.08 -3.81) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 5.08 -3.81) (end -5.08 -3.81) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -5.08 -3.81) (end -5.08 3.81) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -5.08 3.81) (end 5.08 3.81) (layer F.SilkS) (width 0.3048))
+ (pad 1 thru_hole rect (at -2.54 0 270) (size 2.54 2.54) (drill 1.524) (layers *.Cu *.Mask F.SilkS)
+ (net 3 /12Vext))
+ (pad 2 thru_hole circle (at 2.54 0 270) (size 2.54 2.54) (drill 1.524) (layers *.Cu *.Mask F.SilkS)
+ (net 12 GND))
+ (model device/bornier_2.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 1 1 1))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module DIP-8__300_ELL (layer Composant) (tedit 200000) (tstamp 4AE6C394)
+ (at 149.225 68.326)
+ (descr "8 pins DIL package, elliptical pads")
+ (tags DIL)
+ (path /4B4B1230)
+ (fp_text reference U1 (at -6.35 0 90) (layer F.SilkS)
+ (effects (font (size 1.778 1.143) (thickness 0.28702)))
+ )
+ (fp_text value ICL7660 (at 0 0) (layer F.SilkS)
+ (effects (font (size 1.778 1.016) (thickness 0.254)))
+ )
+ (fp_line (start -5.08 -1.27) (end -3.81 -1.27) (layer F.SilkS) (width 0.381))
+ (fp_line (start -3.81 -1.27) (end -3.81 1.27) (layer F.SilkS) (width 0.381))
+ (fp_line (start -3.81 1.27) (end -5.08 1.27) (layer F.SilkS) (width 0.381))
+ (fp_line (start -5.08 -2.54) (end 5.08 -2.54) (layer F.SilkS) (width 0.381))
+ (fp_line (start 5.08 -2.54) (end 5.08 2.54) (layer F.SilkS) (width 0.381))
+ (fp_line (start 5.08 2.54) (end -5.08 2.54) (layer F.SilkS) (width 0.381))
+ (fp_line (start -5.08 2.54) (end -5.08 -2.54) (layer F.SilkS) (width 0.381))
+ (pad 1 thru_hole rect (at -3.81 3.81) (size 1.5748 2.286) (drill 0.8128) (layers *.Cu *.Mask F.SilkS))
+ (pad 2 thru_hole oval (at -1.27 3.81) (size 1.5748 2.286) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 15 N-000010))
+ (pad 3 thru_hole oval (at 1.27 3.81) (size 1.5748 2.286) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 12 GND))
+ (pad 4 thru_hole oval (at 3.81 3.81) (size 1.5748 2.286) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 14 N-000005))
+ (pad 5 thru_hole oval (at 3.81 -3.81) (size 1.5748 2.286) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 2 -VAA))
+ (pad 6 thru_hole oval (at 1.27 -3.81) (size 1.5748 2.286) (drill 0.8128) (layers *.Cu *.Mask F.SilkS))
+ (pad 7 thru_hole oval (at -1.27 -3.81) (size 1.5748 2.286) (drill 0.8128) (layers *.Cu *.Mask F.SilkS))
+ (pad 8 thru_hole oval (at -3.81 -3.81) (size 1.5748 2.286) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 50 VCC))
+ (model dil/dil_8.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 1 1 1))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module DIP-8__300_ELL (layer Composant) (tedit 200000) (tstamp 4AE6C396)
+ (at 111.125 92.456)
+ (descr "8 pins DIL package, elliptical pads")
+ (tags DIL)
+ (path /4B3A13A4/4B3A1368)
+ (fp_text reference U4 (at -6.35 0 90) (layer F.SilkS)
+ (effects (font (size 1.778 1.143) (thickness 0.28702)))
+ )
+ (fp_text value LM358N (at 0 0) (layer F.SilkS)
+ (effects (font (size 1.778 1.016) (thickness 0.254)))
+ )
+ (fp_line (start -5.08 -1.27) (end -3.81 -1.27) (layer F.SilkS) (width 0.381))
+ (fp_line (start -3.81 -1.27) (end -3.81 1.27) (layer F.SilkS) (width 0.381))
+ (fp_line (start -3.81 1.27) (end -5.08 1.27) (layer F.SilkS) (width 0.381))
+ (fp_line (start -5.08 -2.54) (end 5.08 -2.54) (layer F.SilkS) (width 0.381))
+ (fp_line (start 5.08 -2.54) (end 5.08 2.54) (layer F.SilkS) (width 0.381))
+ (fp_line (start 5.08 2.54) (end -5.08 2.54) (layer F.SilkS) (width 0.381))
+ (fp_line (start -5.08 2.54) (end -5.08 -2.54) (layer F.SilkS) (width 0.381))
+ (pad 1 thru_hole rect (at -3.81 3.81) (size 1.5748 2.286) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 43 N-000042))
+ (pad 2 thru_hole oval (at -1.27 3.81) (size 1.5748 2.286) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 44 N-000043))
+ (pad 3 thru_hole oval (at 1.27 3.81) (size 1.5748 2.286) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 5 /ampli_h2))
+ (pad 4 thru_hole oval (at 3.81 3.81) (size 1.5748 2.286) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 2 -VAA))
+ (pad 5 thru_hole oval (at 3.81 -3.81) (size 1.5748 2.286) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 46 N-000045))
+ (pad 6 thru_hole oval (at 1.27 -3.81) (size 1.5748 2.286) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 5 /ampli_h2))
+ (pad 7 thru_hole oval (at -1.27 -3.81) (size 1.5748 2.286) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 5 /ampli_h2))
+ (pad 8 thru_hole oval (at -3.81 -3.81) (size 1.5748 2.286) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 1 +12V))
+ (model dil/dil_8.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 1 1 1))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module bornier2 (layer Composant) (tedit 3EC0ED69) (tstamp 4B3A1599)
+ (at 94.615 114.046 270)
+ (descr "Bornier d'alimentation 2 pins")
+ (tags DEV)
+ (path /4B3A1333/4B3A1367)
+ (fp_text reference P4 (at 0 -5.08 270) (layer F.SilkS)
+ (effects (font (thickness 0.3048)))
+ )
+ (fp_text value CONN_2 (at 0 5.08 270) (layer F.SilkS)
+ (effects (font (thickness 0.3048)))
+ )
+ (fp_line (start 5.08 2.54) (end -5.08 2.54) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 5.08 3.81) (end 5.08 -3.81) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 5.08 -3.81) (end -5.08 -3.81) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -5.08 -3.81) (end -5.08 3.81) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -5.08 3.81) (end 5.08 3.81) (layer F.SilkS) (width 0.3048))
+ (pad 1 thru_hole rect (at -2.54 0 270) (size 2.54 2.54) (drill 1.524) (layers *.Cu *.Mask F.SilkS)
+ (net 7 /ampli_h4))
+ (pad 2 thru_hole circle (at 2.54 0 270) (size 2.54 2.54) (drill 1.524) (layers *.Cu *.Mask F.SilkS)
+ (net 12 GND))
+ (model device/bornier_2.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 1 1 1))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module bornier2 (layer Composant) (tedit 3EC0ED69) (tstamp 4B3A159B)
+ (at 182.245 97.536 90)
+ (descr "Bornier d'alimentation 2 pins")
+ (tags DEV)
+ (path /4B3A13A4/4B3A136C)
+ (fp_text reference P5 (at 0 -5.08 90) (layer F.SilkS)
+ (effects (font (thickness 0.3048)))
+ )
+ (fp_text value CONN_2 (at 0 5.08 90) (layer F.SilkS)
+ (effects (font (thickness 0.3048)))
+ )
+ (fp_line (start 5.08 2.54) (end -5.08 2.54) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 5.08 3.81) (end 5.08 -3.81) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 5.08 -3.81) (end -5.08 -3.81) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -5.08 -3.81) (end -5.08 3.81) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -5.08 3.81) (end 5.08 3.81) (layer F.SilkS) (width 0.3048))
+ (pad 1 thru_hole rect (at -2.54 0 90) (size 2.54 2.54) (drill 1.524) (layers *.Cu *.Mask F.SilkS)
+ (net 8 /ampli_h5))
+ (pad 2 thru_hole circle (at 2.54 0 90) (size 2.54 2.54) (drill 1.524) (layers *.Cu *.Mask F.SilkS)
+ (net 12 GND))
+ (model device/bornier_2.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 1 1 1))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module bornier2 (layer Composant) (tedit 3EC0ED69) (tstamp 4B3A159D)
+ (at 95.885 87.376 270)
+ (descr "Bornier d'alimentation 2 pins")
+ (tags DEV)
+ (path /4B3A13A4/4B3A1367)
+ (fp_text reference P6 (at 0 -5.08 270) (layer F.SilkS)
+ (effects (font (thickness 0.3048)))
+ )
+ (fp_text value CONN_2 (at 0 5.08 270) (layer F.SilkS)
+ (effects (font (thickness 0.3048)))
+ )
+ (fp_line (start 5.08 2.54) (end -5.08 2.54) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 5.08 3.81) (end 5.08 -3.81) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 5.08 -3.81) (end -5.08 -3.81) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -5.08 -3.81) (end -5.08 3.81) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -5.08 3.81) (end 5.08 3.81) (layer F.SilkS) (width 0.3048))
+ (pad 1 thru_hole rect (at -2.54 0 270) (size 2.54 2.54) (drill 1.524) (layers *.Cu *.Mask F.SilkS)
+ (net 9 /ampli_h6))
+ (pad 2 thru_hole circle (at 2.54 0 270) (size 2.54 2.54) (drill 1.524) (layers *.Cu *.Mask F.SilkS)
+ (net 12 GND))
+ (model device/bornier_2.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 1 1 1))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module DIP-8__300_ELL (layer Composant) (tedit 200000) (tstamp 4B3A159E)
+ (at 111.125 120.396)
+ (descr "8 pins DIL package, elliptical pads")
+ (tags DIL)
+ (path /4B3A1333/4B3A1368)
+ (fp_text reference U3 (at -6.35 0 90) (layer F.SilkS)
+ (effects (font (size 1.778 1.143) (thickness 0.28702)))
+ )
+ (fp_text value LM358N (at 0 0) (layer F.SilkS)
+ (effects (font (size 1.778 1.016) (thickness 0.254)))
+ )
+ (fp_line (start -5.08 -1.27) (end -3.81 -1.27) (layer F.SilkS) (width 0.381))
+ (fp_line (start -3.81 -1.27) (end -3.81 1.27) (layer F.SilkS) (width 0.381))
+ (fp_line (start -3.81 1.27) (end -5.08 1.27) (layer F.SilkS) (width 0.381))
+ (fp_line (start -5.08 -2.54) (end 5.08 -2.54) (layer F.SilkS) (width 0.381))
+ (fp_line (start 5.08 -2.54) (end 5.08 2.54) (layer F.SilkS) (width 0.381))
+ (fp_line (start 5.08 2.54) (end -5.08 2.54) (layer F.SilkS) (width 0.381))
+ (fp_line (start -5.08 2.54) (end -5.08 -2.54) (layer F.SilkS) (width 0.381))
+ (pad 1 thru_hole rect (at -3.81 3.81) (size 1.5748 2.286) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 26 N-000021))
+ (pad 2 thru_hole oval (at -1.27 3.81) (size 1.5748 2.286) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 27 N-000022))
+ (pad 3 thru_hole oval (at 1.27 3.81) (size 1.5748 2.286) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 4 /ampli_h1))
+ (pad 4 thru_hole oval (at 3.81 3.81) (size 1.5748 2.286) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 2 -VAA))
+ (pad 5 thru_hole oval (at 3.81 -3.81) (size 1.5748 2.286) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 29 N-000024))
+ (pad 6 thru_hole oval (at 1.27 -3.81) (size 1.5748 2.286) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 4 /ampli_h1))
+ (pad 7 thru_hole oval (at -1.27 -3.81) (size 1.5748 2.286) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 4 /ampli_h1))
+ (pad 8 thru_hole oval (at -3.81 -3.81) (size 1.5748 2.286) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 1 +12V))
+ (model dil/dil_8.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 1 1 1))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module R3-LARGE_PADS (layer Composant) (tedit 47E26765) (tstamp 4B3A159F)
+ (at 130.683 120.142 180)
+ (descr "Resitance 3 pas")
+ (tags R)
+ (path /4B3A1333/4B3A136D)
+ (autoplace_cost180 10)
+ (fp_text reference R9 (at 0 0 180) (layer F.SilkS)
+ (effects (font (size 1.397 1.27) (thickness 0.2032)))
+ )
+ (fp_text value 1K (at 0 0 180) (layer F.SilkS) hide
+ (effects (font (size 1.397 1.27) (thickness 0.2032)))
+ )
+ (fp_line (start -3.81 0) (end -3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.81 0) (end 3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 0) (end 3.302 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 -1.016) (end -3.302 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 -1.016) (end -3.302 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 1.016) (end 3.302 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 1.016) (end 3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 -0.508) (end -2.794 -1.016) (layer F.SilkS) (width 0.3048))
+ (pad 1 thru_hole circle (at -3.81 0 180) (size 1.651 1.651) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 16 N-000011))
+ (pad 2 thru_hole circle (at 3.81 0 180) (size 1.651 1.651) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 26 N-000021))
+ (model discret/resistor.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 0.3 0.3 0.3))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module R3-LARGE_PADS (layer Composant) (tedit 47E26765) (tstamp 4B3A15A1)
+ (at 169.545 91.186 180)
+ (descr "Resitance 3 pas")
+ (tags R)
+ (path /4B3A13A4/4B3A1364)
+ (autoplace_cost180 10)
+ (fp_text reference R15 (at 0 0 180) (layer F.SilkS)
+ (effects (font (size 1.397 1.27) (thickness 0.2032)))
+ )
+ (fp_text value 47 (at 0 0 180) (layer F.SilkS) hide
+ (effects (font (size 1.397 1.27) (thickness 0.2032)))
+ )
+ (fp_line (start -3.81 0) (end -3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.81 0) (end 3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 0) (end 3.302 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 -1.016) (end -3.302 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 -1.016) (end -3.302 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 1.016) (end 3.302 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 1.016) (end 3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 -0.508) (end -2.794 -1.016) (layer F.SilkS) (width 0.3048))
+ (pad 1 thru_hole circle (at -3.81 0 180) (size 1.651 1.651) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 8 /ampli_h5))
+ (pad 2 thru_hole circle (at 3.81 0 180) (size 1.651 1.651) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 38 N-000037))
+ (model discret/resistor.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 0.3 0.3 0.3))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module RV2 (layer Composant) (tedit 3FA15781) (tstamp 4B3A15A2)
+ (at 95.885 124.206)
+ (descr "Resistance variable / potentiometre")
+ (tags R)
+ (path /4B3A1333/4B3A1357)
+ (autoplace_cost90 10)
+ (autoplace_cost180 10)
+ (fp_text reference RV1 (at 0 -5.08) (layer F.SilkS)
+ (effects (font (size 1.397 1.27) (thickness 0.2032)))
+ )
+ (fp_text value 4,7K (at -0.254 5.207) (layer F.SilkS)
+ (effects (font (size 1.397 1.27) (thickness 0.2032)))
+ )
+ (fp_circle (center 0 0.381) (end 0 -3.175) (layer F.SilkS) (width 0.2032))
+ (pad 1 thru_hole circle (at -2.54 1.27) (size 1.524 1.524) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 23 N-000018))
+ (pad 2 thru_hole circle (at 0 -1.27) (size 1.524 1.524) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 27 N-000022))
+ (pad 3 thru_hole circle (at 2.54 1.27) (size 1.524 1.524) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 10 /ampli_h7))
+ (model discret/adjustable_rx2.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 1 1 1))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module RV2 (layer Composant) (tedit 3FA15781) (tstamp 4B3A15A4)
+ (at 95.885 98.806)
+ (descr "Resistance variable / potentiometre")
+ (tags R)
+ (path /4B3A13A4/4B3A1357)
+ (autoplace_cost90 10)
+ (autoplace_cost180 10)
+ (fp_text reference RV2 (at 0 -5.08) (layer F.SilkS)
+ (effects (font (size 1.397 1.27) (thickness 0.2032)))
+ )
+ (fp_text value 4,7K (at -0.254 5.207) (layer F.SilkS)
+ (effects (font (size 1.397 1.27) (thickness 0.2032)))
+ )
+ (fp_circle (center 0 0.381) (end 0 -3.175) (layer F.SilkS) (width 0.2032))
+ (pad 1 thru_hole circle (at -2.54 1.27) (size 1.524 1.524) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 40 N-000039))
+ (pad 2 thru_hole circle (at 0 -1.27) (size 1.524 1.524) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 44 N-000043))
+ (pad 3 thru_hole circle (at 2.54 1.27) (size 1.524 1.524) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 11 /ampli_h8))
+ (model discret/adjustable_rx2.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 1 1 1))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module C2 (layer Composant) (tedit 200000) (tstamp 4AE19645)
+ (at 116.205 110.236 180)
+ (descr "Condensateur = 2 pas")
+ (tags C)
+ (path /4B3A1333/4B3A1358)
+ (fp_text reference C3 (at 0 0 180) (layer F.SilkS)
+ (effects (font (size 1.016 1.016) (thickness 0.2032)))
+ )
+ (fp_text value 15nF (at 0 0 180) (layer F.SilkS) hide
+ (effects (font (size 1.016 1.016) (thickness 0.2032)))
+ )
+ (fp_line (start -3.556 -1.016) (end 3.556 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.556 -1.016) (end 3.556 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.556 1.016) (end -3.556 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.556 1.016) (end -3.556 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.556 -0.508) (end -3.048 -1.016) (layer F.SilkS) (width 0.3048))
+ (pad 1 thru_hole circle (at -2.54 0 180) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 4 /ampli_h1))
+ (pad 2 thru_hole circle (at 2.54 0 180) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 18 N-000013))
+ (model discret/capa_2pas_5x5mm.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 1 1 1))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module C2 (layer Composant) (tedit 200000) (tstamp 4AE1963D)
+ (at 116.205 112.776)
+ (descr "Condensateur = 2 pas")
+ (tags C)
+ (path /4B3A1333/4B3A1366)
+ (fp_text reference C4 (at 0 0) (layer F.SilkS)
+ (effects (font (size 1.016 1.016) (thickness 0.2032)))
+ )
+ (fp_text value 4,7nF (at 0 0) (layer F.SilkS) hide
+ (effects (font (size 1.016 1.016) (thickness 0.2032)))
+ )
+ (fp_line (start -3.556 -1.016) (end 3.556 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.556 -1.016) (end 3.556 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.556 1.016) (end -3.556 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.556 1.016) (end -3.556 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.556 -0.508) (end -3.048 -1.016) (layer F.SilkS) (width 0.3048))
+ (pad 1 thru_hole circle (at -2.54 0) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 29 N-000024))
+ (pad 2 thru_hole circle (at 2.54 0) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 12 GND))
+ (model discret/capa_2pas_5x5mm.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 1 1 1))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module C2 (layer Composant) (tedit 200000) (tstamp 4AE19643)
+ (at 140.716 130.048)
+ (descr "Condensateur = 2 pas")
+ (tags C)
+ (path /4B3A1333/4B3A1365)
+ (fp_text reference C5 (at 0 0) (layer F.SilkS)
+ (effects (font (size 1.016 1.016) (thickness 0.2032)))
+ )
+ (fp_text value 820pF (at 0 0) (layer F.SilkS) hide
+ (effects (font (size 1.016 1.016) (thickness 0.2032)))
+ )
+ (fp_line (start -3.556 -1.016) (end 3.556 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.556 -1.016) (end 3.556 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.556 1.016) (end -3.556 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.556 1.016) (end -3.556 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.556 -0.508) (end -3.048 -1.016) (layer F.SilkS) (width 0.3048))
+ (pad 1 thru_hole circle (at -2.54 0) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 20 N-000015))
+ (pad 2 thru_hole circle (at 2.54 0) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 12 GND))
+ (model discret/capa_2pas_5x5mm.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 1 1 1))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module C2 (layer Composant) (tedit 200000) (tstamp 4AE19647)
+ (at 116.205 81.026 180)
+ (descr "Condensateur = 2 pas")
+ (tags C)
+ (path /4B3A13A4/4B3A1358)
+ (fp_text reference C6 (at 0 0 180) (layer F.SilkS)
+ (effects (font (size 1.016 1.016) (thickness 0.2032)))
+ )
+ (fp_text value 15nF (at 0 0 180) (layer F.SilkS) hide
+ (effects (font (size 1.016 1.016) (thickness 0.2032)))
+ )
+ (fp_line (start -3.556 -1.016) (end 3.556 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.556 -1.016) (end 3.556 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.556 1.016) (end -3.556 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.556 1.016) (end -3.556 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.556 -0.508) (end -3.048 -1.016) (layer F.SilkS) (width 0.3048))
+ (pad 1 thru_hole circle (at -2.54 0 180) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 5 /ampli_h2))
+ (pad 2 thru_hole circle (at 2.54 0 180) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 35 N-000034))
+ (model discret/capa_2pas_5x5mm.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 1 1 1))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module C2 (layer Composant) (tedit 200000) (tstamp 4AE1964A)
+ (at 116.205 84.836)
+ (descr "Condensateur = 2 pas")
+ (tags C)
+ (path /4B3A13A4/4B3A1366)
+ (fp_text reference C7 (at 0 0) (layer F.SilkS)
+ (effects (font (size 1.016 1.016) (thickness 0.2032)))
+ )
+ (fp_text value 4,7nF (at 0 0) (layer F.SilkS) hide
+ (effects (font (size 1.016 1.016) (thickness 0.2032)))
+ )
+ (fp_line (start -3.556 -1.016) (end 3.556 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.556 -1.016) (end 3.556 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.556 1.016) (end -3.556 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.556 1.016) (end -3.556 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.556 -0.508) (end -3.048 -1.016) (layer F.SilkS) (width 0.3048))
+ (pad 1 thru_hole circle (at -2.54 0) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 46 N-000045))
+ (pad 2 thru_hole circle (at 2.54 0) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 12 GND))
+ (model discret/capa_2pas_5x5mm.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 1 1 1))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module C2 (layer Composant) (tedit 200000) (tstamp 4AE1963B)
+ (at 140.589 104.14)
+ (descr "Condensateur = 2 pas")
+ (tags C)
+ (path /4B3A13A4/4B3A1365)
+ (fp_text reference C8 (at 0 0) (layer F.SilkS)
+ (effects (font (size 1.016 1.016) (thickness 0.2032)))
+ )
+ (fp_text value 820pF (at 0 0) (layer F.SilkS) hide
+ (effects (font (size 1.016 1.016) (thickness 0.2032)))
+ )
+ (fp_line (start -3.556 -1.016) (end 3.556 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.556 -1.016) (end 3.556 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.556 1.016) (end -3.556 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.556 1.016) (end -3.556 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.556 -0.508) (end -3.048 -1.016) (layer F.SilkS) (width 0.3048))
+ (pad 1 thru_hole circle (at -2.54 0) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 37 N-000036))
+ (pad 2 thru_hole circle (at 2.54 0) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 12 GND))
+ (model discret/capa_2pas_5x5mm.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 1 1 1))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module CP8 (layer Composant) (tedit 200000) (tstamp 4AE19641)
+ (at 112.395 60.706 180)
+ (descr "Condensateur polarise")
+ (tags CP)
+ (path /4B3A1558)
+ (fp_text reference C9 (at 1.27 1.27 180) (layer F.SilkS)
+ (effects (font (thickness 0.3048)))
+ )
+ (fp_text value 47uF/63V (at 1.27 -1.27 180) (layer F.SilkS)
+ (effects (font (thickness 0.3048)))
+ )
+ (fp_line (start -10.16 0) (end -8.89 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -7.62 1.27) (end -8.89 1.27) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -8.89 1.27) (end -8.89 -1.27) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -8.89 -1.27) (end -7.62 -1.27) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -7.62 2.54) (end -7.62 -2.54) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -7.62 -2.54) (end 8.89 -2.54) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 8.89 -2.54) (end 8.89 2.54) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 8.89 2.54) (end -7.62 2.54) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 8.89 0) (end 10.16 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -5.08 -2.54) (end -5.08 2.54) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -6.35 2.54) (end -6.35 -2.54) (layer F.SilkS) (width 0.3048))
+ (pad 1 thru_hole rect (at -10.16 0 180) (size 1.778 1.778) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 13 HT))
+ (pad 2 thru_hole circle (at 10.16 0 180) (size 1.778 1.778) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 12 GND))
+ (model discret/c_pol.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 0.8 0.8 0.8))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module D3 (layer Composant) (tedit 200000) (tstamp 4AE19660)
+ (at 131.445 106.426 180)
+ (descr "Diode 3 pas")
+ (tags "DIODE DEV")
+ (path /4B3A1333/4B3A1375)
+ (fp_text reference D2 (at 0 0 180) (layer F.SilkS)
+ (effects (font (size 1.016 1.016) (thickness 0.2032)))
+ )
+ (fp_text value 1N4148 (at 0 0 180) (layer F.SilkS) hide
+ (effects (font (size 1.016 1.016) (thickness 0.2032)))
+ )
+ (fp_line (start 3.81 0) (end 3.048 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.048 0) (end 3.048 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.048 -1.016) (end -3.048 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.048 -1.016) (end -3.048 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.048 0) (end -3.81 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.048 0) (end -3.048 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.048 1.016) (end 3.048 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.048 1.016) (end 3.048 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 2.54 -1.016) (end 2.54 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 2.286 1.016) (end 2.286 -1.016) (layer F.SilkS) (width 0.3048))
+ (pad 2 thru_hole rect (at 3.81 0 180) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 17 N-000012))
+ (pad 1 thru_hole circle (at -3.81 0 180) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 13 HT))
+ (model discret/diode.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 0.3 0.3 0.3))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module D3 (layer Composant) (tedit 200000) (tstamp 4AE1965E)
+ (at 131.445 108.966 180)
+ (descr "Diode 3 pas")
+ (tags "DIODE DEV")
+ (path /4B3A1333/4B3A1377)
+ (fp_text reference D3 (at 0 0 180) (layer F.SilkS)
+ (effects (font (size 1.016 1.016) (thickness 0.2032)))
+ )
+ (fp_text value 1N4148 (at 0 0 180) (layer F.SilkS) hide
+ (effects (font (size 1.016 1.016) (thickness 0.2032)))
+ )
+ (fp_line (start 3.81 0) (end 3.048 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.048 0) (end 3.048 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.048 -1.016) (end -3.048 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.048 -1.016) (end -3.048 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.048 0) (end -3.81 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.048 0) (end -3.048 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.048 1.016) (end 3.048 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.048 1.016) (end 3.048 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 2.54 -1.016) (end 2.54 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 2.286 1.016) (end 2.286 -1.016) (layer F.SilkS) (width 0.3048))
+ (pad 2 thru_hole rect (at 3.81 0 180) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 24 N-000019))
+ (pad 1 thru_hole circle (at -3.81 0 180) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 17 N-000012))
+ (model discret/diode.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 0.3 0.3 0.3))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module D3 (layer Composant) (tedit 200000) (tstamp 4AE1965C)
+ (at 140.97 119.761 270)
+ (descr "Diode 3 pas")
+ (tags "DIODE DEV")
+ (path /4B3A1333/4B3A137B)
+ (fp_text reference D4 (at 0 0 270) (layer F.SilkS)
+ (effects (font (size 1.016 1.016) (thickness 0.2032)))
+ )
+ (fp_text value 1N4148 (at 0 0 270) (layer F.SilkS) hide
+ (effects (font (size 1.016 1.016) (thickness 0.2032)))
+ )
+ (fp_line (start 3.81 0) (end 3.048 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.048 0) (end 3.048 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.048 -1.016) (end -3.048 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.048 -1.016) (end -3.048 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.048 0) (end -3.81 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.048 0) (end -3.048 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.048 1.016) (end 3.048 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.048 1.016) (end 3.048 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 2.54 -1.016) (end 2.54 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 2.286 1.016) (end 2.286 -1.016) (layer F.SilkS) (width 0.3048))
+ (pad 2 thru_hole rect (at 3.81 0 270) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 30 N-000025))
+ (pad 1 thru_hole circle (at -3.81 0 270) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 28 N-000023))
+ (model discret/diode.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 0.3 0.3 0.3))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module D3 (layer Composant) (tedit 200000) (tstamp 4AE19666)
+ (at 128.905 79.756 180)
+ (descr "Diode 3 pas")
+ (tags "DIODE DEV")
+ (path /4B3A13A4/4B3A1375)
+ (fp_text reference D5 (at 0 0 180) (layer F.SilkS)
+ (effects (font (size 1.016 1.016) (thickness 0.2032)))
+ )
+ (fp_text value 1N4148 (at 0 0 180) (layer F.SilkS) hide
+ (effects (font (size 1.016 1.016) (thickness 0.2032)))
+ )
+ (fp_line (start 3.81 0) (end 3.048 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.048 0) (end 3.048 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.048 -1.016) (end -3.048 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.048 -1.016) (end -3.048 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.048 0) (end -3.81 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.048 0) (end -3.048 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.048 1.016) (end 3.048 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.048 1.016) (end 3.048 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 2.54 -1.016) (end 2.54 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 2.286 1.016) (end 2.286 -1.016) (layer F.SilkS) (width 0.3048))
+ (pad 2 thru_hole rect (at 3.81 0 180) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 34 N-000033))
+ (pad 1 thru_hole circle (at -3.81 0 180) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 13 HT))
+ (model discret/diode.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 0.3 0.3 0.3))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module D3 (layer Composant) (tedit 200000) (tstamp 4AE1965A)
+ (at 128.905 82.296)
+ (descr "Diode 3 pas")
+ (tags "DIODE DEV")
+ (path /4B3A13A4/4B3A1377)
+ (fp_text reference D6 (at 0 0) (layer F.SilkS)
+ (effects (font (size 1.016 1.016) (thickness 0.2032)))
+ )
+ (fp_text value 1N4148 (at 0 0) (layer F.SilkS) hide
+ (effects (font (size 1.016 1.016) (thickness 0.2032)))
+ )
+ (fp_line (start 3.81 0) (end 3.048 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.048 0) (end 3.048 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.048 -1.016) (end -3.048 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.048 -1.016) (end -3.048 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.048 0) (end -3.81 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.048 0) (end -3.048 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.048 1.016) (end 3.048 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.048 1.016) (end 3.048 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 2.54 -1.016) (end 2.54 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 2.286 1.016) (end 2.286 -1.016) (layer F.SilkS) (width 0.3048))
+ (pad 2 thru_hole rect (at 3.81 0) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 41 N-000040))
+ (pad 1 thru_hole circle (at -3.81 0) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 34 N-000033))
+ (model discret/diode.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 0.3 0.3 0.3))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module D3 (layer Composant) (tedit 200000) (tstamp 4AE19664)
+ (at 141.605 93.726 270)
+ (descr "Diode 3 pas")
+ (tags "DIODE DEV")
+ (path /4B3A13A4/4B3A137B)
+ (fp_text reference D7 (at 0 0 270) (layer F.SilkS)
+ (effects (font (size 1.016 1.016) (thickness 0.2032)))
+ )
+ (fp_text value 1N4148 (at 0 0 270) (layer F.SilkS) hide
+ (effects (font (size 1.016 1.016) (thickness 0.2032)))
+ )
+ (fp_line (start 3.81 0) (end 3.048 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.048 0) (end 3.048 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.048 -1.016) (end -3.048 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.048 -1.016) (end -3.048 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.048 0) (end -3.81 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.048 0) (end -3.048 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.048 1.016) (end 3.048 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.048 1.016) (end 3.048 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 2.54 -1.016) (end 2.54 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 2.286 1.016) (end 2.286 -1.016) (layer F.SilkS) (width 0.3048))
+ (pad 2 thru_hole rect (at 3.81 0 270) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 47 N-000046))
+ (pad 1 thru_hole circle (at -3.81 0 270) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 45 N-000044))
+ (model discret/diode.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 0.3 0.3 0.3))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module R3-LARGE_PADS (layer Composant) (tedit 47E26765) (tstamp 4AE19678)
+ (at 142.875 108.966 180)
+ (descr "Resitance 3 pas")
+ (tags R)
+ (path /4B3A1333/4B3A1376)
+ (autoplace_cost180 10)
+ (fp_text reference R3 (at 0 0 180) (layer F.SilkS)
+ (effects (font (size 1.397 1.27) (thickness 0.2032)))
+ )
+ (fp_text value 470 (at 0 0 180) (layer F.SilkS) hide
+ (effects (font (size 1.397 1.27) (thickness 0.2032)))
+ )
+ (fp_line (start -3.81 0) (end -3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.81 0) (end 3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 0) (end 3.302 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 -1.016) (end -3.302 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 -1.016) (end -3.302 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 1.016) (end 3.302 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 1.016) (end 3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 -0.508) (end -2.794 -1.016) (layer F.SilkS) (width 0.3048))
+ (pad 1 thru_hole circle (at -3.81 0 180) (size 1.651 1.651) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 13 HT))
+ (pad 2 thru_hole circle (at 3.81 0 180) (size 1.651 1.651) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 32 N-000027))
+ (model discret/resistor.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 0.3 0.3 0.3))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module R3-LARGE_PADS (layer Composant) (tedit 47E26765) (tstamp 4AE1969E)
+ (at 122.555 119.126 270)
+ (descr "Resitance 3 pas")
+ (tags R)
+ (path /4B3A1333/4B3A1374)
+ (autoplace_cost180 10)
+ (fp_text reference R4 (at 0 0 270) (layer F.SilkS)
+ (effects (font (size 1.397 1.27) (thickness 0.2032)))
+ )
+ (fp_text value 220K (at 0 0 270) (layer F.SilkS) hide
+ (effects (font (size 1.397 1.27) (thickness 0.2032)))
+ )
+ (fp_line (start -3.81 0) (end -3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.81 0) (end 3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 0) (end 3.302 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 -1.016) (end -3.302 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 -1.016) (end -3.302 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 1.016) (end 3.302 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 1.016) (end 3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 -0.508) (end -2.794 -1.016) (layer F.SilkS) (width 0.3048))
+ (pad 1 thru_hole circle (at -3.81 0 270) (size 1.651 1.651) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 24 N-000019))
+ (pad 2 thru_hole circle (at 3.81 0 270) (size 1.651 1.651) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 12 GND))
+ (model discret/resistor.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 0.3 0.3 0.3))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module R3-LARGE_PADS (layer Composant) (tedit 47E26765) (tstamp 4AE1967E)
+ (at 169.545 119.126 180)
+ (descr "Resitance 3 pas")
+ (tags R)
+ (path /4B3A1333/4B3A1364)
+ (autoplace_cost180 10)
+ (fp_text reference R5 (at 0 0 180) (layer F.SilkS)
+ (effects (font (size 1.397 1.27) (thickness 0.2032)))
+ )
+ (fp_text value 47 (at 0 0 180) (layer F.SilkS) hide
+ (effects (font (size 1.397 1.27) (thickness 0.2032)))
+ )
+ (fp_line (start -3.81 0) (end -3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.81 0) (end 3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 0) (end 3.302 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 -1.016) (end -3.302 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 -1.016) (end -3.302 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 1.016) (end 3.302 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 1.016) (end 3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 -0.508) (end -2.794 -1.016) (layer F.SilkS) (width 0.3048))
+ (pad 1 thru_hole circle (at -3.81 0 180) (size 1.651 1.651) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 6 /ampli_h3))
+ (pad 2 thru_hole circle (at 3.81 0 180) (size 1.651 1.651) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 21 N-000016))
+ (model discret/resistor.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 0.3 0.3 0.3))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module R3-LARGE_PADS (layer Composant) (tedit 47E26765) (tstamp 4AE1968C)
+ (at 106.045 110.236 180)
+ (descr "Resitance 3 pas")
+ (tags R)
+ (path /4B3A1333/4B3A136B)
+ (autoplace_cost180 10)
+ (fp_text reference R6 (at 0 0 180) (layer F.SilkS)
+ (effects (font (size 1.397 1.27) (thickness 0.2032)))
+ )
+ (fp_text value 22K (at 0 0 180) (layer F.SilkS) hide
+ (effects (font (size 1.397 1.27) (thickness 0.2032)))
+ )
+ (fp_line (start -3.81 0) (end -3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.81 0) (end 3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 0) (end 3.302 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 -1.016) (end -3.302 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 -1.016) (end -3.302 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 1.016) (end 3.302 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 1.016) (end 3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 -0.508) (end -2.794 -1.016) (layer F.SilkS) (width 0.3048))
+ (pad 1 thru_hole circle (at -3.81 0 180) (size 1.651 1.651) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 18 N-000013))
+ (pad 2 thru_hole circle (at 3.81 0 180) (size 1.651 1.651) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 19 N-000014))
+ (model discret/resistor.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 0.3 0.3 0.3))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module R3-LARGE_PADS (layer Composant) (tedit 47E26765) (tstamp 4AE196A0)
+ (at 106.045 112.776 180)
+ (descr "Resitance 3 pas")
+ (tags R)
+ (path /4B3A1333/4B3A1359)
+ (autoplace_cost180 10)
+ (fp_text reference R7 (at 0 0 180) (layer F.SilkS)
+ (effects (font (size 1.397 1.27) (thickness 0.2032)))
+ )
+ (fp_text value 22K (at 0 0 180) (layer F.SilkS) hide
+ (effects (font (size 1.397 1.27) (thickness 0.2032)))
+ )
+ (fp_line (start -3.81 0) (end -3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.81 0) (end 3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 0) (end 3.302 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 -1.016) (end -3.302 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 -1.016) (end -3.302 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 1.016) (end 3.302 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 1.016) (end 3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 -0.508) (end -2.794 -1.016) (layer F.SilkS) (width 0.3048))
+ (pad 1 thru_hole circle (at -3.81 0 180) (size 1.651 1.651) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 29 N-000024))
+ (pad 2 thru_hole circle (at 3.81 0 180) (size 1.651 1.651) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 18 N-000013))
+ (model discret/resistor.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 0.3 0.3 0.3))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module R3-LARGE_PADS (layer Composant) (tedit 47E26765) (tstamp 4AE19680)
+ (at 122.555 128.016 180)
+ (descr "Resitance 3 pas")
+ (tags R)
+ (path /4B3A1333/4B3A1371)
+ (autoplace_cost180 10)
+ (fp_text reference R8 (at 0 0 180) (layer F.SilkS)
+ (effects (font (size 1.397 1.27) (thickness 0.2032)))
+ )
+ (fp_text value 1K (at 0 0 180) (layer F.SilkS) hide
+ (effects (font (size 1.397 1.27) (thickness 0.2032)))
+ )
+ (fp_line (start -3.81 0) (end -3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.81 0) (end 3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 0) (end 3.302 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 -1.016) (end -3.302 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 -1.016) (end -3.302 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 1.016) (end 3.302 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 1.016) (end 3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 -0.508) (end -2.794 -1.016) (layer F.SilkS) (width 0.3048))
+ (pad 1 thru_hole circle (at -3.81 0 180) (size 1.651 1.651) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 25 N-000020))
+ (pad 2 thru_hole circle (at 3.81 0 180) (size 1.651 1.651) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 12 GND))
+ (model discret/resistor.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 0.3 0.3 0.3))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module R3-LARGE_PADS (layer Composant) (tedit 47E26765) (tstamp 4AE19694)
+ (at 140.589 127.381 180)
+ (descr "Resitance 3 pas")
+ (tags R)
+ (path /4B3A1333/4B61688C)
+ (autoplace_cost180 10)
+ (fp_text reference R10 (at 0 0 180) (layer F.SilkS)
+ (effects (font (size 1.397 1.27) (thickness 0.2032)))
+ )
+ (fp_text value 5,6K (at 0 0 180) (layer F.SilkS) hide
+ (effects (font (size 1.397 1.27) (thickness 0.2032)))
+ )
+ (fp_line (start -3.81 0) (end -3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.81 0) (end 3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 0) (end 3.302 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 -1.016) (end -3.302 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 -1.016) (end -3.302 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 1.016) (end 3.302 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 1.016) (end 3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 -0.508) (end -2.794 -1.016) (layer F.SilkS) (width 0.3048))
+ (pad 1 thru_hole circle (at -3.81 0 180) (size 1.651 1.651) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 30 N-000025))
+ (pad 2 thru_hole circle (at 3.81 0 180) (size 1.651 1.651) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 20 N-000015))
+ (model discret/resistor.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 0.3 0.3 0.3))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module R3-LARGE_PADS (layer Composant) (tedit 47E26765) (tstamp 4AE19686)
+ (at 102.87 122.936 90)
+ (descr "Resitance 3 pas")
+ (tags R)
+ (path /4B3A1333/4B3A1362)
+ (autoplace_cost180 10)
+ (fp_text reference R11 (at 0 0 90) (layer F.SilkS)
+ (effects (font (size 1.397 1.27) (thickness 0.2032)))
+ )
+ (fp_text value 4,7K (at 0 0 90) (layer F.SilkS) hide
+ (effects (font (size 1.397 1.27) (thickness 0.2032)))
+ )
+ (fp_line (start -3.81 0) (end -3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.81 0) (end 3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 0) (end 3.302 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 -1.016) (end -3.302 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 -1.016) (end -3.302 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 1.016) (end 3.302 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 1.016) (end 3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 -0.508) (end -2.794 -1.016) (layer F.SilkS) (width 0.3048))
+ (pad 1 thru_hole circle (at -3.81 0 90) (size 1.651 1.651) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 23 N-000018))
+ (pad 2 thru_hole circle (at 3.81 0 90) (size 1.651 1.651) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 12 GND))
+ (model discret/resistor.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 0.3 0.3 0.3))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module R3-LARGE_PADS (layer Composant) (tedit 47E26765) (tstamp 4AE1966C)
+ (at 156.21 120.396 270)
+ (descr "Resitance 3 pas")
+ (tags R)
+ (path /4B3A1333/4B3A1370)
+ (autoplace_cost180 10)
+ (fp_text reference R12 (at 0 0 270) (layer F.SilkS)
+ (effects (font (size 1.397 1.27) (thickness 0.2032)))
+ )
+ (fp_text value 220K (at 0 0 270) (layer F.SilkS) hide
+ (effects (font (size 1.397 1.27) (thickness 0.2032)))
+ )
+ (fp_line (start -3.81 0) (end -3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.81 0) (end 3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 0) (end 3.302 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 -1.016) (end -3.302 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 -1.016) (end -3.302 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 1.016) (end 3.302 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 1.016) (end 3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 -0.508) (end -2.794 -1.016) (layer F.SilkS) (width 0.3048))
+ (pad 1 thru_hole circle (at -3.81 0 270) (size 1.651 1.651) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 10 /ampli_h7))
+ (pad 2 thru_hole circle (at 3.81 0 270) (size 1.651 1.651) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 22 N-000017))
+ (model discret/resistor.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 0.3 0.3 0.3))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module R3-LARGE_PADS (layer Composant) (tedit 47E26765) (tstamp 4AE19682)
+ (at 141.605 86.106 180)
+ (descr "Resitance 3 pas")
+ (tags R)
+ (path /4B3A13A4/4B3A1376)
+ (autoplace_cost180 10)
+ (fp_text reference R13 (at 0 0 180) (layer F.SilkS)
+ (effects (font (size 1.397 1.27) (thickness 0.2032)))
+ )
+ (fp_text value 470 (at 0 0 180) (layer F.SilkS) hide
+ (effects (font (size 1.397 1.27) (thickness 0.2032)))
+ )
+ (fp_line (start -3.81 0) (end -3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.81 0) (end 3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 0) (end 3.302 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 -1.016) (end -3.302 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 -1.016) (end -3.302 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 1.016) (end 3.302 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 1.016) (end 3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 -0.508) (end -2.794 -1.016) (layer F.SilkS) (width 0.3048))
+ (pad 1 thru_hole circle (at -3.81 0 180) (size 1.651 1.651) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 13 HT))
+ (pad 2 thru_hole circle (at 3.81 0 180) (size 1.651 1.651) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 49 N-000048))
+ (model discret/resistor.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 0.3 0.3 0.3))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module R3-LARGE_PADS (layer Composant) (tedit 47E26765) (tstamp 4B4B1A4E)
+ (at 141.605 81.026)
+ (descr "Resitance 3 pas")
+ (tags R)
+ (path /4B3A13A4/4B3A1374)
+ (autoplace_cost180 10)
+ (fp_text reference R14 (at 0 0) (layer F.SilkS)
+ (effects (font (size 1.397 1.27) (thickness 0.2032)))
+ )
+ (fp_text value 220K (at 0 0) (layer F.SilkS) hide
+ (effects (font (size 1.397 1.27) (thickness 0.2032)))
+ )
+ (fp_line (start -3.81 0) (end -3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.81 0) (end 3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 0) (end 3.302 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 -1.016) (end -3.302 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 -1.016) (end -3.302 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 1.016) (end 3.302 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 1.016) (end 3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 -0.508) (end -2.794 -1.016) (layer F.SilkS) (width 0.3048))
+ (pad 1 thru_hole circle (at -3.81 0) (size 1.651 1.651) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 41 N-000040))
+ (pad 2 thru_hole circle (at 3.81 0) (size 1.651 1.651) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 12 GND))
+ (model discret/resistor.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 0.3 0.3 0.3))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module R3-LARGE_PADS (layer Composant) (tedit 47E26765) (tstamp 4AE19690)
+ (at 106.045 82.296 180)
+ (descr "Resitance 3 pas")
+ (tags R)
+ (path /4B3A13A4/4B3A136B)
+ (autoplace_cost180 10)
+ (fp_text reference R16 (at 0 0 180) (layer F.SilkS)
+ (effects (font (size 1.397 1.27) (thickness 0.2032)))
+ )
+ (fp_text value 22K (at 0 0 180) (layer F.SilkS) hide
+ (effects (font (size 1.397 1.27) (thickness 0.2032)))
+ )
+ (fp_line (start -3.81 0) (end -3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.81 0) (end 3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 0) (end 3.302 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 -1.016) (end -3.302 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 -1.016) (end -3.302 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 1.016) (end 3.302 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 1.016) (end 3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 -0.508) (end -2.794 -1.016) (layer F.SilkS) (width 0.3048))
+ (pad 1 thru_hole circle (at -3.81 0 180) (size 1.651 1.651) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 35 N-000034))
+ (pad 2 thru_hole circle (at 3.81 0 180) (size 1.651 1.651) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 36 N-000035))
+ (model discret/resistor.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 0.3 0.3 0.3))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module R3-LARGE_PADS (layer Composant) (tedit 47E26765) (tstamp 4AE19670)
+ (at 106.045 84.836)
+ (descr "Resitance 3 pas")
+ (tags R)
+ (path /4B3A13A4/4B3A1359)
+ (autoplace_cost180 10)
+ (fp_text reference R17 (at 0 0) (layer F.SilkS)
+ (effects (font (size 1.397 1.27) (thickness 0.2032)))
+ )
+ (fp_text value 22K (at 0 0) (layer F.SilkS) hide
+ (effects (font (size 1.397 1.27) (thickness 0.2032)))
+ )
+ (fp_line (start -3.81 0) (end -3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.81 0) (end 3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 0) (end 3.302 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 -1.016) (end -3.302 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 -1.016) (end -3.302 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 1.016) (end 3.302 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 1.016) (end 3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 -0.508) (end -2.794 -1.016) (layer F.SilkS) (width 0.3048))
+ (pad 1 thru_hole circle (at -3.81 0) (size 1.651 1.651) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 46 N-000045))
+ (pad 2 thru_hole circle (at 3.81 0) (size 1.651 1.651) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 35 N-000034))
+ (model discret/resistor.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 0.3 0.3 0.3))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module R3-LARGE_PADS (layer Composant) (tedit 47E26765) (tstamp 4AE1967A)
+ (at 121.793 101.346 180)
+ (descr "Resitance 3 pas")
+ (tags R)
+ (path /4B3A13A4/4B3A1371)
+ (autoplace_cost180 10)
+ (fp_text reference R18 (at 0 0 180) (layer F.SilkS)
+ (effects (font (size 1.397 1.27) (thickness 0.2032)))
+ )
+ (fp_text value 1K (at 0 0 180) (layer F.SilkS) hide
+ (effects (font (size 1.397 1.27) (thickness 0.2032)))
+ )
+ (fp_line (start -3.81 0) (end -3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.81 0) (end 3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 0) (end 3.302 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 -1.016) (end -3.302 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 -1.016) (end -3.302 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 1.016) (end 3.302 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 1.016) (end 3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 -0.508) (end -2.794 -1.016) (layer F.SilkS) (width 0.3048))
+ (pad 1 thru_hole circle (at -3.81 0 180) (size 1.651 1.651) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 42 N-000041))
+ (pad 2 thru_hole circle (at 3.81 0 180) (size 1.651 1.651) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 12 GND))
+ (model discret/resistor.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 0.3 0.3 0.3))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module R3-LARGE_PADS (layer Composant) (tedit 47E26765) (tstamp 4AE19692)
+ (at 122.301 94.996 180)
+ (descr "Resitance 3 pas")
+ (tags R)
+ (path /4B3A13A4/4B3A136D)
+ (autoplace_cost180 10)
+ (fp_text reference R19 (at 0 0 180) (layer F.SilkS)
+ (effects (font (size 1.397 1.27) (thickness 0.2032)))
+ )
+ (fp_text value 1K (at 0 0 180) (layer F.SilkS) hide
+ (effects (font (size 1.397 1.27) (thickness 0.2032)))
+ )
+ (fp_line (start -3.81 0) (end -3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.81 0) (end 3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 0) (end 3.302 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 -1.016) (end -3.302 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 -1.016) (end -3.302 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 1.016) (end 3.302 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 1.016) (end 3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 -0.508) (end -2.794 -1.016) (layer F.SilkS) (width 0.3048))
+ (pad 1 thru_hole circle (at -3.81 0 180) (size 1.651 1.651) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 33 N-000032))
+ (pad 2 thru_hole circle (at 3.81 0 180) (size 1.651 1.651) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 43 N-000042))
+ (model discret/resistor.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 0.3 0.3 0.3))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module R3-LARGE_PADS (layer Composant) (tedit 47E26765) (tstamp 4AE1969A)
+ (at 137.795 101.346 180)
+ (descr "Resitance 3 pas")
+ (tags R)
+ (path /4B3A13A4/4B61688C)
+ (autoplace_cost180 10)
+ (fp_text reference R20 (at 0 0 180) (layer F.SilkS)
+ (effects (font (size 1.397 1.27) (thickness 0.2032)))
+ )
+ (fp_text value 5,6K (at 0 0 180) (layer F.SilkS) hide
+ (effects (font (size 1.397 1.27) (thickness 0.2032)))
+ )
+ (fp_line (start -3.81 0) (end -3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.81 0) (end 3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 0) (end 3.302 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 -1.016) (end -3.302 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 -1.016) (end -3.302 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 1.016) (end 3.302 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 1.016) (end 3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 -0.508) (end -2.794 -1.016) (layer F.SilkS) (width 0.3048))
+ (pad 1 thru_hole circle (at -3.81 0 180) (size 1.651 1.651) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 47 N-000046))
+ (pad 2 thru_hole circle (at 3.81 0 180) (size 1.651 1.651) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 37 N-000036))
+ (model discret/resistor.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 0.3 0.3 0.3))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module R3-LARGE_PADS (layer Composant) (tedit 47E26765) (tstamp 4AE1968E)
+ (at 102.235 98.806 90)
+ (descr "Resitance 3 pas")
+ (tags R)
+ (path /4B3A13A4/4B3A1362)
+ (autoplace_cost180 10)
+ (fp_text reference R21 (at 0 0 90) (layer F.SilkS)
+ (effects (font (size 1.397 1.27) (thickness 0.2032)))
+ )
+ (fp_text value 4,7K (at 0 0 90) (layer F.SilkS) hide
+ (effects (font (size 1.397 1.27) (thickness 0.2032)))
+ )
+ (fp_line (start -3.81 0) (end -3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.81 0) (end 3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 0) (end 3.302 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 -1.016) (end -3.302 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 -1.016) (end -3.302 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 1.016) (end 3.302 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 1.016) (end 3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 -0.508) (end -2.794 -1.016) (layer F.SilkS) (width 0.3048))
+ (pad 1 thru_hole circle (at -3.81 0 90) (size 1.651 1.651) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 40 N-000039))
+ (pad 2 thru_hole circle (at 3.81 0 90) (size 1.651 1.651) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 12 GND))
+ (model discret/resistor.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 0.3 0.3 0.3))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module R3-LARGE_PADS (layer Composant) (tedit 47E26765) (tstamp 4AE19684)
+ (at 156.845 93.726 270)
+ (descr "Resitance 3 pas")
+ (tags R)
+ (path /4B3A13A4/4B3A1370)
+ (autoplace_cost180 10)
+ (fp_text reference R22 (at 0 0 270) (layer F.SilkS)
+ (effects (font (size 1.397 1.27) (thickness 0.2032)))
+ )
+ (fp_text value 220K (at 0 0 270) (layer F.SilkS) hide
+ (effects (font (size 1.397 1.27) (thickness 0.2032)))
+ )
+ (fp_line (start -3.81 0) (end -3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.81 0) (end 3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 0) (end 3.302 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 -1.016) (end -3.302 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 -1.016) (end -3.302 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 1.016) (end 3.302 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 1.016) (end 3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 -0.508) (end -2.794 -1.016) (layer F.SilkS) (width 0.3048))
+ (pad 1 thru_hole circle (at -3.81 0 270) (size 1.651 1.651) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 11 /ampli_h8))
+ (pad 2 thru_hole circle (at 3.81 0 270) (size 1.651 1.651) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 39 N-000038))
+ (model discret/resistor.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 0.3 0.3 0.3))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module MIRE (layer Composant) (tedit 200000) (tstamp 4B3A2B8E)
+ (at 188.595 51.816)
+ (fp_text reference MIRE (at -0.127 4.572) (layer F.SilkS) hide
+ (effects (font (size 1.016 1.016) (thickness 0.2032)))
+ )
+ (fp_text value * (at 3.302 2.794) (layer F.SilkS) hide
+ (effects (font (thickness 0.2032)))
+ )
+ (fp_circle (center 0 0) (end 2.54 1.905) (layer F.SilkS) (width 0.381))
+ (pad "" thru_hole rect (at -1.524 0) (size 2.794 0.254) (drill 0.762) (layers *.Cu *.Mask F.SilkS))
+ (pad "" thru_hole rect (at 0 -1.524) (size 0.254 2.794) (drill 0.762) (layers *.Cu *.Mask F.SilkS))
+ (pad "" thru_hole circle (at 0 0) (size 0.254 0.254) (drill 0.0508) (layers *.Cu *.Mask))
+ (pad "" thru_hole rect (at -1.524 0) (size 2.286 0.254) (drill 0.762) (layers *.Cu *.Mask F.SilkS))
+ (pad "" thru_hole rect (at 0 1.524) (size 0.254 2.794) (drill 0.762) (layers *.Cu *.Mask F.SilkS))
+ (pad "" thru_hole rect (at 1.524 0) (size 2.794 0.254) (drill 0.762) (layers *.Cu *.Mask F.SilkS))
+ )
+
+ (module MIRE (layer Composant) (tedit 200000) (tstamp 4B3A2B9E)
+ (at 88.265 51.816)
+ (fp_text reference MIRE (at -0.127 4.572) (layer F.SilkS) hide
+ (effects (font (size 1.016 1.016) (thickness 0.2032)))
+ )
+ (fp_text value * (at 3.302 2.794) (layer F.SilkS) hide
+ (effects (font (thickness 0.2032)))
+ )
+ (fp_circle (center 0 0) (end 2.54 1.905) (layer F.SilkS) (width 0.381))
+ (pad "" thru_hole rect (at -1.524 0) (size 2.794 0.254) (drill 0.762) (layers *.Cu *.Mask F.SilkS))
+ (pad "" thru_hole rect (at 0 -1.524) (size 0.254 2.794) (drill 0.762) (layers *.Cu *.Mask F.SilkS))
+ (pad "" thru_hole circle (at 0 0) (size 0.254 0.254) (drill 0.0508) (layers *.Cu *.Mask))
+ (pad "" thru_hole rect (at -1.524 0) (size 2.286 0.254) (drill 0.762) (layers *.Cu *.Mask F.SilkS))
+ (pad "" thru_hole rect (at 0 1.524) (size 0.254 2.794) (drill 0.762) (layers *.Cu *.Mask F.SilkS))
+ (pad "" thru_hole rect (at 1.524 0) (size 2.794 0.254) (drill 0.762) (layers *.Cu *.Mask F.SilkS))
+ )
+
+ (module MIRE (layer Composant) (tedit 200000) (tstamp 4B3A2BA7)
+ (at 88.265 131.826)
+ (fp_text reference MIRE (at -0.127 4.572) (layer F.SilkS) hide
+ (effects (font (size 1.016 1.016) (thickness 0.2032)))
+ )
+ (fp_text value * (at 3.302 2.794) (layer F.SilkS) hide
+ (effects (font (thickness 0.2032)))
+ )
+ (fp_circle (center 0 0) (end 2.54 1.905) (layer F.SilkS) (width 0.381))
+ (pad "" thru_hole rect (at -1.524 0) (size 2.794 0.254) (drill 0.762) (layers *.Cu *.Mask F.SilkS))
+ (pad "" thru_hole rect (at 0 -1.524) (size 0.254 2.794) (drill 0.762) (layers *.Cu *.Mask F.SilkS))
+ (pad "" thru_hole circle (at 0 0) (size 0.254 0.254) (drill 0.0508) (layers *.Cu *.Mask))
+ (pad "" thru_hole rect (at -1.524 0) (size 2.286 0.254) (drill 0.762) (layers *.Cu *.Mask F.SilkS))
+ (pad "" thru_hole rect (at 0 1.524) (size 0.254 2.794) (drill 0.762) (layers *.Cu *.Mask F.SilkS))
+ (pad "" thru_hole rect (at 1.524 0) (size 2.794 0.254) (drill 0.762) (layers *.Cu *.Mask F.SilkS))
+ )
+
+ (module MIRE (layer Composant) (tedit 200000) (tstamp 4B3A2BAD)
+ (at 188.595 131.826)
+ (fp_text reference MIRE (at -0.127 4.572) (layer F.SilkS) hide
+ (effects (font (size 1.016 1.016) (thickness 0.2032)))
+ )
+ (fp_text value * (at 3.302 2.794) (layer F.SilkS) hide
+ (effects (font (thickness 0.2032)))
+ )
+ (fp_circle (center 0 0) (end 2.54 1.905) (layer F.SilkS) (width 0.381))
+ (pad "" thru_hole rect (at -1.524 0) (size 2.794 0.254) (drill 0.762) (layers *.Cu *.Mask F.SilkS))
+ (pad "" thru_hole rect (at 0 -1.524) (size 0.254 2.794) (drill 0.762) (layers *.Cu *.Mask F.SilkS))
+ (pad "" thru_hole circle (at 0 0) (size 0.254 0.254) (drill 0.0508) (layers *.Cu *.Mask))
+ (pad "" thru_hole rect (at -1.524 0) (size 2.286 0.254) (drill 0.762) (layers *.Cu *.Mask F.SilkS))
+ (pad "" thru_hole rect (at 0 1.524) (size 0.254 2.794) (drill 0.762) (layers *.Cu *.Mask F.SilkS))
+ (pad "" thru_hole rect (at 1.524 0) (size 2.794 0.254) (drill 0.762) (layers *.Cu *.Mask F.SilkS))
+ )
+
+ (module CP6 (layer Composant) (tedit 200000) (tstamp 4B4B1727)
+ (at 147.32 77.216)
+ (descr "Condensateur polarise")
+ (tags CP)
+ (path /4B4B15E7)
+ (fp_text reference C10 (at 0 0) (layer F.SilkS)
+ (effects (font (thickness 0.3048)))
+ )
+ (fp_text value 10uF (at 0.635 0) (layer F.SilkS) hide
+ (effects (font (thickness 0.3048)))
+ )
+ (fp_line (start -7.62 0) (end -6.604 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -6.096 0.508) (end -6.604 0.508) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -6.604 0.508) (end -6.604 -0.508) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -6.604 -0.508) (end -6.096 -0.508) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 7.62 0) (end 6.604 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 6.604 0) (end 6.604 -1.524) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 6.604 -1.524) (end -6.096 -1.524) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -6.096 -1.524) (end -6.096 1.524) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -6.096 1.524) (end 6.604 1.524) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 6.604 1.524) (end 6.604 0) (layer F.SilkS) (width 0.3048))
+ (pad 1 thru_hole rect (at -7.62 0) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 15 N-000010))
+ (pad 2 thru_hole circle (at 7.62 0) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 14 N-000005))
+ (model discret/c_pol.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 0.6 0.6 0.6))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module CP6 (layer Composant) (tedit 200000) (tstamp 4B4B1729)
+ (at 160.02 70.866 90)
+ (descr "Condensateur polarise")
+ (tags CP)
+ (path /4B4B15D9)
+ (fp_text reference C11 (at 0 0 90) (layer F.SilkS)
+ (effects (font (thickness 0.3048)))
+ )
+ (fp_text value 10uF (at 0.635 0 90) (layer F.SilkS) hide
+ (effects (font (thickness 0.3048)))
+ )
+ (fp_line (start -7.62 0) (end -6.604 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -6.096 0.508) (end -6.604 0.508) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -6.604 0.508) (end -6.604 -0.508) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -6.604 -0.508) (end -6.096 -0.508) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 7.62 0) (end 6.604 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 6.604 0) (end 6.604 -1.524) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 6.604 -1.524) (end -6.096 -1.524) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -6.096 -1.524) (end -6.096 1.524) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -6.096 1.524) (end 6.604 1.524) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 6.604 1.524) (end 6.604 0) (layer F.SilkS) (width 0.3048))
+ (pad 1 thru_hole rect (at -7.62 0 90) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 12 GND))
+ (pad 2 thru_hole circle (at 7.62 0 90) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 2 -VAA))
+ (model discret/c_pol.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 0.6 0.6 0.6))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module LM78LXX (layer Composant) (tedit 4B58205C) (tstamp 4AE19669)
+ (at 125.095 69.596 90)
+ (descr "Regulateur TO92 serie LM78Lxx")
+ (tags "TR TO92")
+ (path /4B4B1532)
+ (fp_text reference U2 (at -1.905 3.81 90) (layer F.SilkS)
+ (effects (font (size 1.016 1.016) (thickness 0.2032)))
+ )
+ (fp_text value 78L05 (at -1.27 -5.08 90) (layer F.SilkS)
+ (effects (font (size 1.016 1.016) (thickness 0.2032)))
+ )
+ (fp_line (start -1.27 2.54) (end 2.54 -1.27) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 2.54 -1.27) (end 2.54 -2.54) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 2.54 -2.54) (end 1.27 -3.81) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 1.27 -3.81) (end -1.27 -3.81) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -1.27 -3.81) (end -3.81 -1.27) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.81 -1.27) (end -3.81 1.27) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.81 1.27) (end -2.54 2.54) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -2.54 2.54) (end -1.27 2.54) (layer F.SilkS) (width 0.3048))
+ (pad VI thru_hole circle (at 1.27 -1.27 90) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 1 +12V))
+ (pad GND thru_hole rect (at -1.27 -1.397 90) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 12 GND))
+ (pad VO thru_hole circle (at -1.27 1.27 90) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 50 VCC))
+ (model discret/to98.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 1 1 1))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module C2 (layer Composant) (tedit 200000) (tstamp 4B581420)
+ (at 116.205 78.486)
+ (descr "Condensateur = 2 pas")
+ (tags C)
+ (path /4B3A13A4/4B4F3641)
+ (fp_text reference C14 (at 0 0) (layer F.SilkS)
+ (effects (font (size 1.016 1.016) (thickness 0.2032)))
+ )
+ (fp_text value 150nF (at 0 0) (layer F.SilkS) hide
+ (effects (font (size 1.016 1.016) (thickness 0.2032)))
+ )
+ (fp_line (start -3.556 -1.016) (end 3.556 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.556 -1.016) (end 3.556 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.556 1.016) (end -3.556 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.556 1.016) (end -3.556 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.556 -0.508) (end -3.048 -1.016) (layer F.SilkS) (width 0.3048))
+ (pad 1 thru_hole circle (at -2.54 0) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 36 N-000035))
+ (pad 2 thru_hole circle (at 2.54 0) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 12 GND))
+ (model discret/capa_2pas_5x5mm.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 1 1 1))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module C2 (layer Composant) (tedit 200000) (tstamp 4B581422)
+ (at 116.205 107.061)
+ (descr "Condensateur = 2 pas")
+ (tags C)
+ (path /4B3A1333/4B4F3641)
+ (fp_text reference C12 (at 0 0) (layer F.SilkS)
+ (effects (font (size 1.016 1.016) (thickness 0.2032)))
+ )
+ (fp_text value 150nF (at 0 0) (layer F.SilkS) hide
+ (effects (font (size 1.016 1.016) (thickness 0.2032)))
+ )
+ (fp_line (start -3.556 -1.016) (end 3.556 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.556 -1.016) (end 3.556 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.556 1.016) (end -3.556 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.556 1.016) (end -3.556 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.556 -0.508) (end -3.048 -1.016) (layer F.SilkS) (width 0.3048))
+ (pad 1 thru_hole circle (at -2.54 0) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 19 N-000014))
+ (pad 2 thru_hole circle (at 2.54 0) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 12 GND))
+ (model discret/capa_2pas_5x5mm.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 1 1 1))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module R3-LARGE_PADS (layer Composant) (tedit 47E26765) (tstamp 4B581423)
+ (at 106.045 107.061 180)
+ (descr "Resitance 3 pas")
+ (tags R)
+ (path /4B3A1333/4B4F363E)
+ (autoplace_cost180 10)
+ (fp_text reference R23 (at 0 0 180) (layer F.SilkS)
+ (effects (font (size 1.397 1.27) (thickness 0.2032)))
+ )
+ (fp_text value 1K (at 0 0 180) (layer F.SilkS) hide
+ (effects (font (size 1.397 1.27) (thickness 0.2032)))
+ )
+ (fp_line (start -3.81 0) (end -3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.81 0) (end 3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 0) (end 3.302 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 -1.016) (end -3.302 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 -1.016) (end -3.302 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 1.016) (end 3.302 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 1.016) (end 3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 -0.508) (end -2.794 -1.016) (layer F.SilkS) (width 0.3048))
+ (pad 1 thru_hole circle (at -3.81 0 180) (size 1.651 1.651) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 19 N-000014))
+ (pad 2 thru_hole circle (at 3.81 0 180) (size 1.651 1.651) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 7 /ampli_h4))
+ (model discret/resistor.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 0.3 0.3 0.3))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module R3-LARGE_PADS (layer Composant) (tedit 47E26765) (tstamp 4B581425)
+ (at 106.553 78.74 180)
+ (descr "Resitance 3 pas")
+ (tags R)
+ (path /4B3A13A4/4B4F363E)
+ (autoplace_cost180 10)
+ (fp_text reference R24 (at 0 0 180) (layer F.SilkS)
+ (effects (font (size 1.397 1.27) (thickness 0.2032)))
+ )
+ (fp_text value 1K (at 0 0 180) (layer F.SilkS) hide
+ (effects (font (size 1.397 1.27) (thickness 0.2032)))
+ )
+ (fp_line (start -3.81 0) (end -3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.81 0) (end 3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 0) (end 3.302 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 -1.016) (end -3.302 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 -1.016) (end -3.302 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 1.016) (end 3.302 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 1.016) (end 3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 -0.508) (end -2.794 -1.016) (layer F.SilkS) (width 0.3048))
+ (pad 1 thru_hole circle (at -3.81 0 180) (size 1.651 1.651) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 36 N-000035))
+ (pad 2 thru_hole circle (at 3.81 0 180) (size 1.651 1.651) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 9 /ampli_h6))
+ (model discret/resistor.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 0.3 0.3 0.3))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module D3 (layer Composant) (tedit 200000) (tstamp 4B617CC8)
+ (at 145.415 93.726 270)
+ (descr "Diode 3 pas")
+ (tags "DIODE DEV")
+ (path /4B3A13A4/4B616AFA)
+ (fp_text reference D9 (at 0 0 270) (layer F.SilkS)
+ (effects (font (size 1.016 1.016) (thickness 0.2032)))
+ )
+ (fp_text value 1N4148 (at 0 0 270) (layer F.SilkS) hide
+ (effects (font (size 1.016 1.016) (thickness 0.2032)))
+ )
+ (fp_line (start 3.81 0) (end 3.048 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.048 0) (end 3.048 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.048 -1.016) (end -3.048 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.048 -1.016) (end -3.048 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.048 0) (end -3.81 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.048 0) (end -3.048 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.048 1.016) (end 3.048 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.048 1.016) (end 3.048 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 2.54 -1.016) (end 2.54 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 2.286 1.016) (end 2.286 -1.016) (layer F.SilkS) (width 0.3048))
+ (pad 2 thru_hole rect (at 3.81 0 270) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 45 N-000044))
+ (pad 1 thru_hole circle (at -3.81 0 270) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 48 N-000047))
+ (model discret/diode.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 0.3 0.3 0.3))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module D3 (layer Composant) (tedit 200000) (tstamp 4B617CCA)
+ (at 144.78 119.761 270)
+ (descr "Diode 3 pas")
+ (tags "DIODE DEV")
+ (path /4B3A1333/4B616AFA)
+ (fp_text reference D8 (at 0 0 270) (layer F.SilkS)
+ (effects (font (size 1.016 1.016) (thickness 0.2032)))
+ )
+ (fp_text value 1N4148 (at 0 0 270) (layer F.SilkS) hide
+ (effects (font (size 1.016 1.016) (thickness 0.2032)))
+ )
+ (fp_line (start 3.81 0) (end 3.048 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.048 0) (end 3.048 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.048 -1.016) (end -3.048 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.048 -1.016) (end -3.048 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.048 0) (end -3.81 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.048 0) (end -3.048 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.048 1.016) (end 3.048 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.048 1.016) (end 3.048 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 2.54 -1.016) (end 2.54 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 2.286 1.016) (end 2.286 -1.016) (layer F.SilkS) (width 0.3048))
+ (pad 2 thru_hole rect (at 3.81 0 270) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 28 N-000023))
+ (pad 1 thru_hole circle (at -3.81 0 270) (size 1.397 1.397) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 31 N-000026))
+ (model discret/diode.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 0.3 0.3 0.3))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module R3-LARGE_PADS (layer Composant) (tedit 47E26765) (tstamp 4B617CCB)
+ (at 169.545 122.936 180)
+ (descr "Resitance 3 pas")
+ (tags R)
+ (path /4B3A1333/4B616B96)
+ (autoplace_cost180 10)
+ (fp_text reference R25 (at 0 0 180) (layer F.SilkS)
+ (effects (font (size 1.397 1.27) (thickness 0.2032)))
+ )
+ (fp_text value 47 (at 0 0 180) (layer F.SilkS) hide
+ (effects (font (size 1.397 1.27) (thickness 0.2032)))
+ )
+ (fp_line (start -3.81 0) (end -3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.81 0) (end 3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 0) (end 3.302 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 -1.016) (end -3.302 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 -1.016) (end -3.302 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 1.016) (end 3.302 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 1.016) (end 3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 -0.508) (end -2.794 -1.016) (layer F.SilkS) (width 0.3048))
+ (pad 1 thru_hole circle (at -3.81 0 180) (size 1.651 1.651) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 6 /ampli_h3))
+ (pad 2 thru_hole circle (at 3.81 0 180) (size 1.651 1.651) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 22 N-000017))
+ (model discret/resistor.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 0.3 0.3 0.3))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module R3-LARGE_PADS (layer Composant) (tedit 47E26765) (tstamp 4B617CCD)
+ (at 160.02 120.396 270)
+ (descr "Resitance 3 pas")
+ (tags R)
+ (path /4B3A1333/4B617B88)
+ (autoplace_cost180 10)
+ (fp_text reference R26 (at 0 0 270) (layer F.SilkS)
+ (effects (font (size 1.397 1.27) (thickness 0.2032)))
+ )
+ (fp_text value 220K (at 0 0 270) (layer F.SilkS) hide
+ (effects (font (size 1.397 1.27) (thickness 0.2032)))
+ )
+ (fp_line (start -3.81 0) (end -3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.81 0) (end 3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 0) (end 3.302 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 -1.016) (end -3.302 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 -1.016) (end -3.302 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 1.016) (end 3.302 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 1.016) (end 3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 -0.508) (end -2.794 -1.016) (layer F.SilkS) (width 0.3048))
+ (pad 1 thru_hole circle (at -3.81 0 270) (size 1.651 1.651) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 10 /ampli_h7))
+ (pad 2 thru_hole circle (at 3.81 0 270) (size 1.651 1.651) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 21 N-000016))
+ (model discret/resistor.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 0.3 0.3 0.3))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module R3-LARGE_PADS (layer Composant) (tedit 47E26765) (tstamp 4B617CCF)
+ (at 169.545 97.536 180)
+ (descr "Resitance 3 pas")
+ (tags R)
+ (path /4B3A13A4/4B616B96)
+ (autoplace_cost180 10)
+ (fp_text reference R27 (at 0 0 180) (layer F.SilkS)
+ (effects (font (size 1.397 1.27) (thickness 0.2032)))
+ )
+ (fp_text value 47 (at 0 0 180) (layer F.SilkS) hide
+ (effects (font (size 1.397 1.27) (thickness 0.2032)))
+ )
+ (fp_line (start -3.81 0) (end -3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.81 0) (end 3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 0) (end 3.302 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 -1.016) (end -3.302 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 -1.016) (end -3.302 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 1.016) (end 3.302 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 1.016) (end 3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 -0.508) (end -2.794 -1.016) (layer F.SilkS) (width 0.3048))
+ (pad 1 thru_hole circle (at -3.81 0 180) (size 1.651 1.651) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 8 /ampli_h5))
+ (pad 2 thru_hole circle (at 3.81 0 180) (size 1.651 1.651) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 39 N-000038))
+ (model discret/resistor.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 0.3 0.3 0.3))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (module R3-LARGE_PADS (layer Composant) (tedit 47E26765) (tstamp 4B617CD1)
+ (at 161.29 93.726 270)
+ (descr "Resitance 3 pas")
+ (tags R)
+ (path /4B3A13A4/4B617B88)
+ (autoplace_cost180 10)
+ (fp_text reference R28 (at 0 0 270) (layer F.SilkS)
+ (effects (font (size 1.397 1.27) (thickness 0.2032)))
+ )
+ (fp_text value 220K (at 0 0 270) (layer F.SilkS) hide
+ (effects (font (size 1.397 1.27) (thickness 0.2032)))
+ )
+ (fp_line (start -3.81 0) (end -3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.81 0) (end 3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 0) (end 3.302 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 -1.016) (end -3.302 -1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 -1.016) (end -3.302 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 1.016) (end 3.302 1.016) (layer F.SilkS) (width 0.3048))
+ (fp_line (start 3.302 1.016) (end 3.302 0) (layer F.SilkS) (width 0.3048))
+ (fp_line (start -3.302 -0.508) (end -2.794 -1.016) (layer F.SilkS) (width 0.3048))
+ (pad 1 thru_hole circle (at -3.81 0 270) (size 1.651 1.651) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 11 /ampli_h8))
+ (pad 2 thru_hole circle (at 3.81 0 270) (size 1.651 1.651) (drill 0.8128) (layers *.Cu *.Mask F.SilkS)
+ (net 38 N-000037))
+ (model discret/resistor.wrl
+ (at (xyz 0 0 0))
+ (scale (xyz 0.3 0.3 0.3))
+ (rotate (xyz 0 0 0))
+ )
+ )
+
+ (gr_text "Actionneur\nPiezo New Amp\nV02" (at 173.355 68.453) (layer Composant)
+ (effects (font (size 2.032 1.524) (thickness 0.3048)))
+ )
+ (gr_text "Actionneur\nPiezo New Amp\nV02" (at 176.149 64.643) (layer Cuivre)
+ (effects (font (size 2.032 1.524) (thickness 0.3048)) (justify mirror))
+ )
+ (gr_line (start 88.265 131.826) (end 88.265 51.816) (angle 90) (layer Edge.Cuts) (width 0.2032))
+ (gr_line (start 188.595 131.826) (end 88.265 131.826) (angle 90) (layer Edge.Cuts) (width 0.2032))
+ (gr_line (start 188.595 51.816) (end 188.595 131.826) (angle 90) (layer Edge.Cuts) (width 0.2032))
+ (gr_line (start 88.265 51.816) (end 188.595 51.816) (angle 90) (layer Edge.Cuts) (width 0.2032))
+
+ (segment (start 91.6686 87.249) (end 98.298 87.249) (width 0.6096) (layer Cuivre) (net 1))
+ (segment (start 99.695 88.646) (end 107.315 88.646) (width 0.6096) (layer Cuivre) (net 1) (status 400))
+ (segment (start 98.298 87.249) (end 99.695 88.646) (width 0.6096) (layer Cuivre) (net 1))
+ (segment (start 91.6686 85.4837) (end 91.6686 81.8134) (width 0.6096) (layer Cuivre) (net 1))
+ (segment (start 100.838 76.962) (end 109.27842 76.962) (width 0.6096) (layer Cuivre) (net 1))
+ (segment (start 99.568 78.232) (end 100.838 76.962) (width 0.6096) (layer Cuivre) (net 1))
+ (segment (start 95.25 78.232) (end 99.568 78.232) (width 0.6096) (layer Cuivre) (net 1))
+ (segment (start 91.6686 81.8134) (end 95.25 78.232) (width 0.6096) (layer Cuivre) (net 1))
+ (segment (start 123.825 68.326) (end 123.825 63.74892) (width 0.6096) (layer Cuivre) (net 1) (status 800))
+ (segment (start 136.78916 63.74892) (end 139.83208 60.706) (width 0.6096) (layer Cuivre) (net 1))
+ (segment (start 122.4915 63.74892) (end 123.825 63.74892) (width 0.6096) (layer Cuivre) (net 1))
+ (segment (start 123.825 63.74892) (end 136.78916 63.74892) (width 0.6096) (layer Cuivre) (net 1))
+ (segment (start 117.91442 68.326) (end 122.4915 63.74892) (width 0.6096) (layer Cuivre) (net 1))
+ (segment (start 141.605 60.706) (end 139.83208 60.706) (width 0.6096) (layer Cuivre) (net 1) (status 800))
+ (segment (start 116.205 68.326) (end 117.91442 68.326) (width 0.6096) (layer Cuivre) (net 1) (status 800))
+ (segment (start 116.205 68.326) (end 116.205 70.03542) (width 0.6096) (layer Cuivre) (net 1) (status 800))
+ (segment (start 116.205 70.03542) (end 109.27842 76.962) (width 0.6096) (layer Cuivre) (net 1))
+ (segment (start 91.6686 112.75314) (end 91.6686 87.249) (width 0.6096) (layer Cuivre) (net 1))
+ (segment (start 91.6686 87.249) (end 91.6686 85.4837) (width 0.6096) (layer Cuivre) (net 1))
+ (segment (start 92.64904 113.73358) (end 91.6686 112.75314) (width 0.6096) (layer Cuivre) (net 1))
+ (segment (start 94.68866 113.73358) (end 92.64904 113.73358) (width 0.6096) (layer Cuivre) (net 1))
+ (segment (start 97.54108 116.586) (end 94.68866 113.73358) (width 0.6096) (layer Cuivre) (net 1))
+ (segment (start 107.315 116.586) (end 97.54108 116.586) (width 0.6096) (layer Cuivre) (net 1) (status 800))
+ (segment (start 113.411 126.492) (end 114.681 126.492) (width 0.6096) (layer Composant) (net 2))
+ (segment (start 114.935 126.238) (end 114.935 124.206) (width 0.6096) (layer Composant) (net 2) (status 400))
+ (segment (start 114.681 126.492) (end 114.935 126.238) (width 0.6096) (layer Composant) (net 2))
+ (segment (start 105.41 126.492) (end 104.775 125.857) (width 0.6096) (layer Composant) (net 2))
+ (segment (start 113.411 126.492) (end 105.41 126.492) (width 0.6096) (layer Composant) (net 2))
+ (segment (start 128.905 76.581) (end 128.143 76.581) (width 0.6096) (layer Cuivre) (net 2))
+ (segment (start 123.317 77.724) (end 122.555 78.486) (width 0.6096) (layer Cuivre) (net 2))
+ (segment (start 127 77.724) (end 123.317 77.724) (width 0.6096) (layer Cuivre) (net 2))
+ (segment (start 128.143 76.581) (end 127 77.724) (width 0.6096) (layer Cuivre) (net 2))
+ (segment (start 114.935 102.108) (end 114.935 102.362) (width 0.6096) (layer Composant) (net 2))
+ (segment (start 104.775 109.601) (end 104.775 125.857) (width 0.6096) (layer Composant) (net 2))
+ (segment (start 114.935 96.266) (end 114.935 102.108) (width 0.6096) (layer Composant) (net 2) (status 800))
+ (segment (start 114.935 102.362) (end 112.776 104.521) (width 0.6096) (layer Composant) (net 2))
+ (segment (start 112.776 104.521) (end 107.061 104.521) (width 0.6096) (layer Composant) (net 2))
+ (segment (start 107.061 104.521) (end 104.775 106.807) (width 0.6096) (layer Composant) (net 2))
+ (segment (start 104.775 106.807) (end 104.775 109.601) (width 0.6096) (layer Composant) (net 2))
+ (segment (start 114.935 96.266) (end 114.935 95.123) (width 0.6096) (layer Cuivre) (net 2) (status 800))
+ (segment (start 114.935 95.123) (end 117.602 92.456) (width 0.6096) (layer Cuivre) (net 2))
+ (segment (start 117.602 92.456) (end 120.015 92.456) (width 0.6096) (layer Cuivre) (net 2))
+ (segment (start 120.015 92.456) (end 122.555 89.916) (width 0.6096) (layer Cuivre) (net 2))
+ (segment (start 122.555 89.916) (end 122.555 78.486) (width 0.6096) (layer Cuivre) (net 2))
+ (segment (start 153.035 66.421) (end 153.035 64.516) (width 0.6096) (layer Cuivre) (net 2) (status 400))
+ (segment (start 152.4 67.056) (end 153.035 66.421) (width 0.6096) (layer Cuivre) (net 2))
+ (segment (start 140.97 67.056) (end 152.4 67.056) (width 0.6096) (layer Cuivre) (net 2))
+ (segment (start 131.445 76.581) (end 140.97 67.056) (width 0.6096) (layer Cuivre) (net 2))
+ (segment (start 128.905 76.581) (end 131.445 76.581) (width 0.6096) (layer Cuivre) (net 2))
+ (segment (start 153.035 64.516) (end 158.75 64.516) (width 0.6096) (layer Cuivre) (net 2) (status 800))
+ (segment (start 158.75 64.516) (end 160.02 63.246) (width 0.6096) (layer Cuivre) (net 2) (status 400))
+ (segment (start 98.92792 68.326) (end 97.65792 69.596) (width 0.6096) (layer Cuivre) (net 3))
+ (segment (start 103.505 68.326) (end 98.92792 68.326) (width 0.6096) (layer Cuivre) (net 3) (status 800))
+ (segment (start 95.885 69.596) (end 97.65792 69.596) (width 0.6096) (layer Cuivre) (net 3) (status 800))
+ (segment (start 116.713 112.522) (end 116.713 113.792) (width 0.4318) (layer Cuivre) (net 4))
+ (segment (start 116.713 121.158) (end 112.395 121.158) (width 0.4318) (layer Cuivre) (net 4))
+ (segment (start 118.618 119.253) (end 116.713 121.158) (width 0.4318) (layer Cuivre) (net 4))
+ (segment (start 118.618 115.697) (end 118.618 119.253) (width 0.4318) (layer Cuivre) (net 4))
+ (segment (start 116.713 113.792) (end 118.618 115.697) (width 0.4318) (layer Cuivre) (net 4))
+ (segment (start 112.395 121.158) (end 112.395 116.586) (width 0.4318) (layer Cuivre) (net 4) (status 400))
+ (segment (start 117.983 110.236) (end 116.713 111.506) (width 0.4318) (layer Cuivre) (net 4))
+ (segment (start 112.395 116.586) (end 109.855 116.586) (width 0.4318) (layer Cuivre) (net 4) (status C00))
+ (segment (start 116.713 111.506) (end 116.713 112.522) (width 0.4318) (layer Cuivre) (net 4))
+ (segment (start 118.745 110.236) (end 117.983 110.236) (width 0.4318) (layer Cuivre) (net 4) (status 800))
+ (segment (start 112.395 124.206) (end 112.395 121.158) (width 0.4318) (layer Cuivre) (net 4) (status 800))
+ (segment (start 109.855 88.646) (end 112.395 88.646) (width 0.4318) (layer Cuivre) (net 5) (status C00))
+ (segment (start 118.745 81.026) (end 118.745 81.661) (width 0.4318) (layer Cuivre) (net 5) (status 800))
+ (segment (start 118.745 81.661) (end 116.84 83.566) (width 0.4318) (layer Cuivre) (net 5))
+ (segment (start 116.078 90.678) (end 114.427 90.678) (width 0.4318) (layer Cuivre) (net 5))
+ (segment (start 112.395 88.646) (end 112.395 96.266) (width 0.4318) (layer Cuivre) (net 5) (status C00))
+ (segment (start 114.427 90.678) (end 112.395 88.646) (width 0.4318) (layer Cuivre) (net 5) (status 400))
+ (segment (start 116.84 83.566) (end 116.84 89.916) (width 0.4318) (layer Cuivre) (net 5))
+ (segment (start 116.84 89.916) (end 116.078 90.678) (width 0.4318) (layer Cuivre) (net 5))
+ (segment (start 173.355 122.936) (end 173.355 119.126) (width 0.4318) (layer Cuivre) (net 6) (status C00))
+ (segment (start 173.355 122.936) (end 182.245 122.936) (width 0.4318) (layer Cuivre) (net 6) (status C00))
+ (segment (start 95.758 107.061) (end 102.235 107.061) (width 0.4318) (layer Cuivre) (net 7) (status 400))
+ (segment (start 94.615 108.204) (end 95.758 107.061) (width 0.4318) (layer Cuivre) (net 7))
+ (segment (start 94.615 111.506) (end 94.615 108.204) (width 0.4318) (layer Cuivre) (net 7) (status 800))
+ (segment (start 173.355 97.536) (end 173.355 100.076) (width 0.4318) (layer Cuivre) (net 8) (status 800))
+ (segment (start 173.355 100.076) (end 182.245 100.076) (width 0.4318) (layer Cuivre) (net 8) (status 400))
+ (segment (start 173.355 97.536) (end 173.355 91.186) (width 0.4318) (layer Cuivre) (net 8) (status C00))
+ (segment (start 100.965 80.518) (end 102.743 78.74) (width 0.4318) (layer Cuivre) (net 9) (status 400))
+ (segment (start 95.885 83.185) (end 98.552 80.518) (width 0.4318) (layer Cuivre) (net 9))
+ (segment (start 98.552 80.518) (end 100.965 80.518) (width 0.4318) (layer Cuivre) (net 9))
+ (segment (start 95.885 84.836) (end 95.885 83.185) (width 0.4318) (layer Cuivre) (net 9) (status 800))
+ (segment (start 133.35 118.364) (end 134.112 118.364) (width 0.4318) (layer Cuivre) (net 10))
+ (segment (start 142.875 117.221) (end 146.05 120.396) (width 0.4318) (layer Cuivre) (net 10))
+ (segment (start 142.875 115.316) (end 142.875 117.221) (width 0.4318) (layer Cuivre) (net 10))
+ (segment (start 141.605 114.046) (end 142.875 115.316) (width 0.4318) (layer Cuivre) (net 10))
+ (segment (start 138.43 114.046) (end 141.605 114.046) (width 0.4318) (layer Cuivre) (net 10))
+ (segment (start 134.112 118.364) (end 138.43 114.046) (width 0.4318) (layer Cuivre) (net 10))
+ (segment (start 152.4 120.396) (end 146.05 120.396) (width 0.4318) (layer Cuivre) (net 10))
+ (segment (start 152.4 120.396) (end 156.21 116.586) (width 0.4318) (layer Cuivre) (net 10) (status 400))
+ (segment (start 160.02 116.586) (end 156.21 116.586) (width 0.4318) (layer Cuivre) (net 10) (status C00))
+ (segment (start 98.425 125.476) (end 99.695 125.476) (width 0.4318) (layer Cuivre) (net 10) (status 800))
+ (segment (start 105.029 125.222) (end 105.029 127.254) (width 0.4318) (layer Cuivre) (net 10))
+ (segment (start 101.092 124.079) (end 103.886 124.079) (width 0.4318) (layer Cuivre) (net 10))
+ (segment (start 103.886 124.079) (end 105.029 125.222) (width 0.4318) (layer Cuivre) (net 10))
+ (segment (start 99.695 125.476) (end 101.092 124.079) (width 0.4318) (layer Cuivre) (net 10))
+ (segment (start 124.0155 125.9205) (end 118.0465 125.9205) (width 0.4318) (layer Cuivre) (net 10))
+ (segment (start 115.189 128.778) (end 106.553 128.778) (width 0.4318) (layer Cuivre) (net 10))
+ (segment (start 106.553 128.778) (end 105.029 127.254) (width 0.4318) (layer Cuivre) (net 10))
+ (segment (start 118.0465 125.9205) (end 117.221 126.746) (width 0.4318) (layer Cuivre) (net 10))
+ (segment (start 131.572 118.364) (end 133.35 118.364) (width 0.4318) (layer Cuivre) (net 10))
+ (segment (start 124.0155 125.9205) (end 131.572 118.364) (width 0.4318) (layer Cuivre) (net 10))
+ (segment (start 117.221 126.746) (end 115.189 128.778) (width 0.4318) (layer Cuivre) (net 10))
+ (segment (start 156.845 92.456) (end 154.305 94.996) (width 0.4318) (layer Cuivre) (net 11))
+ (segment (start 154.305 94.996) (end 146.685 94.996) (width 0.4318) (layer Cuivre) (net 11))
+ (segment (start 156.845 89.916) (end 161.29 89.916) (width 0.4318) (layer Cuivre) (net 11) (status C00))
+ (segment (start 118.618 99.187) (end 116.586 99.187) (width 0.4318) (layer Cuivre) (net 11))
+ (segment (start 135.89 92.71) (end 123.698 92.71) (width 0.4318) (layer Cuivre) (net 11))
+ (segment (start 120.523 95.885) (end 120.523 98.552) (width 0.4318) (layer Cuivre) (net 11))
+ (segment (start 123.698 92.71) (end 120.523 95.885) (width 0.4318) (layer Cuivre) (net 11))
+ (segment (start 119.888 99.187) (end 120.523 98.552) (width 0.4318) (layer Cuivre) (net 11))
+ (segment (start 118.618 99.187) (end 119.888 99.187) (width 0.4318) (layer Cuivre) (net 11))
+ (segment (start 105.7275 102.0445) (end 113.7285 102.0445) (width 0.4318) (layer Cuivre) (net 11))
+ (segment (start 105.7275 102.0445) (end 103.69804 100.01504) (width 0.4318) (layer Cuivre) (net 11))
+ (segment (start 98.48596 100.01504) (end 103.69804 100.01504) (width 0.4318) (layer Cuivre) (net 11))
+ (segment (start 116.586 99.187) (end 116.078 99.695) (width 0.4318) (layer Cuivre) (net 11))
+ (segment (start 113.7285 102.0445) (end 116.078 99.695) (width 0.4318) (layer Cuivre) (net 11))
+ (segment (start 98.425 100.076) (end 98.48596 100.01504) (width 0.4318) (layer Cuivre) (net 11) (status 800))
+ (segment (start 135.89 92.71) (end 135.89 91.186) (width 0.4318) (layer Cuivre) (net 11))
+ (segment (start 143.51 91.821) (end 146.685 94.996) (width 0.4318) (layer Cuivre) (net 11))
+ (segment (start 143.51 89.281) (end 143.51 91.821) (width 0.4318) (layer Cuivre) (net 11))
+ (segment (start 142.24 88.011) (end 143.51 89.281) (width 0.4318) (layer Cuivre) (net 11))
+ (segment (start 139.065 88.011) (end 142.24 88.011) (width 0.4318) (layer Cuivre) (net 11))
+ (segment (start 135.89 91.186) (end 139.065 88.011) (width 0.4318) (layer Cuivre) (net 11))
+ (segment (start 156.845 89.916) (end 156.845 92.456) (width 0.4318) (layer Cuivre) (net 11) (status 800))
+ (segment (start 150.495 70.231) (end 151.765 68.961) (width 0.6096) (layer Cuivre) (net 12))
+ (segment (start 150.495 72.136) (end 150.495 70.231) (width 0.6096) (layer Cuivre) (net 12) (status 800))
+ (segment (start 151.765 68.961) (end 156.21 68.961) (width 0.6096) (layer Cuivre) (net 12))
+ (segment (start 123.698 70.866) (end 123.825 70.866) (width 0.6096) (layer Cuivre) (net 12) (status 800))
+ (segment (start 123.825 70.866) (end 125.73 68.961) (width 0.6096) (layer Cuivre) (net 12))
+ (segment (start 156.21 68.961) (end 160.02 72.771) (width 0.6096) (layer Cuivre) (net 12))
+ (segment (start 160.02 72.771) (end 160.02 78.486) (width 0.6096) (layer Cuivre) (net 12) (status 400))
+ (segment (start 122.555 60.706) (end 121.76506 60.706) (width 0.6096) (layer Cuivre) (net 13) (status 800))
+ (segment (start 124.8537 57.61736) (end 160.74136 57.61736) (width 0.6096) (layer Cuivre) (net 13))
+ (segment (start 132.715 79.756) (end 135.76046 82.80146) (width 0.6096) (layer Cuivre) (net 13) (status 800))
+ (segment (start 164.465 77.851) (end 159.385 82.931) (width 0.6096) (layer Cuivre) (net 13))
+ (segment (start 143.383 108.966) (end 146.685 108.966) (width 0.6096) (layer Cuivre) (net 13) (status 400))
+ (segment (start 140.843 106.426) (end 143.383 108.966) (width 0.6096) (layer Cuivre) (net 13))
+ (segment (start 145.415 86.106) (end 147.955 83.566) (width 0.6096) (layer Composant) (net 13) (status 800))
+ (segment (start 135.255 106.426) (end 140.716 106.426) (width 0.6096) (layer Cuivre) (net 13) (status 800))
+ (segment (start 160.74136 57.61736) (end 164.465 61.341) (width 0.6096) (layer Cuivre) (net 13))
+ (segment (start 140.716 106.426) (end 140.843 106.426) (width 0.6096) (layer Cuivre) (net 13))
+ (segment (start 135.76046 82.80146) (end 142.11046 82.80146) (width 0.6096) (layer Cuivre) (net 13))
+ (segment (start 121.76506 60.706) (end 124.8537 57.61736) (width 0.6096) (layer Cuivre) (net 13))
+ (segment (start 146.685 108.966) (end 150.93696 113.21542) (width 0.6096) (layer Cuivre) (net 13) (status 800))
+ (segment (start 151.765 115.316) (end 151.765 114.04854) (width 0.6096) (layer Cuivre) (net 13) (status 800))
+ (segment (start 151.765 114.04854) (end 150.93696 113.21542) (width 0.6096) (layer Cuivre) (net 13))
+ (segment (start 117.16258 56.896) (end 95.885 56.896) (width 0.6096) (layer Cuivre) (net 13) (status 400))
+ (segment (start 120.97258 60.706) (end 117.16258 56.896) (width 0.6096) (layer Cuivre) (net 13))
+ (segment (start 159.385 82.931) (end 152.4 82.931) (width 0.6096) (layer Cuivre) (net 13))
+ (segment (start 149.225 86.106) (end 145.415 86.106) (width 0.6096) (layer Cuivre) (net 13) (status 400))
+ (segment (start 164.465 61.341) (end 164.465 77.851) (width 0.6096) (layer Cuivre) (net 13))
+ (segment (start 151.765 88.646) (end 149.225 86.106) (width 0.6096) (layer Cuivre) (net 13) (status 800))
+ (segment (start 142.11046 82.80146) (end 145.415 86.106) (width 0.6096) (layer Cuivre) (net 13) (status 400))
+ (segment (start 151.765 108.966) (end 146.685 108.966) (width 0.6096) (layer Composant) (net 13) (status 400))
+ (segment (start 147.955 83.566) (end 151.765 83.566) (width 0.6096) (layer Composant) (net 13))
+ (segment (start 151.765 83.566) (end 154.305 86.106) (width 0.6096) (layer Composant) (net 13))
+ (segment (start 154.305 86.106) (end 154.305 106.426) (width 0.6096) (layer Composant) (net 13))
+ (segment (start 121.76506 60.706) (end 120.97258 60.706) (width 0.6096) (layer Cuivre) (net 13))
+ (segment (start 154.305 106.426) (end 151.765 108.966) (width 0.6096) (layer Composant) (net 13))
+ (segment (start 152.4 82.931) (end 149.225 86.106) (width 0.6096) (layer Cuivre) (net 13))
+ (segment (start 153.035 72.136) (end 153.035 75.311) (width 0.4318) (layer Cuivre) (net 14) (status 800))
+ (segment (start 153.035 75.311) (end 154.94 77.216) (width 0.4318) (layer Cuivre) (net 14) (status 400))
+ (segment (start 147.955 70.231) (end 147.955 72.136) (width 0.4318) (layer Cuivre) (net 15) (status 400))
+ (segment (start 142.24 68.961) (end 146.685 68.961) (width 0.4318) (layer Cuivre) (net 15))
+ (segment (start 146.685 68.961) (end 147.955 70.231) (width 0.4318) (layer Cuivre) (net 15))
+ (segment (start 139.7 77.216) (end 139.7 71.501) (width 0.4318) (layer Cuivre) (net 15) (status 800))
+ (segment (start 139.7 71.501) (end 142.24 68.961) (width 0.4318) (layer Cuivre) (net 15))
+ (segment (start 134.112 120.142) (end 132.715 121.539) (width 0.4318) (layer Cuivre) (net 16))
+ (segment (start 132.715 121.539) (end 132.715 125.476) (width 0.4318) (layer Cuivre) (net 16))
+ (segment (start 134.493 120.142) (end 134.112 120.142) (width 0.4318) (layer Cuivre) (net 16) (status 800))
+ (segment (start 132.715 125.476) (end 131.445 126.746) (width 0.4318) (layer Cuivre) (net 16) (status 400))
+ (segment (start 127.635 106.426) (end 128.83642 106.426) (width 0.4318) (layer Cuivre) (net 17) (status 800))
+ (segment (start 131.37642 108.966) (end 128.83642 106.426) (width 0.4318) (layer Cuivre) (net 17))
+ (segment (start 135.255 108.966) (end 131.37642 108.966) (width 0.4318) (layer Cuivre) (net 17) (status 800))
+ (segment (start 113.665 110.236) (end 109.855 110.236) (width 0.4318) (layer Cuivre) (net 18) (status C00))
+ (segment (start 104.775 110.236) (end 109.855 110.236) (width 0.4318) (layer Cuivre) (net 18) (status 400))
+ (segment (start 102.235 112.776) (end 104.775 110.236) (width 0.4318) (layer Cuivre) (net 18) (status 800))
+ (segment (start 106.172 107.061) (end 109.855 107.061) (width 0.4318) (layer Cuivre) (net 19) (status 400))
+ (segment (start 102.235 110.236) (end 102.997 110.236) (width 0.4318) (layer Cuivre) (net 19) (status 800))
+ (segment (start 109.855 107.061) (end 113.665 107.061) (width 0.4318) (layer Cuivre) (net 19) (status C00))
+ (segment (start 102.997 110.236) (end 106.172 107.061) (width 0.4318) (layer Cuivre) (net 19))
+ (segment (start 136.779 127.381) (end 136.779 128.651) (width 0.4318) (layer Cuivre) (net 20) (status 800))
+ (segment (start 136.779 128.651) (end 138.176 130.048) (width 0.4318) (layer Cuivre) (net 20) (status 400))
+ (segment (start 160.02 124.206) (end 160.02 121.031) (width 0.4318) (layer Cuivre) (net 21) (status 800))
+ (segment (start 161.925 119.126) (end 165.735 119.126) (width 0.4318) (layer Cuivre) (net 21) (status 400))
+ (segment (start 160.02 121.031) (end 161.925 119.126) (width 0.4318) (layer Cuivre) (net 21))
+ (segment (start 165.735 119.126) (end 165.735 115.951) (width 0.4318) (layer Cuivre) (net 21) (status 800))
+ (segment (start 161.925 114.046) (end 156.21 114.046) (width 0.4318) (layer Cuivre) (net 21))
+ (segment (start 156.21 114.046) (end 152.4 117.856) (width 0.4318) (layer Cuivre) (net 21))
+ (segment (start 152.4 117.856) (end 149.225 117.856) (width 0.4318) (layer Cuivre) (net 21) (status 400))
+ (segment (start 165.735 115.951) (end 163.83 114.046) (width 0.4318) (layer Cuivre) (net 21))
+ (segment (start 163.83 114.046) (end 161.925 114.046) (width 0.4318) (layer Cuivre) (net 21))
+ (segment (start 156.21 124.206) (end 156.21 126.111) (width 0.4318) (layer Cuivre) (net 22) (status 800))
+ (segment (start 163.195 126.111) (end 165.735 122.936) (width 0.4318) (layer Cuivre) (net 22) (status 400))
+ (segment (start 162.56 126.746) (end 163.195 126.111) (width 0.4318) (layer Cuivre) (net 22))
+ (segment (start 156.845 126.746) (end 162.56 126.746) (width 0.4318) (layer Cuivre) (net 22))
+ (segment (start 156.21 126.111) (end 156.845 126.746) (width 0.4318) (layer Cuivre) (net 22))
+ (segment (start 150.495 122.936) (end 154.94 122.936) (width 0.4318) (layer Cuivre) (net 22) (status 800))
+ (segment (start 154.94 122.936) (end 156.21 124.206) (width 0.4318) (layer Cuivre) (net 22) (status 400))
+ (segment (start 102.87 126.746) (end 101.6 128.016) (width 0.4318) (layer Cuivre) (net 23) (status 800))
+ (segment (start 93.345 125.476) (end 95.885 128.016) (width 0.4318) (layer Cuivre) (net 23) (status 800))
+ (segment (start 95.885 128.016) (end 101.6 128.016) (width 0.4318) (layer Cuivre) (net 23))
+ (segment (start 127.635 110.16742) (end 130.175 112.70742) (width 0.4318) (layer Cuivre) (net 24))
+ (segment (start 127.635 108.966) (end 127.635 110.16742) (width 0.4318) (layer Cuivre) (net 24) (status 800))
+ (segment (start 122.555 115.316) (end 122.555 115.24742) (width 0.4318) (layer Cuivre) (net 24) (status 800))
+ (segment (start 130.175 112.70742) (end 130.175 114.046) (width 0.4318) (layer Cuivre) (net 24) (status 400))
+ (segment (start 122.555 115.24742) (end 127.635 110.16742) (width 0.4318) (layer Cuivre) (net 24))
+ (segment (start 130.175 124.206) (end 131.445 124.206) (width 0.4318) (layer Cuivre) (net 25) (status 400))
+ (segment (start 126.365 128.016) (end 130.175 124.206) (width 0.4318) (layer Cuivre) (net 25) (status 800))
+ (segment (start 126.873 121.666) (end 126.873 120.142) (width 0.4318) (layer Cuivre) (net 26) (status 400))
+ (segment (start 123.444 125.095) (end 126.873 121.666) (width 0.4318) (layer Cuivre) (net 26))
+ (segment (start 107.315 125.476) (end 107.315 124.206) (width 0.4318) (layer Cuivre) (net 26) (status 400))
+ (segment (start 115.57 126.873) (end 108.712 126.873) (width 0.4318) (layer Cuivre) (net 26))
+ (segment (start 117.348 125.095) (end 123.444 125.095) (width 0.4318) (layer Cuivre) (net 26))
+ (segment (start 108.712 126.873) (end 107.315 125.476) (width 0.4318) (layer Cuivre) (net 26))
+ (segment (start 116.5225 125.9205) (end 117.348 125.095) (width 0.4318) (layer Cuivre) (net 26))
+ (segment (start 116.5225 125.9205) (end 115.57 126.873) (width 0.4318) (layer Cuivre) (net 26))
+ (segment (start 109.093 121.539) (end 109.855 122.301) (width 0.4318) (layer Cuivre) (net 27))
+ (segment (start 102.108 122.936) (end 103.505 121.539) (width 0.4318) (layer Cuivre) (net 27))
+ (segment (start 95.885 122.936) (end 102.108 122.936) (width 0.4318) (layer Cuivre) (net 27) (status 800))
+ (segment (start 109.855 122.301) (end 109.855 124.206) (width 0.4318) (layer Cuivre) (net 27) (status 400))
+ (segment (start 103.505 121.539) (end 109.093 121.539) (width 0.4318) (layer Cuivre) (net 27))
+ (segment (start 144.78 122.301) (end 144.78 123.571) (width 0.4318) (layer Cuivre) (net 28) (status 400))
+ (segment (start 140.97 118.491) (end 144.78 122.301) (width 0.4318) (layer Cuivre) (net 28))
+ (segment (start 140.97 115.951) (end 140.97 118.491) (width 0.4318) (layer Cuivre) (net 28) (status 800))
+ (segment (start 109.855 112.776) (end 113.665 112.776) (width 0.4318) (layer Cuivre) (net 29) (status C00))
+ (segment (start 114.935 114.046) (end 114.935 116.586) (width 0.4318) (layer Cuivre) (net 29) (status 400))
+ (segment (start 113.665 112.776) (end 114.935 114.046) (width 0.4318) (layer Cuivre) (net 29) (status 800))
+ (segment (start 140.97 123.571) (end 140.97 123.952) (width 0.4318) (layer Cuivre) (net 30) (status 800))
+ (segment (start 140.97 123.952) (end 144.399 127.381) (width 0.4318) (layer Cuivre) (net 30) (status 400))
+ (segment (start 133.985 124.206) (end 137.795 124.206) (width 0.4318) (layer Cuivre) (net 30) (status 800))
+ (segment (start 138.43 123.571) (end 140.97 123.571) (width 0.4318) (layer Cuivre) (net 30) (status 400))
+ (segment (start 137.795 124.206) (end 138.43 123.571) (width 0.4318) (layer Cuivre) (net 30))
+ (segment (start 144.399 127.381) (end 144.78 127.381) (width 0.4318) (layer Cuivre) (net 30) (status 800))
+ (segment (start 144.78 127.381) (end 145.415 128.016) (width 0.4318) (layer Cuivre) (net 30))
+ (segment (start 145.415 128.016) (end 149.225 128.016) (width 0.4318) (layer Cuivre) (net 30))
+ (segment (start 149.225 128.016) (end 150.495 126.746) (width 0.4318) (layer Cuivre) (net 30))
+ (segment (start 150.495 126.746) (end 150.495 125.476) (width 0.4318) (layer Cuivre) (net 30) (status 400))
+ (segment (start 138.43 112.141) (end 136.525 114.046) (width 0.4318) (layer Cuivre) (net 31))
+ (segment (start 144.145 112.141) (end 138.43 112.141) (width 0.4318) (layer Cuivre) (net 31))
+ (segment (start 144.78 112.776) (end 144.145 112.141) (width 0.4318) (layer Cuivre) (net 31))
+ (segment (start 149.225 115.316) (end 147.32 113.411) (width 0.4318) (layer Cuivre) (net 31) (status 800))
+ (segment (start 147.32 113.411) (end 144.78 113.411) (width 0.4318) (layer Cuivre) (net 31))
+ (segment (start 136.525 114.046) (end 132.715 114.046) (width 0.4318) (layer Cuivre) (net 31) (status 400))
+ (segment (start 144.78 115.951) (end 144.78 113.411) (width 0.4318) (layer Cuivre) (net 31) (status 800))
+ (segment (start 144.78 113.411) (end 144.78 112.776) (width 0.4318) (layer Cuivre) (net 31))
+ (segment (start 135.18896 112.84204) (end 139.065 108.966) (width 0.4318) (layer Cuivre) (net 32) (status 400))
+ (segment (start 131.56692 112.84204) (end 135.18896 112.84204) (width 0.4318) (layer Cuivre) (net 32))
+ (segment (start 131.37642 115.26266) (end 131.37642 113.03254) (width 0.4318) (layer Cuivre) (net 32))
+ (segment (start 130.175 116.586) (end 130.175 115.38458) (width 0.4318) (layer Cuivre) (net 32) (status 800))
+ (segment (start 131.2545 115.38458) (end 131.37642 115.26266) (width 0.4318) (layer Cuivre) (net 32))
+ (segment (start 131.37642 113.03254) (end 131.56692 112.84204) (width 0.4318) (layer Cuivre) (net 32))
+ (segment (start 130.175 115.38458) (end 131.2545 115.38458) (width 0.4318) (layer Cuivre) (net 32))
+ (segment (start 126.619 103.632) (end 130.175 100.076) (width 0.4318) (layer Cuivre) (net 33) (status 400))
+ (segment (start 123.19 96.647) (end 123.19 102.362) (width 0.4318) (layer Cuivre) (net 33))
+ (segment (start 123.19 102.362) (end 124.46 103.632) (width 0.4318) (layer Cuivre) (net 33))
+ (segment (start 124.841 94.996) (end 123.19 96.647) (width 0.4318) (layer Cuivre) (net 33))
+ (segment (start 126.111 94.996) (end 124.841 94.996) (width 0.4318) (layer Cuivre) (net 33) (status 800))
+ (segment (start 124.46 103.632) (end 126.619 103.632) (width 0.4318) (layer Cuivre) (net 33))
+ (segment (start 125.095 79.756) (end 125.095 82.296) (width 0.4318) (layer Cuivre) (net 34) (status C00))
+ (segment (start 109.855 82.296) (end 109.855 81.661) (width 0.4318) (layer Cuivre) (net 35) (status 800))
+ (segment (start 109.855 84.836) (end 109.855 82.296) (width 0.4318) (layer Cuivre) (net 35) (status C00))
+ (segment (start 110.49 81.026) (end 113.665 81.026) (width 0.4318) (layer Cuivre) (net 35) (status 400))
+ (segment (start 109.855 81.661) (end 110.49 81.026) (width 0.4318) (layer Cuivre) (net 35))
+ (segment (start 102.235 82.296) (end 106.807 82.296) (width 0.4318) (layer Cuivre) (net 36) (status 800))
+ (segment (start 110.363 78.74) (end 113.411 78.74) (width 0.4318) (layer Cuivre) (net 36) (status 800))
+ (segment (start 113.411 78.74) (end 113.665 78.486) (width 0.4318) (layer Cuivre) (net 36) (status 400))
+ (segment (start 106.807 82.296) (end 110.363 78.74) (width 0.4318) (layer Cuivre) (net 36) (status 400))
+ (segment (start 135.255 101.346) (end 138.049 104.14) (width 0.4318) (layer Cuivre) (net 37) (status 400))
+ (segment (start 133.985 101.346) (end 135.255 101.346) (width 0.4318) (layer Cuivre) (net 37) (status 800))
+ (segment (start 161.29 97.536) (end 161.29 95.631) (width 0.4318) (layer Cuivre) (net 38) (status 800))
+ (segment (start 161.29 95.631) (end 165.735 91.186) (width 0.4318) (layer Cuivre) (net 38) (status 400))
+ (segment (start 165.735 91.186) (end 165.735 88.011) (width 0.4318) (layer Cuivre) (net 38) (status 800))
+ (segment (start 154.305 89.916) (end 154.305 87.376) (width 0.4318) (layer Cuivre) (net 38))
+ (segment (start 154.305 87.376) (end 155.575 86.106) (width 0.4318) (layer Cuivre) (net 38))
+ (segment (start 155.575 86.106) (end 160.655 86.106) (width 0.4318) (layer Cuivre) (net 38))
+ (segment (start 149.225 91.186) (end 153.035 91.186) (width 0.4318) (layer Cuivre) (net 38) (status 800))
+ (segment (start 154.305 89.916) (end 153.035 91.186) (width 0.4318) (layer Cuivre) (net 38))
+ (segment (start 165.735 88.011) (end 163.83 86.106) (width 0.4318) (layer Cuivre) (net 38))
+ (segment (start 163.83 86.106) (end 160.655 86.106) (width 0.4318) (layer Cuivre) (net 38))
+ (segment (start 165.735 97.536) (end 165.735 98.806) (width 0.4318) (layer Cuivre) (net 39) (status 800))
+ (segment (start 164.465 100.076) (end 161.925 100.076) (width 0.4318) (layer Cuivre) (net 39))
+ (segment (start 165.735 98.806) (end 164.465 100.076) (width 0.4318) (layer Cuivre) (net 39))
+ (segment (start 150.495 98.171) (end 156.21 98.171) (width 0.4318) (layer Cuivre) (net 39) (status 800))
+ (segment (start 156.21 98.171) (end 156.845 97.536) (width 0.4318) (layer Cuivre) (net 39) (status 400))
+ (segment (start 156.845 98.806) (end 158.115 100.076) (width 0.4318) (layer Cuivre) (net 39))
+ (segment (start 158.115 100.076) (end 161.925 100.076) (width 0.4318) (layer Cuivre) (net 39))
+ (segment (start 156.845 97.536) (end 156.845 98.806) (width 0.4318) (layer Cuivre) (net 39) (status 800))
+ (segment (start 95.885 102.616) (end 102.235 102.616) (width 0.4318) (layer Cuivre) (net 40) (status 400))
+ (segment (start 93.345 100.076) (end 95.885 102.616) (width 0.4318) (layer Cuivre) (net 40) (status 800))
+ (segment (start 137.795 81.026) (end 137.795 79.756) (width 0.4318) (layer Cuivre) (net 41) (status 800))
+ (segment (start 131.445 77.851) (end 130.175 79.121) (width 0.4318) (layer Cuivre) (net 41))
+ (segment (start 132.715 82.296) (end 132.08 82.296) (width 0.4318) (layer Cuivre) (net 41) (status 800))
+ (segment (start 132.08 82.296) (end 130.175 84.201) (width 0.4318) (layer Cuivre) (net 41))
+ (segment (start 130.175 79.121) (end 130.175 79.756) (width 0.4318) (layer Cuivre) (net 41))
+ (segment (start 130.175 79.756) (end 132.715 82.296) (width 0.4318) (layer Cuivre) (net 41) (status 400))
+ (segment (start 137.795 79.756) (end 135.89 77.851) (width 0.4318) (layer Cuivre) (net 41))
+ (segment (start 135.89 77.851) (end 131.445 77.851) (width 0.4318) (layer Cuivre) (net 41))
+ (segment (start 130.175 84.201) (end 130.175 87.376) (width 0.4318) (layer Cuivre) (net 41) (status 400))
+ (segment (start 126.365 97.536) (end 125.603 98.298) (width 0.4318) (layer Cuivre) (net 42))
+ (segment (start 125.603 98.298) (end 125.603 101.346) (width 0.4318) (layer Cuivre) (net 42) (status 400))
+ (segment (start 130.175 97.536) (end 126.365 97.536) (width 0.4318) (layer Cuivre) (net 42) (status 800))
+ (segment (start 118.491 97.028) (end 118.491 94.996) (width 0.4318) (layer Cuivre) (net 43) (status 400))
+ (segment (start 114.046 99.06) (end 114.935 99.06) (width 0.4318) (layer Cuivre) (net 43))
+ (segment (start 108.839 99.06) (end 114.046 99.06) (width 0.4318) (layer Cuivre) (net 43))
+ (segment (start 116.967 98.171) (end 115.824 98.171) (width 0.4318) (layer Cuivre) (net 43))
+ (segment (start 116.967 98.171) (end 117.348 98.171) (width 0.4318) (layer Cuivre) (net 43))
+ (segment (start 117.348 98.171) (end 118.491 97.028) (width 0.4318) (layer Cuivre) (net 43))
+ (segment (start 107.315 97.536) (end 108.839 99.06) (width 0.4318) (layer Cuivre) (net 43))
+ (segment (start 114.935 99.06) (end 115.824 98.171) (width 0.4318) (layer Cuivre) (net 43))
+ (segment (start 107.315 96.266) (end 107.315 97.536) (width 0.4318) (layer Cuivre) (net 43) (status 800))
+ (segment (start 115.824 98.171) (end 114.935 99.06) (width 0.4318) (layer Cuivre) (net 43))
+ (segment (start 105.283 93.726) (end 106.172 92.837) (width 0.4318) (layer Cuivre) (net 44))
+ (segment (start 106.172 92.837) (end 109.093 92.837) (width 0.4318) (layer Cuivre) (net 44))
+ (segment (start 105.283 96.647) (end 105.283 93.726) (width 0.4318) (layer Cuivre) (net 44))
+ (segment (start 95.885 97.536) (end 104.394 97.536) (width 0.4318) (layer Cuivre) (net 44) (status 800))
+ (segment (start 109.855 93.599) (end 109.855 96.266) (width 0.4318) (layer Cuivre) (net 44) (status 400))
+ (segment (start 109.093 92.837) (end 109.855 93.599) (width 0.4318) (layer Cuivre) (net 44))
+ (segment (start 104.394 97.536) (end 105.283 96.647) (width 0.4318) (layer Cuivre) (net 44))
+ (segment (start 141.605 91.821) (end 145.415 95.631) (width 0.4318) (layer Cuivre) (net 45))
+ (segment (start 141.605 89.916) (end 141.605 91.821) (width 0.4318) (layer Cuivre) (net 45) (status 800))
+ (segment (start 145.415 95.631) (end 145.415 97.536) (width 0.4318) (layer Cuivre) (net 45) (status 400))
+ (segment (start 113.665 85.471) (end 114.935 86.741) (width 0.4318) (layer Cuivre) (net 46))
+ (segment (start 114.935 86.741) (end 114.935 88.646) (width 0.4318) (layer Cuivre) (net 46) (status 400))
+ (segment (start 113.03 84.836) (end 111.252 86.614) (width 0.4318) (layer Cuivre) (net 46))
+ (segment (start 113.665 84.836) (end 113.665 85.471) (width 0.4318) (layer Cuivre) (net 46) (status 800))
+ (segment (start 111.252 86.614) (end 104.013 86.614) (width 0.4318) (layer Cuivre) (net 46))
+ (segment (start 113.665 84.836) (end 113.03 84.836) (width 0.4318) (layer Cuivre) (net 46) (status 800))
+ (segment (start 104.013 86.614) (end 102.235 84.836) (width 0.4318) (layer Cuivre) (net 46) (status 400))
+ (segment (start 134.53364 99.35464) (end 132.715 97.536) (width 0.4318) (layer Cuivre) (net 47) (status 400))
+ (segment (start 139.85494 99.35464) (end 134.53364 99.35464) (width 0.4318) (layer Cuivre) (net 47))
+ (segment (start 150.495 100.711) (end 150.495 101.346) (width 0.4318) (layer Cuivre) (net 47) (status 800))
+ (segment (start 144.145 101.346) (end 141.605 101.346) (width 0.4318) (layer Cuivre) (net 47) (status 400))
+ (segment (start 145.415 102.616) (end 144.145 101.346) (width 0.4318) (layer Cuivre) (net 47))
+ (segment (start 149.225 102.616) (end 145.415 102.616) (width 0.4318) (layer Cuivre) (net 47))
+ (segment (start 150.495 101.346) (end 149.225 102.616) (width 0.4318) (layer Cuivre) (net 47))
+ (segment (start 141.605 97.536) (end 141.605 101.346) (width 0.4318) (layer Cuivre) (net 47) (status C00))
+ (segment (start 141.67358 97.536) (end 139.85494 99.35464) (width 0.4318) (layer Cuivre) (net 47))
+ (segment (start 141.605 97.536) (end 141.67358 97.536) (width 0.4318) (layer Cuivre) (net 47) (status 800))
+ (segment (start 145.415 88.646) (end 144.78 88.011) (width 0.4318) (layer Cuivre) (net 48))
+ (segment (start 144.78 88.011) (end 143.51 88.011) (width 0.4318) (layer Cuivre) (net 48))
+ (segment (start 143.51 88.011) (end 139.7 84.201) (width 0.4318) (layer Cuivre) (net 48))
+ (segment (start 138.43 84.201) (end 138.37666 84.14766) (width 0.4318) (layer Cuivre) (net 48))
+ (segment (start 139.7 84.201) (end 138.43 84.201) (width 0.4318) (layer Cuivre) (net 48))
+ (segment (start 138.37666 84.14766) (end 135.94334 84.14766) (width 0.4318) (layer Cuivre) (net 48))
+ (segment (start 135.94334 84.14766) (end 132.715 87.376) (width 0.4318) (layer Cuivre) (net 48) (status 400))
+ (segment (start 145.415 89.916) (end 145.415 88.646) (width 0.4318) (layer Cuivre) (net 48) (status 800))
+ (segment (start 145.415 88.646) (end 149.225 88.646) (width 0.4318) (layer Cuivre) (net 48) (status 400))
+ (segment (start 137.795 87.122) (end 137.795 86.106) (width 0.4318) (layer Cuivre) (net 49) (status 400))
+ (segment (start 135.001 89.916) (end 137.795 87.122) (width 0.4318) (layer Cuivre) (net 49))
+ (segment (start 130.175 89.916) (end 135.001 89.916) (width 0.4318) (layer Cuivre) (net 49) (status 800))
+ (segment (start 139.7 64.516) (end 145.415 64.516) (width 0.6096) (layer Cuivre) (net 50) (status 400))
+ (segment (start 133.35 70.866) (end 139.7 64.516) (width 0.6096) (layer Cuivre) (net 50))
+ (segment (start 117.475 74.676) (end 125.73 74.676) (width 0.6096) (layer Cuivre) (net 50) (status 800))
+ (segment (start 126.365 74.041) (end 126.365 70.866) (width 0.6096) (layer Cuivre) (net 50) (status 400))
+ (segment (start 125.73 74.676) (end 126.365 74.041) (width 0.6096) (layer Cuivre) (net 50))
+ (segment (start 126.365 70.866) (end 133.35 70.866) (width 0.6096) (layer Cuivre) (net 50) (status 800))
+
+ (zone (net 12) (net_name GND) (layer Cuivre) (tstamp 4AE6D930) (hatch edge 0.508)
+ (connect_pads (clearance 0.508))
+ (min_thickness 0.254)
+ (fill (arc_segments 32) (thermal_gap 0.762) (thermal_bridge_width 0.508))
+ (polygon
+ (pts
+ (xy 187.325 131.572) (xy 187.325 53.086) (xy 88.392 53.086) (xy 88.392 131.572)
+ )
+ )
+ (filled_polygon
+ (pts
+ (xy 185.25998 131.2164) (xy 185.27268 131.2037) (xy 185.37682 131.13512) (xy 185.49112 131.08686) (xy 185.61304 131.064)
+ (xy 187.198 131.064) (xy 187.198 53.213) (xy 89.027 53.213) (xy 89.027 54.80558) (xy 89.0016 54.9275)
+ (xy 88.95334 55.0418) (xy 88.88222 55.1434) (xy 88.8746 55.15102) (xy 88.8746 128.49098) (xy 88.8873 128.50368)
+ (xy 88.95588 128.60782) (xy 89.00414 128.72212) (xy 89.027 128.84404) (xy 89.027 131.064) (xy 91.25458 131.064)
+ (xy 91.3765 131.0894) (xy 91.4908 131.13766) (xy 91.5924 131.20878) (xy 91.59748 131.2164) (xy 118.80342 131.2164)
+ (xy 118.80342 129.7305) (xy 118.46814 129.70764) (xy 118.14556 129.62128) (xy 117.8433 129.47396) (xy 117.78488 129.15646)
+ (xy 118.745 128.19634) (xy 119.70512 129.15646) (xy 119.6467 129.47396) (xy 119.45112 129.57556) (xy 119.13362 129.68478)
+ (xy 118.80342 129.7305) (xy 118.80342 131.2164) (xy 119.88546 131.2164) (xy 119.88546 128.97612) (xy 118.92534 128.016)
+ (xy 119.88546 127.05588) (xy 120.20296 127.1143) (xy 120.30456 127.30988) (xy 120.41378 127.62738) (xy 120.4595 127.95758)
+ (xy 120.43664 128.29286) (xy 120.35028 128.61544) (xy 120.20296 128.9177) (xy 119.88546 128.97612) (xy 119.88546 131.2164)
+ (xy 137.5283 131.2164) (xy 137.52322 131.21386) (xy 137.30732 131.06654) (xy 137.12698 130.87858) (xy 136.98728 130.6576)
+ (xy 136.89076 130.4163) (xy 136.8425 130.15976) (xy 136.84504 129.921) (xy 136.17448 129.25044) (xy 136.07034 129.1209)
+ (xy 136.0678 129.11582) (xy 136.06526 129.11328) (xy 136.04494 129.07772) (xy 135.9916 128.97612) (xy 135.94334 128.8161)
+ (xy 135.9281 128.651) (xy 135.9281 128.56464) (xy 135.82904 128.49606) (xy 135.63092 128.29032) (xy 135.47598 128.04902)
+ (xy 135.37184 127.78486) (xy 135.3185 127.50292) (xy 135.32358 127.2159) (xy 135.382 126.9365) (xy 135.4963 126.67488)
+ (xy 135.65632 126.43866) (xy 135.86206 126.238) (xy 136.10082 126.08306) (xy 136.36498 125.97638) (xy 136.64692 125.92304)
+ (xy 136.93394 125.92304) (xy 137.21334 125.98146) (xy 137.4775 126.09322) (xy 137.71372 126.25324) (xy 137.91438 126.45644)
+ (xy 138.07186 126.69266) (xy 138.18108 126.95936) (xy 138.23696 127.23876) (xy 138.23188 127.56642) (xy 138.16838 127.84328)
+ (xy 138.05154 128.1049) (xy 137.88644 128.33858) (xy 137.77468 128.44272) (xy 138.049 128.71704) (xy 138.3157 128.71704)
+ (xy 138.57224 128.77038) (xy 138.81354 128.87198) (xy 139.02944 129.0193) (xy 139.21232 129.20472) (xy 139.3571 129.42062)
+ (xy 139.45616 129.66192) (xy 139.50696 129.91846) (xy 139.50188 130.21564) (xy 139.44346 130.47218) (xy 139.33932 130.7084)
+ (xy 139.18692 130.92176) (xy 138.99896 131.1021) (xy 138.81608 131.2164) (xy 142.20444 131.2164) (xy 142.20444 130.91922)
+ (xy 141.90218 130.8735) (xy 141.81582 130.7084) (xy 141.71422 130.4163) (xy 141.67104 130.10896) (xy 141.68882 129.79908)
+ (xy 141.76756 129.49936) (xy 141.90218 129.2225) (xy 142.20444 129.17678) (xy 143.07566 130.048) (xy 142.20444 130.91922)
+ (xy 142.20444 131.2164) (xy 142.40002 131.2164) (xy 142.38478 131.09956) (xy 143.256 130.22834) (xy 143.256 129.86766)
+ (xy 142.38478 128.99644) (xy 142.4305 128.69418) (xy 142.5956 128.60782) (xy 142.8877 128.50622) (xy 143.19504 128.46304)
+ (xy 143.42872 128.47574) (xy 143.25092 128.29032) (xy 143.09598 128.04902) (xy 142.99184 127.78486) (xy 142.9385 127.50292)
+ (xy 142.94358 127.2159) (xy 142.95628 127.14224) (xy 140.716 124.9045) (xy 140.20292 124.9045) (xy 140.081 124.8791)
+ (xy 139.9667 124.83084) (xy 139.8651 124.75972) (xy 139.7762 124.67082) (xy 139.70762 124.56668) (xy 139.65936 124.45238)
+ (xy 139.65174 124.4219) (xy 138.78052 124.4219) (xy 138.39698 124.80798) (xy 138.39444 124.80798) (xy 138.39444 124.81052)
+ (xy 138.2649 124.91466) (xy 138.25982 124.91466) (xy 138.25728 124.91974) (xy 138.22172 124.93752) (xy 138.12012 124.9934)
+ (xy 137.9601 125.04166) (xy 137.795 125.0569) (xy 135.01116 125.0569) (xy 134.99592 125.07976) (xy 134.80796 125.2601)
+ (xy 134.58698 125.3998) (xy 134.34314 125.49378) (xy 134.0866 125.5395) (xy 133.82498 125.53442) (xy 133.57098 125.47854)
+ (xy 133.5659 125.476) (xy 133.5659 125.4887) (xy 133.54812 125.6538) (xy 133.52526 125.72238) (xy 133.5024 125.80366)
+ (xy 133.42366 125.95098) (xy 133.31698 126.07798) (xy 132.7785 126.61646) (xy 132.7785 127.51308) (xy 132.7531 127.635)
+ (xy 132.70484 127.7493) (xy 132.63372 127.8509) (xy 132.54482 127.9398) (xy 132.44068 128.00838) (xy 132.32638 128.05664)
+ (xy 132.20446 128.0795) (xy 130.67792 128.0795) (xy 130.556 128.0541) (xy 130.4417 128.00584) (xy 130.3401 127.93472)
+ (xy 130.2512 127.84582) (xy 130.18262 127.74168) (xy 130.13436 127.62738) (xy 130.1115 127.50546) (xy 130.1115 125.97892)
+ (xy 130.1369 125.857) (xy 130.18516 125.7427) (xy 130.25628 125.6411) (xy 130.34518 125.5522) (xy 130.44932 125.48362)
+ (xy 130.56362 125.43536) (xy 130.68554 125.4125) (xy 130.88112 125.40996) (xy 130.79222 125.37186) (xy 130.57632 125.22454)
+ (xy 130.46964 125.11278) (xy 127.80264 127.77978) (xy 127.82296 127.87376) (xy 127.81788 128.20142) (xy 127.75438 128.47828)
+ (xy 127.63754 128.7399) (xy 127.47244 128.97358) (xy 127.2667 129.1717) (xy 127.0254 129.3241) (xy 126.7587 129.42824)
+ (xy 126.47676 129.4765) (xy 126.18974 129.47142) (xy 125.91288 129.40792) (xy 125.64872 129.29362) (xy 125.41504 129.13106)
+ (xy 125.21692 128.92532) (xy 125.06198 128.68402) (xy 124.95784 128.41986) (xy 124.9045 128.13792) (xy 124.90958 127.8509)
+ (xy 124.968 127.5715) (xy 125.0823 127.30988) (xy 125.24232 127.07366) (xy 125.44806 126.873) (xy 125.68682 126.71806)
+ (xy 125.95098 126.61138) (xy 126.23292 126.55804) (xy 126.51994 126.55804) (xy 126.60122 126.57328) (xy 129.57302 123.60148)
+ (xy 129.58064 123.5964) (xy 129.71018 123.49226) (xy 129.79654 123.44654) (xy 129.84988 123.4186) (xy 129.85496 123.41606)
+ (xy 129.8575 123.41606) (xy 129.87274 123.41098) (xy 130.0099 123.37034) (xy 130.175 123.3551) (xy 130.41376 123.35256)
+ (xy 130.42138 123.34494) (xy 130.6068 123.16206) (xy 130.82524 123.01982) (xy 131.06908 122.9233) (xy 131.32562 122.87504)
+ (xy 131.5847 122.87504) (xy 131.84124 122.92838) (xy 131.8641 122.936) (xy 131.8641 121.5263) (xy 131.88188 121.3612)
+ (xy 131.90728 121.27992) (xy 131.9276 121.21134) (xy 132.00634 121.06402) (xy 132.11302 120.93702) (xy 133.03504 120.01246)
+ (xy 133.03758 119.9769) (xy 133.096 119.6975) (xy 133.2103 119.43588) (xy 133.35762 119.2149) (xy 131.92252 119.2149)
+ (xy 124.61748 126.52248) (xy 124.61494 126.52248) (xy 124.61494 126.52502) (xy 124.4854 126.62916) (xy 124.48032 126.62916)
+ (xy 124.47778 126.63424) (xy 124.44222 126.65202) (xy 124.34062 126.7079) (xy 124.1806 126.75616) (xy 124.0155 126.7714)
+ (xy 119.6848 126.7714) (xy 119.70512 126.87554) (xy 118.74246 127.83312) (xy 118.56466 128.016) (xy 117.60454 128.97612)
+ (xy 117.28704 128.9177) (xy 117.18544 128.72212) (xy 117.07622 128.40462) (xy 117.03812 128.1303) (xy 115.79098 129.37998)
+ (xy 115.78844 129.37998) (xy 115.78844 129.38252) (xy 115.6589 129.48666) (xy 115.65382 129.48666) (xy 115.65128 129.49174)
+ (xy 115.61572 129.50952) (xy 115.51412 129.5654) (xy 115.3541 129.61366) (xy 115.189 129.6289) (xy 106.5403 129.6289)
+ (xy 106.3752 129.61112) (xy 106.22788 129.56286) (xy 106.22534 129.56286) (xy 106.2228 129.56032) (xy 106.21772 129.56032)
+ (xy 106.1593 129.5273) (xy 106.07802 129.48412) (xy 105.95102 129.37998) (xy 104.42448 127.85344) (xy 104.32034 127.7239)
+ (xy 104.3178 127.71882) (xy 104.31526 127.71628) (xy 104.29494 127.68072) (xy 104.2416 127.57912) (xy 104.19334 127.4191)
+ (xy 104.18826 127.36576) (xy 104.14254 127.4699) (xy 103.97744 127.70358) (xy 103.7717 127.9017) (xy 103.5304 128.0541)
+ (xy 103.2637 128.15824) (xy 102.98176 128.2065) (xy 102.69474 128.20142) (xy 102.63124 128.18618) (xy 102.20198 128.61798)
+ (xy 102.19944 128.61798) (xy 102.19944 128.62052) (xy 102.0699 128.72466) (xy 102.06482 128.72466) (xy 102.06228 128.72974)
+ (xy 102.02672 128.74752) (xy 101.92512 128.8034) (xy 101.7651 128.85166) (xy 101.6 128.8669) (xy 95.8723 128.8669)
+ (xy 95.7072 128.84912) (xy 95.63608 128.82626) (xy 95.55734 128.8034) (xy 95.41002 128.72212) (xy 95.28302 128.61798)
+ (xy 93.5228 126.85776) (xy 93.45168 126.873) (xy 93.1799 126.86792) (xy 92.9132 126.8095) (xy 92.66174 126.70028)
+ (xy 92.43822 126.5428) (xy 92.24772 126.34722) (xy 92.09786 126.11608) (xy 91.9988 125.86208) (xy 91.948 125.59284)
+ (xy 91.95308 125.32106) (xy 92.00896 125.05182) (xy 92.11818 124.80036) (xy 92.27058 124.57684) (xy 92.46616 124.3838)
+ (xy 92.69476 124.23394) (xy 92.94876 124.13234) (xy 93.15958 124.0917) (xy 93.15958 117.86362) (xy 92.79636 117.74932)
+ (xy 92.63126 117.44198) (xy 92.50426 117.04066) (xy 92.456 116.62156) (xy 92.48902 116.20246) (xy 92.60586 115.7986)
+ (xy 92.79636 115.42268) (xy 93.15958 115.30838) (xy 94.43466 116.586) (xy 93.15958 117.86362) (xy 93.15958 124.0917)
+ (xy 93.218 124.08154) (xy 93.48978 124.08154) (xy 93.75902 124.13742) (xy 94.01048 124.2441) (xy 94.23908 124.3965)
+ (xy 94.42958 124.58954) (xy 94.58198 124.81814) (xy 94.68612 125.0696) (xy 94.73946 125.33884) (xy 94.73438 125.65126)
+ (xy 94.73184 125.65888) (xy 96.23552 127.1651) (xy 101.24694 127.1651) (xy 101.42982 126.97968) (xy 101.4095 126.86792)
+ (xy 101.41458 126.5809) (xy 101.473 126.3015) (xy 101.5873 126.03988) (xy 101.74732 125.80366) (xy 101.95306 125.603)
+ (xy 102.19182 125.44806) (xy 102.45598 125.34138) (xy 102.73792 125.28804) (xy 103.02494 125.28804) (xy 103.30434 125.34646)
+ (xy 103.5685 125.45822) (xy 103.80472 125.61824) (xy 104.00538 125.82144) (xy 104.16286 126.05766) (xy 104.1781 126.09322)
+ (xy 104.1781 125.57506) (xy 103.53294 124.92736) (xy 101.44252 124.92736) (xy 100.29698 126.07798) (xy 100.29444 126.07798)
+ (xy 100.29444 126.08052) (xy 100.1649 126.18466) (xy 100.15982 126.18466) (xy 100.15728 126.18974) (xy 100.12172 126.20752)
+ (xy 100.02012 126.2634) (xy 99.8601 126.31166) (xy 99.695 126.3269) (xy 99.5299 126.3269) (xy 99.48672 126.3904)
+ (xy 99.2886 126.5809) (xy 99.05746 126.72568) (xy 98.80346 126.82474) (xy 98.53168 126.873) (xy 98.2599 126.86792)
+ (xy 97.9932 126.8095) (xy 97.74174 126.70028) (xy 97.51822 126.5428) (xy 97.32772 126.34722) (xy 97.17786 126.11608)
+ (xy 97.0788 125.86208) (xy 97.028 125.59284) (xy 97.03308 125.32106) (xy 97.08896 125.05182) (xy 97.19818 124.80036)
+ (xy 97.35058 124.57684) (xy 97.54616 124.3838) (xy 97.77476 124.23394) (xy 98.02876 124.13234) (xy 98.298 124.08154)
+ (xy 98.56978 124.08154) (xy 98.83902 124.13742) (xy 99.09048 124.2441) (xy 99.31908 124.3965) (xy 99.44354 124.52096)
+ (xy 100.18014 123.7869) (xy 96.9899 123.7869) (xy 96.94672 123.8504) (xy 96.7486 124.0409) (xy 96.51746 124.18568)
+ (xy 96.26346 124.28474) (xy 95.99168 124.333) (xy 95.7199 124.32792) (xy 95.4532 124.2695) (xy 95.20174 124.16028)
+ (xy 94.97822 124.0028) (xy 94.78772 123.80722) (xy 94.63786 123.57608) (xy 94.5388 123.32208) (xy 94.488 123.05284)
+ (xy 94.49308 122.78106) (xy 94.54896 122.51182) (xy 94.65056 122.2756) (xy 94.65056 118.745) (xy 94.23146 118.71198)
+ (xy 93.8276 118.59514) (xy 93.45168 118.40464) (xy 93.33738 118.04142) (xy 94.615 116.76634) (xy 95.89262 118.04142)
+ (xy 95.77832 118.40464) (xy 95.47098 118.56974) (xy 95.06966 118.69674) (xy 94.65056 118.745) (xy 94.65056 122.2756)
+ (xy 94.65818 122.26036) (xy 94.81058 122.03684) (xy 95.00616 121.8438) (xy 95.23476 121.69394) (xy 95.48876 121.59234)
+ (xy 95.758 121.54154) (xy 96.02978 121.54154) (xy 96.29902 121.59742) (xy 96.55048 121.7041) (xy 96.77908 121.8565)
+ (xy 96.96958 122.04954) (xy 96.99244 122.0851) (xy 101.72954 122.08256) (xy 101.72954 120.08612) (xy 101.41204 120.0277)
+ (xy 101.31044 119.83212) (xy 101.20122 119.51462) (xy 101.1555 119.18442) (xy 101.17836 118.84914) (xy 101.26472 118.52656)
+ (xy 101.41204 118.2243) (xy 101.72954 118.16588) (xy 102.68966 119.126) (xy 101.72954 120.08612) (xy 101.72954 122.08256)
+ (xy 101.75494 122.08256) (xy 102.89286 120.94718) (xy 102.90556 120.93448) (xy 102.9081 120.9294) (xy 102.91064 120.9294)
+ (xy 103.04018 120.82526) (xy 103.04018 120.82272) (xy 102.92842 120.8405) (xy 102.59314 120.81764) (xy 102.27056 120.73128)
+ (xy 101.9683 120.58396) (xy 101.90988 120.26646) (xy 102.87 119.30634) (xy 103.83012 120.26646) (xy 103.7717 120.58396)
+ (xy 103.57612 120.68556) (xy 103.5685 120.6881) (xy 104.01046 120.6881) (xy 104.01046 120.08612) (xy 103.05034 119.126)
+ (xy 104.01046 118.16588) (xy 104.32796 118.2243) (xy 104.42956 118.41988) (xy 104.53878 118.73738) (xy 104.5845 119.06758)
+ (xy 104.56164 119.40286) (xy 104.47528 119.72544) (xy 104.32796 120.0277) (xy 104.01046 120.08612) (xy 104.01046 120.6881)
+ (xy 109.1057 120.6881) (xy 109.2708 120.70588) (xy 109.347 120.73128) (xy 109.42066 120.7516) (xy 109.56798 120.83034)
+ (xy 109.69498 120.93702) (xy 110.4646 121.70664) (xy 110.56874 121.83618) (xy 110.61192 121.92254) (xy 110.6424 121.97588)
+ (xy 110.6424 121.98096) (xy 110.64494 121.9835) (xy 110.64748 121.99874) (xy 110.69066 122.1359) (xy 110.7059 122.301)
+ (xy 110.7059 122.7074) (xy 110.75924 122.7455) (xy 110.95736 122.94362) (xy 111.10976 123.17476) (xy 111.125 123.21032)
+ (xy 111.14786 123.15952) (xy 111.30534 122.93092) (xy 111.50346 122.73534) (xy 111.5441 122.7074) (xy 111.5441 118.08206)
+ (xy 111.49076 118.0465) (xy 111.29264 117.84838) (xy 111.14024 117.61724) (xy 111.12246 117.57914) (xy 111.10214 117.63248)
+ (xy 110.94466 117.86108) (xy 110.74654 118.05666) (xy 110.5154 118.20906) (xy 110.25632 118.3132) (xy 109.98454 118.364)
+ (xy 109.70768 118.364) (xy 109.4359 118.30812) (xy 109.17936 118.20144) (xy 108.95076 118.0465) (xy 108.75264 117.84838)
+ (xy 108.60024 117.61724) (xy 108.58246 117.57914) (xy 108.56214 117.63248) (xy 108.40466 117.86108) (xy 108.20654 118.05666)
+ (xy 107.9754 118.20906) (xy 107.71632 118.3132) (xy 107.44454 118.364) (xy 107.16768 118.364) (xy 106.8959 118.30812)
+ (xy 106.63936 118.20144) (xy 106.41076 118.0465) (xy 106.21264 117.84838) (xy 106.06024 117.61724) (xy 106.02214 117.5258)
+ (xy 103.4796 117.5258) (xy 103.7717 117.66804) (xy 103.83012 117.98554) (xy 102.87 118.94566) (xy 101.90988 117.98554)
+ (xy 101.9683 117.66804) (xy 102.16388 117.56644) (xy 102.28072 117.52326) (xy 97.52838 117.5258) (xy 97.46742 117.51818)
+ (xy 97.35566 117.50802) (xy 97.34804 117.50548) (xy 97.3455 117.50548) (xy 97.17024 117.4496) (xy 97.05086 117.38356)
+ (xy 97.01784 117.36578) (xy 97.01276 117.3607) (xy 97.01022 117.3607) (xy 96.9772 117.33276) (xy 96.8756 117.25148)
+ (xy 96.70542 117.08384) (xy 96.62414 117.3734) (xy 96.43364 117.74932) (xy 96.07042 117.86362) (xy 94.79534 116.586)
+ (xy 94.615 116.40566) (xy 93.33738 115.13058) (xy 93.45168 114.76736) (xy 93.6244 114.67338) (xy 92.63634 114.67338)
+ (xy 92.45346 114.65306) (xy 92.2909 114.59972) (xy 92.28582 114.59972) (xy 92.28328 114.59718) (xy 92.2782 114.59718)
+ (xy 92.1258 114.51336) (xy 92.12072 114.50828) (xy 92.11818 114.50828) (xy 92.0369 114.4397) (xy 91.98356 114.39652)
+ (xy 91.00312 113.41862) (xy 91.00058 113.41608) (xy 90.88628 113.27384) (xy 90.88374 113.26876) (xy 90.8812 113.26622)
+ (xy 90.81516 113.13922) (xy 90.79992 113.11128) (xy 90.79738 113.10874) (xy 90.79484 113.10112) (xy 90.77452 113.03)
+ (xy 90.74658 112.93602) (xy 90.7288 112.75314) (xy 90.7288 81.83372) (xy 90.72626 81.81086) (xy 90.74658 81.62798)
+ (xy 90.74658 81.62036) (xy 90.74912 81.61782) (xy 90.75928 81.57718) (xy 90.79992 81.45272) (xy 90.80246 81.44256)
+ (xy 90.805 81.44256) (xy 90.81008 81.42732) (xy 90.88628 81.29016) (xy 91.00312 81.14792) (xy 94.42958 77.72146)
+ (xy 94.42958 75.95362) (xy 94.06636 75.83932) (xy 93.90126 75.53198) (xy 93.77426 75.13066) (xy 93.726 74.71156)
+ (xy 93.75902 74.29246) (xy 93.87586 73.8886) (xy 94.06636 73.51268) (xy 94.42958 73.39838) (xy 94.54642 73.51522)
+ (xy 94.54642 71.501) (xy 94.4245 71.4756) (xy 94.3102 71.42734) (xy 94.2086 71.35622) (xy 94.1197 71.26732)
+ (xy 94.05112 71.16318) (xy 94.00286 71.04888) (xy 93.98 70.92696) (xy 93.98 68.25742) (xy 94.0054 68.1355)
+ (xy 94.05366 68.0212) (xy 94.12478 67.9196) (xy 94.21368 67.8307) (xy 94.31782 67.76212) (xy 94.42958 67.71386)
+ (xy 94.42958 63.25362) (xy 94.06636 63.13932) (xy 93.90126 62.83198) (xy 93.77426 62.43066) (xy 93.726 62.01156)
+ (xy 93.75902 61.59246) (xy 93.87586 61.1886) (xy 94.06636 60.81268) (xy 94.42958 60.69838) (xy 95.70466 61.976)
+ (xy 94.42958 63.25362) (xy 94.42958 67.71386) (xy 94.43212 67.71386) (xy 94.55404 67.691) (xy 95.92056 67.691)
+ (xy 95.92056 64.135) (xy 95.50146 64.10198) (xy 95.0976 63.98514) (xy 94.72168 63.79464) (xy 94.60738 63.43142)
+ (xy 95.885 62.15634) (xy 95.885 61.79566) (xy 94.60738 60.52058) (xy 94.72168 60.15736) (xy 95.02902 59.99226)
+ (xy 95.43034 59.86526) (xy 95.84944 59.817) (xy 96.26854 59.85002) (xy 96.6724 59.96686) (xy 97.04832 60.15736)
+ (xy 97.16262 60.52058) (xy 95.885 61.79566) (xy 95.885 62.15634) (xy 97.16262 63.43142) (xy 97.04832 63.79464)
+ (xy 96.74098 63.95974) (xy 96.33966 64.08674) (xy 95.92056 64.135) (xy 95.92056 67.691) (xy 97.22358 67.691)
+ (xy 97.34042 67.71386) (xy 97.34042 63.25362) (xy 96.06534 61.976) (xy 97.34042 60.69838) (xy 97.70364 60.81268)
+ (xy 97.86874 61.12002) (xy 97.99574 61.52134) (xy 98.044 61.94044) (xy 98.01098 62.35954) (xy 97.89414 62.7634)
+ (xy 97.70364 63.13932) (xy 97.34042 63.25362) (xy 97.34042 67.71386) (xy 97.3455 67.7164) (xy 97.4598 67.76466)
+ (xy 97.5614 67.83578) (xy 97.6503 67.92468) (xy 97.71888 68.02882) (xy 97.76714 68.14312) (xy 97.76714 68.15328)
+ (xy 98.26244 67.66052) (xy 98.26244 67.65798) (xy 98.26498 67.65798) (xy 98.40722 67.54368) (xy 98.40976 67.54114)
+ (xy 98.41484 67.5386) (xy 98.5393 67.47256) (xy 98.56978 67.45732) (xy 98.56978 67.45478) (xy 98.57994 67.45224)
+ (xy 98.64852 67.43192) (xy 98.74504 67.40398) (xy 98.92792 67.3862) (xy 101.04882 67.3862) (xy 101.04882 61.71184)
+ (xy 100.7237 61.6458) (xy 100.61194 61.43244) (xy 100.50272 61.10224) (xy 100.45954 60.75934) (xy 100.4824 60.41136)
+ (xy 100.5713 60.07862) (xy 100.7237 59.7662) (xy 101.04882 59.70016) (xy 102.05466 60.706) (xy 101.04882 61.71184)
+ (xy 101.04882 67.3862) (xy 102.1461 67.3862) (xy 102.23246 67.2592) (xy 102.28834 67.20332) (xy 102.28834 62.48146)
+ (xy 101.94036 62.4586) (xy 101.60762 62.3697) (xy 101.2952 62.2173) (xy 101.22916 61.89218) (xy 102.235 60.88634)
+ (xy 102.235 60.52566) (xy 101.22916 59.51982) (xy 101.2952 59.1947) (xy 101.50856 59.08294) (xy 101.83876 58.97372)
+ (xy 102.18166 58.93054) (xy 102.52964 58.9534) (xy 102.86238 59.0423) (xy 103.1748 59.1947) (xy 103.24084 59.51982)
+ (xy 102.235 60.52566) (xy 102.235 60.88634) (xy 103.24084 61.89218) (xy 103.1748 62.2173) (xy 102.96144 62.32906)
+ (xy 102.63124 62.43828) (xy 102.28834 62.48146) (xy 102.28834 67.20332) (xy 102.46614 67.03314) (xy 102.73538 66.85534)
+ (xy 103.0351 66.73596) (xy 103.35514 66.675) (xy 103.42118 66.675) (xy 103.42118 61.71184) (xy 102.41534 60.706)
+ (xy 103.42118 59.70016) (xy 103.7463 59.7662) (xy 103.85806 59.97956) (xy 103.96728 60.30976) (xy 104.01046 60.65266)
+ (xy 103.9876 61.00064) (xy 103.8987 61.33338) (xy 103.7463 61.6458) (xy 103.42118 61.71184) (xy 103.42118 66.675)
+ (xy 103.67772 66.675) (xy 103.99522 66.74104) (xy 104.2924 66.8655) (xy 104.56164 67.04838) (xy 104.79024 67.27698)
+ (xy 104.97058 67.54622) (xy 105.0925 67.84594) (xy 105.156 68.16344) (xy 105.15092 68.53428) (xy 105.0798 68.84924)
+ (xy 104.94772 69.14388) (xy 104.7623 69.41058) (xy 104.52608 69.6341) (xy 104.2543 69.80682) (xy 103.95204 69.92366)
+ (xy 103.632 69.97954) (xy 103.30942 69.97446) (xy 102.99192 69.90334) (xy 102.69728 69.7738) (xy 102.43058 69.59092)
+ (xy 102.20452 69.35724) (xy 102.14356 69.2658) (xy 99.31908 69.2658) (xy 98.3234 70.26148) (xy 98.32086 70.26148)
+ (xy 98.32086 70.26402) (xy 98.17862 70.37832) (xy 98.17354 70.37832) (xy 98.171 70.3834) (xy 98.044 70.44944)
+ (xy 98.01606 70.46468) (xy 98.01352 70.46468) (xy 98.0059 70.46976) (xy 97.93732 70.48754) (xy 97.8408 70.51802)
+ (xy 97.79 70.52056) (xy 97.79 70.93458) (xy 97.7646 71.0565) (xy 97.71634 71.1708) (xy 97.64522 71.2724)
+ (xy 97.55632 71.3613) (xy 97.45218 71.42988) (xy 97.33788 71.47814) (xy 97.21596 71.501) (xy 94.54642 71.501)
+ (xy 94.54642 73.51522) (xy 95.70466 74.676) (xy 94.54642 75.83424) (xy 94.42958 75.95362) (xy 94.42958 77.72146)
+ (xy 94.58452 77.56652) (xy 94.58452 77.56398) (xy 94.58706 77.56398) (xy 94.7293 77.44968) (xy 94.73184 77.44714)
+ (xy 94.73692 77.4446) (xy 94.86138 77.37856) (xy 94.89186 77.36332) (xy 94.89186 77.36078) (xy 94.90202 77.35824)
+ (xy 94.9706 77.33792) (xy 95.06712 77.30998) (xy 95.25 77.2922) (xy 95.92056 77.2922) (xy 95.92056 76.835)
+ (xy 95.50146 76.80198) (xy 95.0976 76.68514) (xy 94.72168 76.49464) (xy 94.60738 76.13142) (xy 95.885 74.85634)
+ (xy 95.885 74.49566) (xy 94.60738 73.22058) (xy 94.72168 72.85736) (xy 95.02902 72.69226) (xy 95.43034 72.56526)
+ (xy 95.84944 72.517) (xy 96.26854 72.55002) (xy 96.6724 72.66686) (xy 97.04832 72.85736) (xy 97.16262 73.22058)
+ (xy 95.885 74.49566) (xy 95.885 74.85634) (xy 97.16262 76.13142) (xy 97.04832 76.49464) (xy 96.74098 76.65974)
+ (xy 96.33966 76.78674) (xy 95.92056 76.835) (xy 95.92056 77.2922) (xy 97.34042 77.2922) (xy 97.34042 75.95362)
+ (xy 96.06534 74.676) (xy 97.34042 73.39838) (xy 97.70364 73.51268) (xy 97.86874 73.82002) (xy 97.99574 74.22134)
+ (xy 98.044 74.64044) (xy 98.01098 75.05954) (xy 97.89414 75.4634) (xy 97.70364 75.83932) (xy 97.34042 75.95362)
+ (xy 97.34042 77.2922) (xy 99.17684 77.2922) (xy 100.17252 76.29652) (xy 100.17252 76.29398) (xy 100.17506 76.29398)
+ (xy 100.3173 76.17968) (xy 100.31984 76.17714) (xy 100.32492 76.1746) (xy 100.44938 76.10856) (xy 100.47986 76.09332)
+ (xy 100.47986 76.09078) (xy 100.49002 76.08824) (xy 100.5586 76.06792) (xy 100.65512 76.03998) (xy 100.838 76.0222)
+ (xy 100.9142 76.0222) (xy 100.96754 76.0222) (xy 100.9142 75.96886) (xy 100.838 75.89266) (xy 100.91166 75.81646)
+ (xy 100.5713 75.72756) (xy 100.42906 75.46848) (xy 100.31222 75.10272) (xy 100.26396 74.72172) (xy 100.29444 74.33564)
+ (xy 100.39858 73.96734) (xy 100.5713 73.62444) (xy 100.9142 73.533) (xy 102.05466 74.676) (xy 102.235 74.85634)
+ (xy 102.235 74.49566) (xy 101.092 73.3552) (xy 101.18344 73.0123) (xy 101.44252 72.87006) (xy 101.80828 72.75322)
+ (xy 102.18928 72.70496) (xy 102.57536 72.73544) (xy 102.94366 72.83958) (xy 103.28656 73.0123) (xy 103.378 73.3552)
+ (xy 102.235 74.49566) (xy 102.235 74.85634) (xy 102.41534 74.676) (xy 103.5558 73.533) (xy 103.8987 73.62444)
+ (xy 104.04094 73.88352) (xy 104.15778 74.24928) (xy 104.20604 74.63028) (xy 104.17556 75.01636) (xy 104.07142 75.38466)
+ (xy 103.8987 75.72756) (xy 103.5558 75.81646) (xy 103.632 75.89266) (xy 103.5558 75.96886) (xy 103.50246 76.0222)
+ (xy 103.5558 76.0222) (xy 108.88726 76.0222) (xy 114.97056 69.9389) (xy 114.8842 69.90334) (xy 114.7826 69.83222)
+ (xy 114.6937 69.74332) (xy 114.62512 69.63918) (xy 114.57686 69.52488) (xy 114.554 69.40296) (xy 114.554 67.24142)
+ (xy 114.5794 67.1195) (xy 114.62766 67.0052) (xy 114.69878 66.9036) (xy 114.78768 66.8147) (xy 114.89182 66.74612)
+ (xy 115.00612 66.69786) (xy 115.12804 66.675) (xy 117.28958 66.675) (xy 117.4115 66.7004) (xy 117.5258 66.74866)
+ (xy 117.6274 66.81978) (xy 117.7163 66.90868) (xy 117.78488 67.01282) (xy 117.8179 67.09156) (xy 121.82856 63.0809)
+ (xy 121.9708 62.9666) (xy 121.97334 62.96406) (xy 121.97842 62.96152) (xy 122.0216 62.93866) (xy 122.13336 62.88024)
+ (xy 122.30862 62.8269) (xy 122.4915 62.80912) (xy 125.0442 62.80912) (xy 125.0442 61.849) (xy 124.7013 61.75756)
+ (xy 124.55906 61.49848) (xy 124.44222 61.13272) (xy 124.39396 60.75172) (xy 124.42444 60.36564) (xy 124.52858 59.99734)
+ (xy 124.7013 59.65444) (xy 125.0442 59.563) (xy 126.18466 60.706) (xy 125.0442 61.849) (xy 125.0442 62.80912)
+ (xy 126.41072 62.80912) (xy 126.41072 62.67704) (xy 126.02464 62.64656) (xy 125.65634 62.54242) (xy 125.31344 62.3697)
+ (xy 125.222 62.0268) (xy 126.365 60.88634) (xy 126.365 60.52566) (xy 125.222 59.3852) (xy 125.31344 59.0423)
+ (xy 125.57252 58.90006) (xy 125.93828 58.78322) (xy 126.31928 58.73496) (xy 126.70536 58.76544) (xy 127.07366 58.86958)
+ (xy 127.41656 59.0423) (xy 127.508 59.3852) (xy 126.365 60.52566) (xy 126.365 60.88634) (xy 127.508 62.0268)
+ (xy 127.41656 62.3697) (xy 127.15748 62.51194) (xy 126.79172 62.62878) (xy 126.41072 62.67704) (xy 126.41072 62.80912)
+ (xy 127.6858 62.80912) (xy 127.6858 61.849) (xy 126.54534 60.706) (xy 127.6858 59.563) (xy 128.0287 59.65444)
+ (xy 128.17094 59.91352) (xy 128.28778 60.27928) (xy 128.33604 60.66028) (xy 128.30556 61.04636) (xy 128.20142 61.41466)
+ (xy 128.0287 61.75756) (xy 127.6858 61.849) (xy 127.6858 62.80912) (xy 136.398 62.80912) (xy 139.16914 60.03798)
+ (xy 139.31138 59.92368) (xy 139.31392 59.92114) (xy 139.319 59.9186) (xy 139.36218 59.89574) (xy 139.47394 59.83732)
+ (xy 139.6492 59.78398) (xy 139.83208 59.7662) (xy 139.8905 59.7662) (xy 139.8905 59.55792) (xy 139.9159 59.436)
+ (xy 139.96416 59.3217) (xy 140.03528 59.2201) (xy 140.12418 59.1312) (xy 140.22832 59.06262) (xy 140.34262 59.01436)
+ (xy 140.46454 58.9915) (xy 142.75308 58.9915) (xy 142.875 59.0169) (xy 142.9893 59.06516) (xy 143.0909 59.13628)
+ (xy 143.1798 59.22518) (xy 143.24838 59.32932) (xy 143.29664 59.44362) (xy 143.3195 59.56554) (xy 143.3195 61.85408)
+ (xy 143.2941 61.976) (xy 143.24584 62.0903) (xy 143.17472 62.1919) (xy 143.08582 62.2808) (xy 142.98168 62.34938)
+ (xy 142.86738 62.39764) (xy 142.74546 62.4205) (xy 140.45692 62.4205) (xy 140.335 62.3951) (xy 140.2207 62.34684)
+ (xy 140.1191 62.27572) (xy 140.0302 62.18682) (xy 139.96162 62.08268) (xy 139.91336 61.96838) (xy 139.91082 61.95568)
+ (xy 137.45464 64.4144) (xy 137.4521 64.4144) (xy 137.4521 64.41694) (xy 137.30986 64.53124) (xy 137.30478 64.53124)
+ (xy 137.30224 64.53632) (xy 137.17524 64.59982) (xy 137.1473 64.6176) (xy 137.14476 64.6176) (xy 137.13714 64.62268)
+ (xy 137.06602 64.64046) (xy 136.97204 64.67094) (xy 136.78916 64.68872) (xy 124.7648 64.68872) (xy 124.7648 67.38366)
+ (xy 124.86132 67.48272) (xy 125.0061 67.69862) (xy 125.10516 67.93992) (xy 125.15596 68.19646) (xy 125.15088 68.49364)
+ (xy 125.09246 68.75018) (xy 124.98832 68.9864) (xy 124.83592 69.19976) (xy 124.69622 69.33184) (xy 124.82068 69.38518)
+ (xy 124.96546 69.4817) (xy 125.08738 69.60616) (xy 125.1839 69.75094) (xy 125.24994 69.91096) (xy 125.28296 70.08114)
+ (xy 125.28296 70.08876) (xy 125.34138 70.00494) (xy 125.5268 69.82206) (xy 125.74524 69.67982) (xy 125.98908 69.5833)
+ (xy 126.24562 69.53504) (xy 126.5047 69.53504) (xy 126.76124 69.58838) (xy 127.00254 69.68998) (xy 127.21844 69.8373)
+ (xy 127.3048 69.9262) (xy 132.95884 69.9262) (xy 139.03706 63.84798) (xy 139.1793 63.73368) (xy 139.18184 63.73114)
+ (xy 139.18692 63.7286) (xy 139.25042 63.69304) (xy 139.34694 63.64478) (xy 139.34948 63.64478) (xy 139.35964 63.6397)
+ (xy 139.51712 63.59398) (xy 139.7 63.5762) (xy 144.1196 63.5762) (xy 144.16786 63.46952) (xy 144.32534 63.24092)
+ (xy 144.52346 63.04534) (xy 144.7546 62.89294) (xy 145.01368 62.7888) (xy 145.28546 62.738) (xy 145.56232 62.738)
+ (xy 145.8341 62.79388) (xy 146.09064 62.90056) (xy 146.31924 63.0555) (xy 146.51736 63.25362) (xy 146.66976 63.48476)
+ (xy 146.685 63.52032) (xy 146.70786 63.46952) (xy 146.86534 63.24092) (xy 147.06346 63.04534) (xy 147.2946 62.89294)
+ (xy 147.55368 62.7888) (xy 147.82546 62.738) (xy 148.10232 62.738) (xy 148.3741 62.79388) (xy 148.63064 62.90056)
+ (xy 148.85924 63.0555) (xy 149.05736 63.25362) (xy 149.20976 63.48476) (xy 149.225 63.52032) (xy 149.24786 63.46952)
+ (xy 149.40534 63.24092) (xy 149.60346 63.04534) (xy 149.8346 62.89294) (xy 150.09368 62.7888) (xy 150.36546 62.738)
+ (xy 150.64232 62.738) (xy 150.9141 62.79388) (xy 151.17064 62.90056) (xy 151.39924 63.0555) (xy 151.59736 63.25362)
+ (xy 151.74976 63.48476) (xy 151.765 63.52032) (xy 151.78786 63.46952) (xy 151.94534 63.24092) (xy 152.14346 63.04534)
+ (xy 152.3746 62.89294) (xy 152.63368 62.7888) (xy 152.90546 62.738) (xy 153.18232 62.738) (xy 153.4541 62.79388)
+ (xy 153.71064 62.90056) (xy 153.93924 63.0555) (xy 154.13736 63.25362) (xy 154.28976 63.48476) (xy 154.32532 63.5762)
+ (xy 158.35884 63.5762) (xy 158.6865 63.246) (xy 158.69158 63.09614) (xy 158.74492 62.8396) (xy 158.84906 62.60084)
+ (xy 158.99638 62.38494) (xy 159.1818 62.20206) (xy 159.40024 62.05982) (xy 159.64408 61.9633) (xy 159.90062 61.91504)
+ (xy 160.1597 61.91504) (xy 160.41624 61.96838) (xy 160.65754 62.06998) (xy 160.87344 62.2173) (xy 161.05632 62.40272)
+ (xy 161.2011 62.61862) (xy 161.30016 62.85992) (xy 161.35096 63.11646) (xy 161.34588 63.41364) (xy 161.28746 63.67018)
+ (xy 161.18332 63.9064) (xy 161.03092 64.11976) (xy 160.84296 64.3001) (xy 160.62198 64.4398) (xy 160.37814 64.53378)
+ (xy 160.1216 64.5795) (xy 160.01746 64.57696) (xy 159.41548 65.18148) (xy 159.41294 65.18148) (xy 159.41294 65.18402)
+ (xy 159.2707 65.29832) (xy 159.26562 65.29832) (xy 159.26308 65.3034) (xy 159.13608 65.3669) (xy 159.10814 65.38468)
+ (xy 159.1056 65.38468) (xy 159.09798 65.38976) (xy 159.0294 65.40754) (xy 158.93288 65.43802) (xy 158.75 65.4558)
+ (xy 154.32786 65.4558) (xy 154.28214 65.56248) (xy 154.12466 65.79108) (xy 153.9748 65.9384) (xy 153.9748 66.4337)
+ (xy 153.95448 66.61658) (xy 153.90114 66.7766) (xy 153.90114 66.78422) (xy 153.8986 66.78422) (xy 153.8986 66.79184)
+ (xy 153.84272 66.88836) (xy 153.81478 66.94424) (xy 153.8097 66.94678) (xy 153.8097 66.95186) (xy 153.74112 67.0306)
+ (xy 153.69794 67.08648) (xy 153.06548 67.72148) (xy 153.06294 67.72148) (xy 153.06294 67.72402) (xy 152.9207 67.83832)
+ (xy 152.91562 67.83832) (xy 152.91308 67.8434) (xy 152.78608 67.9069) (xy 152.75814 67.92468) (xy 152.7556 67.92468)
+ (xy 152.74798 67.92976) (xy 152.67686 67.94754) (xy 152.58288 67.97802) (xy 152.4 67.9958) (xy 141.35862 67.9958)
+ (xy 132.35432 77.0001) (xy 135.9027 77.0001) (xy 136.0678 77.01788) (xy 136.14654 77.04328) (xy 136.21766 77.0636)
+ (xy 136.36498 77.14234) (xy 136.49198 77.24902) (xy 138.38428 79.14386) (xy 138.39952 79.15656) (xy 138.4046 79.16164)
+ (xy 138.50874 79.29118) (xy 138.55192 79.37754) (xy 138.5824 79.43088) (xy 138.5824 79.43596) (xy 138.58494 79.4385)
+ (xy 138.58494 79.44612) (xy 138.63066 79.5909) (xy 138.6459 79.756) (xy 138.6459 79.83982) (xy 138.72972 79.89824)
+ (xy 138.93038 80.10144) (xy 138.93292 80.10398) (xy 138.93292 78.5495) (xy 138.811 78.5241) (xy 138.6967 78.47584)
+ (xy 138.5951 78.40472) (xy 138.5062 78.31582) (xy 138.43762 78.21168) (xy 138.38936 78.09738) (xy 138.3665 77.97546)
+ (xy 138.3665 76.44892) (xy 138.3919 76.327) (xy 138.44016 76.2127) (xy 138.51128 76.1111) (xy 138.60018 76.0222)
+ (xy 138.70432 75.95362) (xy 138.81862 75.90536) (xy 138.8491 75.89774) (xy 138.8491 71.4883) (xy 138.86688 71.3232)
+ (xy 138.89228 71.24192) (xy 138.9126 71.17334) (xy 138.99134 71.02602) (xy 139.09802 70.89902) (xy 141.63802 68.35902)
+ (xy 141.63802 68.35648) (xy 141.64056 68.35648) (xy 141.71168 68.29552) (xy 141.77518 68.24726) (xy 141.86154 68.20154)
+ (xy 141.91488 68.1736) (xy 141.91996 68.17106) (xy 141.9225 68.17106) (xy 141.9352 68.16598) (xy 142.0749 68.12534)
+ (xy 142.24 68.1101) (xy 146.6977 68.1101) (xy 146.8628 68.12788) (xy 146.94154 68.15328) (xy 147.01266 68.1736)
+ (xy 147.15998 68.25234) (xy 147.28698 68.35902) (xy 148.55952 69.63156) (xy 148.61794 69.70268) (xy 148.66874 69.76618)
+ (xy 148.71192 69.85254) (xy 148.7424 69.90588) (xy 148.7424 69.91096) (xy 148.74494 69.9135) (xy 148.74748 69.92874)
+ (xy 148.79066 70.0659) (xy 148.8059 70.231) (xy 148.8059 70.6374) (xy 148.85924 70.6755) (xy 149.05736 70.87362)
+ (xy 149.0726 70.89902) (xy 149.12594 70.81012) (xy 149.3393 70.56374) (xy 149.59838 70.36308) (xy 149.89048 70.21576)
+ (xy 150.10892 70.14972) (xy 150.368 70.3326) (xy 150.368 72.009) (xy 150.368 72.263) (xy 150.368 73.9394)
+ (xy 150.10892 74.12228) (xy 149.89048 74.05624) (xy 149.59838 73.90892) (xy 149.3393 73.70826) (xy 149.12594 73.46188)
+ (xy 149.0726 73.3679) (xy 149.04466 73.41108) (xy 148.84654 73.60666) (xy 148.6154 73.75906) (xy 148.35632 73.8632)
+ (xy 148.08454 73.914) (xy 147.80768 73.914) (xy 147.5359 73.85812) (xy 147.27936 73.75144) (xy 147.05076 73.5965)
+ (xy 146.85264 73.39838) (xy 146.83232 73.3679) (xy 146.812 73.4695) (xy 146.76374 73.5838) (xy 146.69262 73.6854)
+ (xy 146.60372 73.7743) (xy 146.49958 73.84288) (xy 146.38528 73.89114) (xy 146.26336 73.914) (xy 144.55902 73.914)
+ (xy 144.4371 73.8886) (xy 144.3228 73.84034) (xy 144.2212 73.76922) (xy 144.1323 73.68032) (xy 144.06372 73.57618)
+ (xy 144.01546 73.46188) (xy 143.9926 73.33996) (xy 143.9926 70.92442) (xy 144.018 70.8025) (xy 144.06626 70.6882)
+ (xy 144.13738 70.5866) (xy 144.22628 70.4977) (xy 144.33042 70.42912) (xy 144.44472 70.38086) (xy 144.56664 70.358)
+ (xy 146.27098 70.358) (xy 146.3929 70.3834) (xy 146.5072 70.43166) (xy 146.6088 70.50278) (xy 146.6977 70.59168)
+ (xy 146.76628 70.69582) (xy 146.81454 70.81012) (xy 146.83232 70.90664) (xy 146.86534 70.86092) (xy 147.06346 70.66534)
+ (xy 147.1041 70.6374) (xy 147.1041 70.58406) (xy 146.3294 69.8119) (xy 142.59306 69.8119) (xy 140.5509 71.85406)
+ (xy 140.5509 75.89774) (xy 140.589 75.9079) (xy 140.7033 75.95616) (xy 140.8049 76.02728) (xy 140.8938 76.11618)
+ (xy 140.96238 76.22032) (xy 141.01064 76.33462) (xy 141.0335 76.45654) (xy 141.0335 77.98308) (xy 141.0081 78.105)
+ (xy 140.95984 78.2193) (xy 140.88872 78.3209) (xy 140.79982 78.4098) (xy 140.69568 78.47838) (xy 140.58138 78.52664)
+ (xy 140.45946 78.5495) (xy 138.93292 78.5495) (xy 138.93292 80.10398) (xy 139.08786 80.33766) (xy 139.19708 80.60436)
+ (xy 139.25296 80.88376) (xy 139.24788 81.21142) (xy 139.18438 81.48828) (xy 139.06754 81.7499) (xy 138.98626 81.86166)
+ (xy 142.09014 81.86166) (xy 142.113 81.85912) (xy 142.29588 81.87944) (xy 142.30096 81.87944) (xy 142.30604 81.88198)
+ (xy 142.34414 81.89214) (xy 142.47114 81.93278) (xy 142.47876 81.93532) (xy 142.4813 81.93786) (xy 142.494 81.94294)
+ (xy 142.6337 82.01914) (xy 142.77594 82.13598) (xy 144.27454 83.63458) (xy 144.27454 81.98612) (xy 143.95704 81.9277)
+ (xy 143.85544 81.73212) (xy 143.74622 81.41462) (xy 143.7005 81.08442) (xy 143.72336 80.74914) (xy 143.80972 80.42656)
+ (xy 143.95704 80.1243) (xy 144.27454 80.06588) (xy 145.23466 81.026) (xy 144.27454 81.98612) (xy 144.27454 83.63458)
+ (xy 145.28546 84.64804) (xy 145.47342 84.64804) (xy 145.47342 82.7405) (xy 145.13814 82.71764) (xy 144.81556 82.63128)
+ (xy 144.5133 82.48396) (xy 144.45488 82.16646) (xy 145.415 81.20634) (xy 145.415 80.84566) (xy 144.45488 79.88554)
+ (xy 144.5133 79.56804) (xy 144.70888 79.46644) (xy 145.02638 79.35722) (xy 145.35658 79.3115) (xy 145.69186 79.33436)
+ (xy 146.01444 79.42072) (xy 146.3167 79.56804) (xy 146.37512 79.88554) (xy 145.415 80.84566) (xy 145.415 81.20634)
+ (xy 146.37512 82.16646) (xy 146.3167 82.48396) (xy 146.12112 82.58556) (xy 145.80362 82.69478) (xy 145.47342 82.7405)
+ (xy 145.47342 84.64804) (xy 145.56994 84.64804) (xy 145.84934 84.70646) (xy 146.1135 84.81822) (xy 146.34972 84.97824)
+ (xy 146.53514 85.1662) (xy 146.55546 85.1662) (xy 146.55546 81.98612) (xy 145.59534 81.026) (xy 146.55546 80.06588)
+ (xy 146.87296 80.1243) (xy 146.97456 80.31988) (xy 147.08378 80.63738) (xy 147.1295 80.96758) (xy 147.10664 81.30286)
+ (xy 147.02028 81.62544) (xy 146.87296 81.9277) (xy 146.55546 81.98612) (xy 146.55546 85.1662) (xy 148.83384 85.1662)
+ (xy 151.73706 82.26298) (xy 151.7396 82.2579) (xy 151.74214 82.2579) (xy 151.81072 82.20202) (xy 151.8793 82.14868)
+ (xy 151.88184 82.14614) (xy 151.88692 82.1436) (xy 151.95296 82.10804) (xy 152.04186 82.06232) (xy 152.04694 82.05978)
+ (xy 152.04948 82.05978) (xy 152.05964 82.0547) (xy 152.21712 82.00898) (xy 152.4 81.9912) (xy 155.0416 81.9912)
+ (xy 155.0416 78.5495) (xy 154.77998 78.54442) (xy 154.52598 78.48854) (xy 154.28722 78.38186) (xy 154.07132 78.23454)
+ (xy 153.89098 78.04658) (xy 153.75128 77.8256) (xy 153.65476 77.5843) (xy 153.6065 77.32776) (xy 153.60904 77.089)
+ (xy 152.43048 75.91044) (xy 152.32634 75.7809) (xy 152.3238 75.77582) (xy 152.32126 75.77328) (xy 152.30094 75.73772)
+ (xy 152.2476 75.63612) (xy 152.19934 75.4761) (xy 152.1841 75.311) (xy 152.1841 73.63206) (xy 152.13076 73.5965)
+ (xy 151.93264 73.39838) (xy 151.91486 73.37044) (xy 151.86406 73.46188) (xy 151.6507 73.70826) (xy 151.39162 73.90892)
+ (xy 151.09952 74.05624) (xy 150.88108 74.12228) (xy 150.622 73.9394) (xy 150.622 72.263) (xy 150.622 72.009)
+ (xy 150.622 70.3326) (xy 150.88108 70.14972) (xy 151.09952 70.21576) (xy 151.39162 70.36308) (xy 151.6507 70.56374)
+ (xy 151.86406 70.81012) (xy 151.91486 70.90156) (xy 151.94534 70.86092) (xy 152.14346 70.66534) (xy 152.3746 70.51294)
+ (xy 152.63368 70.4088) (xy 152.90546 70.358) (xy 153.18232 70.358) (xy 153.4541 70.41388) (xy 153.71064 70.52056)
+ (xy 153.93924 70.6755) (xy 154.13736 70.87362) (xy 154.28976 71.10476) (xy 154.39644 71.3613) (xy 154.44978 71.63308)
+ (xy 154.45232 72.37984) (xy 154.44724 72.6567) (xy 154.39136 72.92848) (xy 154.28214 73.18248) (xy 154.12466 73.41108)
+ (xy 153.92654 73.60666) (xy 153.8859 73.63206) (xy 153.8859 74.95794) (xy 154.81046 75.88504) (xy 155.0797 75.88504)
+ (xy 155.33624 75.93838) (xy 155.57754 76.03998) (xy 155.79344 76.1873) (xy 155.97632 76.37272) (xy 156.1211 76.58862)
+ (xy 156.22016 76.82992) (xy 156.27096 77.08646) (xy 156.26588 77.38364) (xy 156.20746 77.64018) (xy 156.10332 77.8764)
+ (xy 155.95092 78.08976) (xy 155.76296 78.2701) (xy 155.54198 78.4098) (xy 155.29814 78.50378) (xy 155.0416 78.5495)
+ (xy 155.0416 81.9912) (xy 158.99384 81.9912) (xy 159.67202 81.31048) (xy 159.67202 80.0735) (xy 159.40278 80.07096)
+ (xy 159.22752 80.07096) (xy 159.05734 80.0354) (xy 158.89732 79.96682) (xy 158.75254 79.8703) (xy 158.63062 79.74584)
+ (xy 158.5341 79.60106) (xy 158.46806 79.44104) (xy 158.43504 79.27086) (xy 158.4325 78.83398) (xy 158.65348 78.613)
+ (xy 158.65348 78.359) (xy 158.4325 78.13802) (xy 158.43504 77.70114) (xy 158.46806 77.53096) (xy 158.5341 77.37094)
+ (xy 158.63062 77.22616) (xy 158.75254 77.1017) (xy 158.89732 77.00518) (xy 159.05734 76.9366) (xy 159.22752 76.90104)
+ (xy 159.40278 76.90104) (xy 159.67202 76.8985) (xy 159.893 77.11948) (xy 159.893 78.359) (xy 158.65348 78.359)
+ (xy 158.65348 78.613) (xy 159.893 78.613) (xy 159.893 79.85252) (xy 159.67202 80.0735) (xy 159.67202 81.31048)
+ (xy 160.93948 80.04302) (xy 160.81248 80.07096) (xy 160.63722 80.07096) (xy 160.36798 80.0735) (xy 160.147 79.85252)
+ (xy 160.147 78.613) (xy 160.147 78.359) (xy 160.147 77.11948) (xy 160.36798 76.8985) (xy 160.63722 76.90104)
+ (xy 160.81248 76.90104) (xy 160.98266 76.9366) (xy 161.14268 77.00518) (xy 161.28746 77.1017) (xy 161.40938 77.22616)
+ (xy 161.5059 77.37094) (xy 161.57194 77.53096) (xy 161.60496 77.70114) (xy 161.6075 78.13802) (xy 161.38652 78.359)
+ (xy 160.147 78.359) (xy 160.147 78.613) (xy 161.38652 78.613) (xy 161.6075 78.83398) (xy 161.60496 79.27086)
+ (xy 161.57702 79.40548) (xy 163.5252 77.45984) (xy 163.5252 61.72962) (xy 160.3502 58.55716) (xy 125.24232 58.55716)
+ (xy 124.07138 59.72556) (xy 124.079 59.75604) (xy 124.079 61.66358) (xy 124.0536 61.7855) (xy 124.00534 61.8998)
+ (xy 123.93422 62.0014) (xy 123.84532 62.0903) (xy 123.74118 62.15888) (xy 123.62688 62.20714) (xy 123.50496 62.23)
+ (xy 121.59742 62.23) (xy 121.4755 62.2046) (xy 121.3612 62.15634) (xy 121.2596 62.08522) (xy 121.1707 61.99632)
+ (xy 121.10212 61.89218) (xy 121.05386 61.77788) (xy 121.031 61.65596) (xy 121.031 61.6458) (xy 120.95988 61.6458)
+ (xy 120.89892 61.63818) (xy 120.78716 61.62802) (xy 120.77954 61.62548) (xy 120.777 61.62548) (xy 120.60174 61.5696)
+ (xy 120.48236 61.50356) (xy 120.44934 61.48578) (xy 120.44426 61.4807) (xy 120.44172 61.4807) (xy 120.36044 61.41212)
+ (xy 120.3071 61.36894) (xy 116.77142 57.8358) (xy 97.79 57.8358) (xy 97.79 58.23458) (xy 97.7646 58.3565)
+ (xy 97.71634 58.4708) (xy 97.64522 58.5724) (xy 97.55632 58.6613) (xy 97.45218 58.72988) (xy 97.33788 58.77814)
+ (xy 97.21596 58.801) (xy 94.54642 58.801) (xy 94.4245 58.7756) (xy 94.3102 58.72734) (xy 94.2086 58.65622)
+ (xy 94.1197 58.56732) (xy 94.05112 58.46318) (xy 94.00286 58.34888) (xy 93.98 58.22696) (xy 93.98 55.55742)
+ (xy 94.0054 55.4355) (xy 94.05366 55.3212) (xy 94.12478 55.2196) (xy 94.21368 55.1307) (xy 94.31782 55.06212)
+ (xy 94.43212 55.01386) (xy 94.55404 54.991) (xy 97.22358 54.991) (xy 97.3455 55.0164) (xy 97.4598 55.06466)
+ (xy 97.5614 55.13578) (xy 97.6503 55.22468) (xy 97.71888 55.32882) (xy 97.76714 55.44312) (xy 97.79 55.56504)
+ (xy 97.79 55.9562) (xy 117.13972 55.95366) (xy 117.16512 55.95366) (xy 117.348 55.97398) (xy 117.35308 55.97398)
+ (xy 117.35816 55.97652) (xy 117.39626 55.98668) (xy 117.52326 56.02732) (xy 117.53088 56.02986) (xy 117.53342 56.0324)
+ (xy 117.54612 56.03748) (xy 117.68582 56.11368) (xy 117.82806 56.23052) (xy 121.10466 59.50966) (xy 121.17578 59.4106)
+ (xy 121.26468 59.3217) (xy 121.36882 59.25312) (xy 121.48312 59.20486) (xy 121.60504 59.182) (xy 121.9581 59.182)
+ (xy 124.19076 56.94934) (xy 124.1933 56.94426) (xy 124.19584 56.94426) (xy 124.26442 56.88838) (xy 124.333 56.83504)
+ (xy 124.33554 56.8325) (xy 124.34062 56.82996) (xy 124.40666 56.7944) (xy 124.49556 56.74868) (xy 124.50064 56.74614)
+ (xy 124.50318 56.74614) (xy 124.51334 56.74106) (xy 124.67082 56.69534) (xy 124.8537 56.67756) (xy 160.7185 56.67756)
+ (xy 160.7439 56.67502) (xy 160.92678 56.69534) (xy 160.93186 56.69534) (xy 160.93694 56.69788) (xy 160.97504 56.70804)
+ (xy 161.10204 56.74868) (xy 161.10966 56.75122) (xy 161.1122 56.75376) (xy 161.1249 56.75884) (xy 161.2646 56.83504)
+ (xy 161.40684 56.95188) (xy 165.11778 60.66536) (xy 165.13302 60.67806) (xy 165.24732 60.8203) (xy 165.24732 60.82284)
+ (xy 165.2524 60.82792) (xy 165.27272 60.8711) (xy 165.33368 60.98286) (xy 165.38702 61.15812) (xy 165.4048 61.341)
+ (xy 165.4048 77.8637) (xy 165.38448 78.04658) (xy 165.3286 78.22184) (xy 165.26256 78.33868) (xy 165.24478 78.37424)
+ (xy 165.2397 78.37678) (xy 165.2397 78.38186) (xy 165.17112 78.4606) (xy 165.12794 78.51648) (xy 160.05048 83.59648)
+ (xy 160.04794 83.59648) (xy 160.04794 83.59902) (xy 159.9057 83.71332) (xy 159.90062 83.71332) (xy 159.89808 83.7184)
+ (xy 159.77108 83.7819) (xy 159.74314 83.79968) (xy 159.7406 83.79968) (xy 159.73298 83.80476) (xy 159.66186 83.82254)
+ (xy 159.56788 83.85302) (xy 159.385 83.8708) (xy 152.78862 83.8708) (xy 150.55342 86.106) (xy 151.76246 87.31504)
+ (xy 151.9047 87.31504) (xy 152.16124 87.36838) (xy 152.40254 87.46998) (xy 152.61844 87.6173) (xy 152.80132 87.80272)
+ (xy 152.9461 88.01862) (xy 153.04516 88.25992) (xy 153.09596 88.51646) (xy 153.09088 88.81364) (xy 153.03246 89.07018)
+ (xy 152.92832 89.3064) (xy 152.77592 89.51976) (xy 152.58796 89.7001) (xy 152.36698 89.8398) (xy 152.12314 89.93378)
+ (xy 151.8666 89.9795) (xy 151.60498 89.97442) (xy 151.35098 89.91854) (xy 151.11222 89.81186) (xy 150.89632 89.66454)
+ (xy 150.71598 89.47658) (xy 150.57628 89.2556) (xy 150.495 89.0524) (xy 150.49246 89.07018) (xy 150.38832 89.3064)
+ (xy 150.23592 89.51976) (xy 150.04796 89.7001) (xy 149.82698 89.8398) (xy 149.79396 89.8525) (xy 149.99208 89.8525)
+ (xy 150.114 89.8779) (xy 150.2283 89.92616) (xy 150.3299 89.99728) (xy 150.4188 90.08618) (xy 150.48738 90.19032)
+ (xy 150.53564 90.30462) (xy 150.54072 90.3351) (xy 152.68194 90.3351) (xy 153.4541 89.56294) (xy 153.4541 87.3633)
+ (xy 153.47188 87.1982) (xy 153.49474 87.11946) (xy 153.5176 87.04834) (xy 153.59634 86.90102) (xy 153.70302 86.77402)
+ (xy 154.97302 85.50402) (xy 154.97302 85.50148) (xy 154.97556 85.50148) (xy 155.04668 85.44052) (xy 155.11018 85.39226)
+ (xy 155.19654 85.34654) (xy 155.24988 85.3186) (xy 155.25496 85.31606) (xy 155.2575 85.31606) (xy 155.27274 85.31098)
+ (xy 155.4099 85.27034) (xy 155.575 85.2551) (xy 163.8427 85.2551) (xy 164.0078 85.27288) (xy 164.08654 85.29828)
+ (xy 164.15766 85.3186) (xy 164.30498 85.39734) (xy 164.43198 85.50402) (xy 166.32428 87.39886) (xy 166.32936 87.4014)
+ (xy 166.32936 73.025) (xy 166.32936 62.2554) (xy 185.66384 62.2554) (xy 185.66384 73.025) (xy 166.32936 73.025)
+ (xy 166.32936 87.4014) (xy 166.3446 87.41664) (xy 166.44874 87.54618) (xy 166.49192 87.63254) (xy 166.5224 87.68588)
+ (xy 166.5224 87.69096) (xy 166.52494 87.6935) (xy 166.52494 87.69858) (xy 166.57066 87.8459) (xy 166.5859 88.011)
+ (xy 166.5859 89.99982) (xy 166.66972 90.05824) (xy 166.87038 90.26144) (xy 167.02786 90.49766) (xy 167.13708 90.76436)
+ (xy 167.19296 91.04376) (xy 167.18788 91.37142) (xy 167.12438 91.64828) (xy 167.00754 91.9099) (xy 166.84244 92.14358)
+ (xy 166.6367 92.3417) (xy 166.3954 92.4941) (xy 166.1287 92.59824) (xy 165.84676 92.6465) (xy 165.55974 92.64142)
+ (xy 165.4937 92.62618) (xy 162.1409 95.98152) (xy 162.1409 96.34982) (xy 162.22472 96.40824) (xy 162.42538 96.61144)
+ (xy 162.58286 96.84766) (xy 162.69208 97.11436) (xy 162.74796 97.39376) (xy 162.74288 97.72142) (xy 162.67938 97.99828)
+ (xy 162.56254 98.2599) (xy 162.39744 98.49358) (xy 162.1917 98.6917) (xy 161.9504 98.8441) (xy 161.6837 98.94824)
+ (xy 161.40176 98.9965) (xy 161.11474 98.99142) (xy 160.83788 98.92792) (xy 160.57372 98.81362) (xy 160.34004 98.65106)
+ (xy 160.14192 98.44532) (xy 159.98698 98.20402) (xy 159.88284 97.93986) (xy 159.8295 97.65792) (xy 159.83458 97.3709)
+ (xy 159.893 97.0915) (xy 160.0073 96.82988) (xy 160.16732 96.59366) (xy 160.37306 96.393) (xy 160.4391 96.34982)
+ (xy 160.4391 95.6183) (xy 160.45688 95.4532) (xy 160.48228 95.37192) (xy 160.5026 95.30334) (xy 160.58134 95.15602)
+ (xy 160.68802 95.02902) (xy 164.29482 91.42222) (xy 164.2745 91.30792) (xy 164.27958 91.0209) (xy 164.338 90.7415)
+ (xy 164.4523 90.47988) (xy 164.61232 90.24366) (xy 164.81806 90.043) (xy 164.8841 89.99982) (xy 164.8841 88.36152)
+ (xy 163.47694 86.9569) (xy 155.92552 86.9569) (xy 155.1559 87.72906) (xy 155.1559 89.9287) (xy 155.13812 90.0938)
+ (xy 155.11526 90.16238) (xy 155.0924 90.24366) (xy 155.01366 90.39098) (xy 154.90698 90.51798) (xy 153.63698 91.78798)
+ (xy 153.63444 91.78798) (xy 153.63444 91.79052) (xy 153.5049 91.89466) (xy 153.49982 91.89466) (xy 153.49728 91.89974)
+ (xy 153.46172 91.91752) (xy 153.36012 91.9734) (xy 153.2001 92.02166) (xy 153.035 92.0369) (xy 150.54072 92.0369)
+ (xy 150.5331 92.075) (xy 150.48484 92.1893) (xy 150.41372 92.2909) (xy 150.32482 92.3798) (xy 150.22068 92.44838)
+ (xy 150.10638 92.49664) (xy 149.98446 92.5195) (xy 148.45792 92.5195) (xy 148.336 92.4941) (xy 148.2217 92.44584)
+ (xy 148.1201 92.37472) (xy 148.0312 92.28582) (xy 147.96262 92.18168) (xy 147.91436 92.06738) (xy 147.8915 91.94546)
+ (xy 147.8915 90.41892) (xy 147.9169 90.297) (xy 147.96516 90.1827) (xy 148.03628 90.0811) (xy 148.12518 89.9922)
+ (xy 148.22932 89.92362) (xy 148.34362 89.87536) (xy 148.46554 89.8525) (xy 148.66112 89.8525) (xy 148.57222 89.81186)
+ (xy 148.35632 89.66454) (xy 148.19376 89.4969) (xy 146.67992 89.4969) (xy 146.69516 89.52992) (xy 146.74596 89.78646)
+ (xy 146.74088 90.08364) (xy 146.68246 90.34018) (xy 146.57832 90.5764) (xy 146.42592 90.78976) (xy 146.23796 90.9701)
+ (xy 146.01698 91.1098) (xy 145.77314 91.20378) (xy 145.5166 91.2495) (xy 145.25498 91.24442) (xy 145.00098 91.18854)
+ (xy 144.76222 91.08186) (xy 144.54632 90.93454) (xy 144.36598 90.74658) (xy 144.3609 90.73642) (xy 144.3609 91.46794)
+ (xy 147.03552 94.1451) (xy 153.9494 94.1451) (xy 155.9941 92.10294) (xy 155.9941 91.09964) (xy 155.89504 91.03106)
+ (xy 155.69692 90.82532) (xy 155.54198 90.58402) (xy 155.43784 90.31986) (xy 155.3845 90.03792) (xy 155.38958 89.7509)
+ (xy 155.448 89.4715) (xy 155.5623 89.20988) (xy 155.72232 88.97366) (xy 155.92806 88.773) (xy 156.16682 88.61806)
+ (xy 156.43098 88.51138) (xy 156.71292 88.45804) (xy 156.99994 88.45804) (xy 157.27934 88.51646) (xy 157.5435 88.62822)
+ (xy 157.77972 88.78824) (xy 157.98038 88.99144) (xy 158.02864 89.0651) (xy 160.10382 89.0651) (xy 160.16732 88.97366)
+ (xy 160.37306 88.773) (xy 160.61182 88.61806) (xy 160.87598 88.51138) (xy 161.15792 88.45804) (xy 161.44494 88.45804)
+ (xy 161.72434 88.51646) (xy 161.9885 88.62822) (xy 162.22472 88.78824) (xy 162.42538 88.99144) (xy 162.58286 89.22766)
+ (xy 162.69208 89.49436) (xy 162.74796 89.77376) (xy 162.74288 90.10142) (xy 162.67938 90.37828) (xy 162.56254 90.6399)
+ (xy 162.39744 90.87358) (xy 162.1917 91.0717) (xy 161.9504 91.2241) (xy 161.6837 91.32824) (xy 161.40176 91.3765)
+ (xy 161.11474 91.37142) (xy 160.83788 91.30792) (xy 160.57372 91.19362) (xy 160.34004 91.03106) (xy 160.14192 90.82532)
+ (xy 160.10382 90.7669) (xy 158.0261 90.7669) (xy 157.95244 90.87358) (xy 157.7467 91.0717) (xy 157.6959 91.10218)
+ (xy 157.6959 92.4687) (xy 157.67812 92.6338) (xy 157.65526 92.70238) (xy 157.6324 92.78366) (xy 157.55112 92.93098)
+ (xy 157.44698 93.05798) (xy 154.90698 95.59798) (xy 154.90444 95.59798) (xy 154.90444 95.60052) (xy 154.7749 95.70466)
+ (xy 154.76982 95.70466) (xy 154.76728 95.70974) (xy 154.73172 95.72752) (xy 154.63012 95.7834) (xy 154.4701 95.83166)
+ (xy 154.305 95.8469) (xy 146.6723 95.8469) (xy 146.5072 95.82912) (xy 146.36242 95.78086) (xy 146.35734 95.78086)
+ (xy 146.3548 95.77832) (xy 146.34972 95.77832) (xy 146.2913 95.7453) (xy 146.2659 95.73006) (xy 146.2659 96.21774)
+ (xy 146.304 96.2279) (xy 146.4183 96.27616) (xy 146.5199 96.34728) (xy 146.6088 96.43618) (xy 146.67738 96.54032)
+ (xy 146.72564 96.65462) (xy 146.7485 96.77654) (xy 146.7485 98.30308) (xy 146.7231 98.425) (xy 146.67484 98.5393)
+ (xy 146.60372 98.6409) (xy 146.51482 98.7298) (xy 146.41068 98.79838) (xy 146.29638 98.84664) (xy 146.17446 98.8695)
+ (xy 144.64792 98.8695) (xy 144.526 98.8441) (xy 144.4117 98.79584) (xy 144.3101 98.72472) (xy 144.2212 98.63582)
+ (xy 144.15262 98.53168) (xy 144.10436 98.41738) (xy 144.0815 98.29546) (xy 144.0815 96.76892) (xy 144.1069 96.647)
+ (xy 144.15516 96.5327) (xy 144.22628 96.4311) (xy 144.31518 96.3422) (xy 144.41932 96.27362) (xy 144.53362 96.22536)
+ (xy 144.5641 96.21774) (xy 144.5641 95.98406) (xy 141.00048 92.42044) (xy 140.89634 92.2909) (xy 140.8938 92.28582)
+ (xy 140.89126 92.28328) (xy 140.87094 92.24772) (xy 140.8176 92.14612) (xy 140.76934 91.9861) (xy 140.7541 91.821)
+ (xy 140.7541 90.9447) (xy 140.73632 90.93454) (xy 140.55598 90.74658) (xy 140.41628 90.5256) (xy 140.31976 90.2843)
+ (xy 140.2715 90.02776) (xy 140.27658 89.76614) (xy 140.32992 89.5096) (xy 140.43406 89.27084) (xy 140.58138 89.05494)
+ (xy 140.7668 88.87206) (xy 140.78204 88.8619) (xy 139.41552 88.8619) (xy 136.7409 91.53652) (xy 136.7409 92.7227)
+ (xy 136.72312 92.8878) (xy 136.68756 92.99702) (xy 136.67486 93.0402) (xy 136.67232 93.04274) (xy 136.67232 93.04528)
+ (xy 136.6647 93.05544) (xy 136.59612 93.18498) (xy 136.48944 93.31452) (xy 136.3599 93.41866) (xy 136.21512 93.4974)
+ (xy 136.2075 93.4974) (xy 136.2075 93.49994) (xy 136.18464 93.50502) (xy 136.0551 93.54566) (xy 135.89 93.5609)
+ (xy 126.37516 93.55836) (xy 126.54534 93.59646) (xy 126.8095 93.70822) (xy 127.04572 93.86824) (xy 127.24638 94.07144)
+ (xy 127.40386 94.30766) (xy 127.51308 94.57436) (xy 127.56896 94.85376) (xy 127.56388 95.18142) (xy 127.50038 95.45828)
+ (xy 127.38354 95.7199) (xy 127.21844 95.95358) (xy 127.0127 96.1517) (xy 126.7714 96.3041) (xy 126.5047 96.40824)
+ (xy 126.22276 96.4565) (xy 125.93574 96.45142) (xy 125.65888 96.38792) (xy 125.39472 96.27362) (xy 125.16104 96.11106)
+ (xy 125.04674 95.99168) (xy 124.0409 97.00006) (xy 124.0409 102.00894) (xy 124.81052 102.77856) (xy 125.33884 102.7811)
+ (xy 125.15088 102.73792) (xy 124.88672 102.62362) (xy 124.65304 102.46106) (xy 124.45492 102.25532) (xy 124.29998 102.01402)
+ (xy 124.19584 101.74986) (xy 124.1425 101.46792) (xy 124.14758 101.1809) (xy 124.206 100.9015) (xy 124.3203 100.63988)
+ (xy 124.48032 100.40366) (xy 124.68606 100.203) (xy 124.7521 100.15982) (xy 124.7521 98.2853) (xy 124.76988 98.1202)
+ (xy 124.79274 98.03892) (xy 124.8156 97.97034) (xy 124.89434 97.82302) (xy 125.00102 97.69602) (xy 125.76302 96.93148)
+ (xy 125.77064 96.9264) (xy 125.90018 96.82226) (xy 125.98654 96.77654) (xy 126.03988 96.7486) (xy 126.04496 96.74606)
+ (xy 126.0475 96.74606) (xy 126.06274 96.74098) (xy 126.1999 96.70034) (xy 126.365 96.6851) (xy 129.14376 96.6851)
+ (xy 129.15138 96.67494) (xy 129.3368 96.49206) (xy 129.55524 96.34982) (xy 129.79908 96.2533) (xy 130.05562 96.20504)
+ (xy 130.3147 96.20504) (xy 130.57124 96.25838) (xy 130.81254 96.35998) (xy 131.02844 96.5073) (xy 131.21132 96.69272)
+ (xy 131.3561 96.90862) (xy 131.44246 97.11944) (xy 131.54406 96.89084) (xy 131.69138 96.67494) (xy 131.8768 96.49206)
+ (xy 132.09524 96.34982) (xy 132.33908 96.2533) (xy 132.59562 96.20504) (xy 132.8547 96.20504) (xy 133.11124 96.25838)
+ (xy 133.35254 96.35998) (xy 133.56844 96.5073) (xy 133.75132 96.69272) (xy 133.8961 96.90862) (xy 133.99516 97.14992)
+ (xy 134.04596 97.40646) (xy 134.04088 97.65792) (xy 134.88416 98.50374) (xy 139.50188 98.50374) (xy 140.2715 97.73412)
+ (xy 140.2715 96.76892) (xy 140.2969 96.647) (xy 140.34516 96.5327) (xy 140.41628 96.4311) (xy 140.50518 96.3422)
+ (xy 140.60932 96.27362) (xy 140.72362 96.22536) (xy 140.84554 96.2025) (xy 142.37208 96.2025) (xy 142.494 96.2279)
+ (xy 142.6083 96.27616) (xy 142.7099 96.34728) (xy 142.7988 96.43618) (xy 142.86738 96.54032) (xy 142.91564 96.65462)
+ (xy 142.9385 96.77654) (xy 142.9385 98.30308) (xy 142.9131 98.425) (xy 142.86484 98.5393) (xy 142.79372 98.6409)
+ (xy 142.70482 98.7298) (xy 142.60068 98.79838) (xy 142.48638 98.84664) (xy 142.4559 98.85172) (xy 142.4559 100.15982)
+ (xy 142.53972 100.21824) (xy 142.74038 100.42144) (xy 142.78864 100.4951) (xy 144.1577 100.4951) (xy 144.3228 100.51288)
+ (xy 144.40154 100.53828) (xy 144.47266 100.5586) (xy 144.61998 100.63734) (xy 144.74698 100.74402) (xy 145.76552 101.7651)
+ (xy 146.90344 101.7651) (xy 146.93646 101.7651) (xy 146.90344 101.73208) (xy 146.82978 101.65842) (xy 146.90344 101.58222)
+ (xy 147.77466 100.711) (xy 146.90344 101.58222) (xy 146.60118 101.5365) (xy 146.51482 101.3714) (xy 146.41322 101.0793)
+ (xy 146.37004 100.77196) (xy 146.38782 100.46208) (xy 146.46656 100.16236) (xy 146.60118 99.8855) (xy 146.90344 99.83978)
+ (xy 147.77466 100.711) (xy 147.95246 100.89134) (xy 147.955 100.53066) (xy 147.08378 99.65944) (xy 147.1295 99.35718)
+ (xy 147.2946 99.27082) (xy 147.5867 99.16922) (xy 147.89404 99.12604) (xy 148.20392 99.14382) (xy 148.50364 99.22256)
+ (xy 148.7805 99.35718) (xy 148.82622 99.65944) (xy 147.955 100.53066) (xy 147.95246 100.89134) (xy 148.13534 100.711)
+ (xy 149.00656 99.83978) (xy 149.30882 99.8855) (xy 149.36724 99.9998) (xy 149.47138 99.84994) (xy 149.6568 99.66706)
+ (xy 149.87524 99.52482) (xy 149.92604 99.5045) (xy 149.72792 99.5045) (xy 149.606 99.4791) (xy 149.4917 99.43084)
+ (xy 149.3901 99.35972) (xy 149.3012 99.27082) (xy 149.23262 99.16668) (xy 149.18436 99.05238) (xy 149.1615 98.93046)
+ (xy 149.1615 97.40392) (xy 149.1869 97.282) (xy 149.23516 97.1677) (xy 149.30628 97.0661) (xy 149.39518 96.9772)
+ (xy 149.49932 96.90862) (xy 149.61362 96.86036) (xy 149.73554 96.8375) (xy 151.26208 96.8375) (xy 151.384 96.8629)
+ (xy 151.4983 96.91116) (xy 151.5999 96.98228) (xy 151.6888 97.07118) (xy 151.75738 97.17532) (xy 151.80564 97.28962)
+ (xy 151.81072 97.3201) (xy 155.39974 97.3201) (xy 155.448 97.0915) (xy 155.5623 96.82988) (xy 155.72232 96.59366)
+ (xy 155.92806 96.393) (xy 156.16682 96.23806) (xy 156.43098 96.13138) (xy 156.71292 96.07804) (xy 156.99994 96.07804)
+ (xy 157.27934 96.13646) (xy 157.5435 96.24822) (xy 157.77972 96.40824) (xy 157.98038 96.61144) (xy 158.13786 96.84766)
+ (xy 158.24708 97.11436) (xy 158.30296 97.39376) (xy 158.29788 97.72142) (xy 158.23438 97.99828) (xy 158.11754 98.2599)
+ (xy 157.95244 98.49358) (xy 157.84068 98.59772) (xy 158.46552 99.2251) (xy 164.1094 99.2251) (xy 164.73424 98.60026)
+ (xy 164.58692 98.44532) (xy 164.43198 98.20402) (xy 164.32784 97.93986) (xy 164.2745 97.65792) (xy 164.27958 97.3709)
+ (xy 164.338 97.0915) (xy 164.4523 96.82988) (xy 164.61232 96.59366) (xy 164.81806 96.393) (xy 165.05682 96.23806)
+ (xy 165.32098 96.13138) (xy 165.60292 96.07804) (xy 165.88994 96.07804) (xy 166.16934 96.13646) (xy 166.4335 96.24822)
+ (xy 166.66972 96.40824) (xy 166.87038 96.61144) (xy 167.02786 96.84766) (xy 167.13708 97.11436) (xy 167.19296 97.39376)
+ (xy 167.18788 97.72142) (xy 167.12438 97.99828) (xy 167.00754 98.2599) (xy 166.84244 98.49358) (xy 166.6367 98.6917)
+ (xy 166.5859 98.72218) (xy 166.5859 98.8187) (xy 166.56812 98.9838) (xy 166.54526 99.05238) (xy 166.5224 99.13366)
+ (xy 166.44366 99.28098) (xy 166.33698 99.40798) (xy 165.06698 100.67798) (xy 165.06444 100.67798) (xy 165.06444 100.68052)
+ (xy 164.9349 100.78466) (xy 164.92982 100.78466) (xy 164.92728 100.78974) (xy 164.89172 100.80752) (xy 164.79012 100.8634)
+ (xy 164.6301 100.91166) (xy 164.465 100.9269) (xy 158.1023 100.9269) (xy 157.9372 100.90912) (xy 157.86608 100.88626)
+ (xy 157.78734 100.8634) (xy 157.64002 100.78466) (xy 157.51302 100.67798) (xy 156.24048 99.40544) (xy 156.13634 99.2759)
+ (xy 156.1338 99.27082) (xy 156.13126 99.26828) (xy 156.11094 99.23272) (xy 156.0576 99.13112) (xy 156.02458 99.0219)
+ (xy 151.81072 99.0219) (xy 151.8031 99.06) (xy 151.75484 99.1743) (xy 151.68372 99.2759) (xy 151.59482 99.3648)
+ (xy 151.49068 99.43338) (xy 151.37638 99.48164) (xy 151.25446 99.5045) (xy 151.05888 99.5045) (xy 151.13254 99.53498)
+ (xy 151.34844 99.6823) (xy 151.53132 99.86772) (xy 151.6761 100.08362) (xy 151.77516 100.32492) (xy 151.82596 100.58146)
+ (xy 151.82088 100.87864) (xy 151.76246 101.13518) (xy 151.65832 101.3714) (xy 151.50592 101.58476) (xy 151.31796 101.7651)
+ (xy 151.17318 101.854) (xy 151.09698 101.94798) (xy 149.82698 103.21798) (xy 149.82444 103.21798) (xy 149.82444 103.22052)
+ (xy 149.6949 103.32466) (xy 149.68982 103.32466) (xy 149.68728 103.32974) (xy 149.65172 103.34752) (xy 149.55012 103.4034)
+ (xy 149.3901 103.45166) (xy 149.225 103.4669) (xy 145.4023 103.4669) (xy 145.2372 103.44912) (xy 145.16862 103.42626)
+ (xy 145.08734 103.4034) (xy 144.94002 103.32466) (xy 144.81302 103.21798) (xy 143.79194 102.1969) (xy 142.7861 102.1969)
+ (xy 142.71244 102.30358) (xy 142.5067 102.5017) (xy 142.2654 102.6541) (xy 141.9987 102.75824) (xy 141.71676 102.8065)
+ (xy 141.42974 102.80142) (xy 141.15288 102.73792) (xy 140.88872 102.62362) (xy 140.65504 102.46106) (xy 140.45692 102.25532)
+ (xy 140.30198 102.01402) (xy 140.19784 101.74986) (xy 140.1445 101.46792) (xy 140.14958 101.1809) (xy 140.208 100.9015)
+ (xy 140.3223 100.63988) (xy 140.48232 100.40366) (xy 140.68806 100.203) (xy 140.7541 100.15982) (xy 140.7541 99.6569)
+ (xy 140.45692 99.95662) (xy 140.45438 99.95662) (xy 140.45438 99.95916) (xy 140.32484 100.0633) (xy 140.31976 100.0633)
+ (xy 140.31722 100.06838) (xy 140.28166 100.08616) (xy 140.18006 100.14204) (xy 140.02004 100.1903) (xy 139.85494 100.20554)
+ (xy 134.8994 100.20554) (xy 134.91972 100.21824) (xy 135.12038 100.42144) (xy 135.16864 100.4951) (xy 135.2677 100.4951)
+ (xy 135.4328 100.51288) (xy 135.51154 100.53828) (xy 135.58266 100.5586) (xy 135.72998 100.63734) (xy 135.85698 100.74402)
+ (xy 137.91946 102.80904) (xy 138.1887 102.80904) (xy 138.44524 102.86238) (xy 138.68654 102.96398) (xy 138.90244 103.1113)
+ (xy 139.08532 103.29672) (xy 139.2301 103.51262) (xy 139.32916 103.75392) (xy 139.37996 104.01046) (xy 139.37488 104.30764)
+ (xy 139.31646 104.56418) (xy 139.21232 104.8004) (xy 139.05992 105.01376) (xy 138.87196 105.1941) (xy 138.65098 105.3338)
+ (xy 138.40714 105.42778) (xy 138.1506 105.4735) (xy 137.88898 105.46842) (xy 137.63498 105.41254) (xy 137.39622 105.30586)
+ (xy 137.18032 105.15854) (xy 136.99998 104.97058) (xy 136.86028 104.7496) (xy 136.76376 104.5083) (xy 136.7155 104.25176)
+ (xy 136.71804 104.013) (xy 135.04926 102.34422) (xy 134.8867 102.5017) (xy 134.6454 102.6541) (xy 134.3787 102.75824)
+ (xy 134.09676 102.8065) (xy 133.80974 102.80142) (xy 133.53288 102.73792) (xy 133.26872 102.62362) (xy 133.03504 102.46106)
+ (xy 132.83692 102.25532) (xy 132.68198 102.01402) (xy 132.57784 101.74986) (xy 132.5245 101.46792) (xy 132.52958 101.1809)
+ (xy 132.588 100.9015) (xy 132.7023 100.63988) (xy 132.86232 100.40366) (xy 133.06806 100.203) (xy 133.30682 100.04806)
+ (xy 133.57098 99.94138) (xy 133.85292 99.88804) (xy 133.86308 99.88804) (xy 132.83946 98.86442) (xy 132.8166 98.8695)
+ (xy 132.55498 98.86442) (xy 132.30098 98.80854) (xy 132.06222 98.70186) (xy 131.84632 98.55454) (xy 131.66598 98.36658)
+ (xy 131.52628 98.1456) (xy 131.445 97.9424) (xy 131.44246 97.96018) (xy 131.33832 98.1964) (xy 131.18592 98.40976)
+ (xy 130.99796 98.5901) (xy 130.77698 98.7298) (xy 130.74396 98.7425) (xy 130.94208 98.7425) (xy 131.064 98.7679)
+ (xy 131.1783 98.81616) (xy 131.2799 98.88728) (xy 131.3688 98.97618) (xy 131.43738 99.08032) (xy 131.48564 99.19462)
+ (xy 131.5085 99.31654) (xy 131.5085 100.84308) (xy 131.4831 100.965) (xy 131.43484 101.0793) (xy 131.36372 101.1809)
+ (xy 131.27482 101.2698) (xy 131.17068 101.33838) (xy 131.05638 101.38664) (xy 130.93446 101.4095) (xy 130.04292 101.4095)
+ (xy 127.22098 104.23398) (xy 127.21844 104.23398) (xy 127.21844 104.23652) (xy 127.0889 104.34066) (xy 127.08382 104.34066)
+ (xy 127.08128 104.34574) (xy 127.04572 104.36352) (xy 126.94412 104.4194) (xy 126.7841 104.46766) (xy 126.619 104.4829)
+ (xy 124.4473 104.4829) (xy 124.2822 104.46512) (xy 124.21362 104.44226) (xy 124.13234 104.4194) (xy 123.98502 104.34066)
+ (xy 123.85802 104.23398) (xy 122.58548 102.96144) (xy 122.48134 102.8319) (xy 122.4788 102.82682) (xy 122.47626 102.82428)
+ (xy 122.45594 102.78872) (xy 122.4026 102.68712) (xy 122.35434 102.5271) (xy 122.3391 102.362) (xy 122.3391 96.6343)
+ (xy 122.35688 96.4692) (xy 122.37974 96.39046) (xy 122.4026 96.31934) (xy 122.48134 96.17202) (xy 122.58802 96.04502)
+ (xy 124.24664 94.3864) (xy 124.37618 94.28226) (xy 124.46254 94.23654) (xy 124.51588 94.2086) (xy 124.52096 94.20606)
+ (xy 124.5235 94.20606) (xy 124.5362 94.20098) (xy 124.6759 94.16034) (xy 124.841 94.1451) (xy 124.92482 94.14256)
+ (xy 124.98832 94.05366) (xy 125.19406 93.853) (xy 125.43282 93.69806) (xy 125.69698 93.59138) (xy 125.857 93.5609)
+ (xy 124.04852 93.5609) (xy 121.3739 96.23552) (xy 121.3739 98.5647) (xy 121.35612 98.7298) (xy 121.30786 98.87458)
+ (xy 121.30786 98.87966) (xy 121.30532 98.87966) (xy 121.30532 98.88728) (xy 121.2723 98.94316) (xy 121.22912 99.02698)
+ (xy 121.12244 99.15398) (xy 120.48998 99.78898) (xy 120.48744 99.78898) (xy 120.48744 99.79152) (xy 120.3579 99.89566)
+ (xy 120.35282 99.89566) (xy 120.35028 99.90074) (xy 120.31472 99.91852) (xy 120.21312 99.9744) (xy 120.0531 100.02266)
+ (xy 119.888 100.0379) (xy 118.9101 100.0379) (xy 118.94312 100.20554) (xy 117.983 101.16566) (xy 117.80266 101.346)
+ (xy 116.84254 102.30612) (xy 116.52504 102.2477) (xy 116.42344 102.05212) (xy 116.31422 101.73462) (xy 116.2685 101.40442)
+ (xy 116.29136 101.06914) (xy 116.37772 100.74656) (xy 116.51742 100.457) (xy 114.33048 102.64648) (xy 114.32794 102.64648)
+ (xy 114.32794 102.64902) (xy 114.1984 102.75316) (xy 114.19332 102.75316) (xy 114.19078 102.75824) (xy 114.15522 102.77602)
+ (xy 114.05362 102.8319) (xy 113.8936 102.88016) (xy 113.7285 102.8954) (xy 105.7148 102.8954) (xy 105.5497 102.87762)
+ (xy 105.40238 102.82936) (xy 105.39984 102.82936) (xy 105.3973 102.82682) (xy 105.39222 102.82682) (xy 105.3338 102.7938)
+ (xy 105.25252 102.75062) (xy 105.12298 102.64394) (xy 103.34498 100.8634) (xy 99.57308 100.8634) (xy 99.48672 100.9904)
+ (xy 99.2886 101.1809) (xy 99.05746 101.32568) (xy 98.80346 101.42474) (xy 98.53168 101.473) (xy 98.2599 101.46792)
+ (xy 97.9932 101.4095) (xy 97.74174 101.30028) (xy 97.51822 101.1428) (xy 97.32772 100.94722) (xy 97.17786 100.71608)
+ (xy 97.0788 100.46208) (xy 97.028 100.19284) (xy 97.03308 99.92106) (xy 97.08896 99.65182) (xy 97.19818 99.40036)
+ (xy 97.35058 99.17684) (xy 97.54616 98.9838) (xy 97.77476 98.83394) (xy 98.02876 98.73234) (xy 98.298 98.68154)
+ (xy 98.56978 98.68154) (xy 98.83902 98.73742) (xy 99.09048 98.8441) (xy 99.31908 98.9965) (xy 99.48418 99.16414)
+ (xy 103.71074 99.16414) (xy 103.87584 99.18192) (xy 103.95458 99.20732) (xy 104.0257 99.22764) (xy 104.17302 99.30638)
+ (xy 104.30002 99.41306) (xy 106.08056 101.1936) (xy 113.37544 101.1936) (xy 114.65814 99.9109) (xy 108.8263 99.9109)
+ (xy 108.6612 99.89312) (xy 108.51388 99.84486) (xy 108.51134 99.84486) (xy 108.5088 99.84232) (xy 108.50372 99.84232)
+ (xy 108.4453 99.8093) (xy 108.36402 99.76612) (xy 108.23702 99.66198) (xy 106.71048 98.13544) (xy 106.63682 98.044)
+ (xy 106.45902 98.044) (xy 106.3371 98.0186) (xy 106.2228 97.97034) (xy 106.1212 97.89922) (xy 106.0323 97.81032)
+ (xy 105.96372 97.70618) (xy 105.91546 97.59188) (xy 105.8926 97.46996) (xy 105.8926 97.23882) (xy 105.88244 97.25152)
+ (xy 104.99598 98.13798) (xy 104.99344 98.13798) (xy 104.99344 98.14052) (xy 104.8639 98.24466) (xy 104.85882 98.24466)
+ (xy 104.85628 98.24974) (xy 104.82072 98.26752) (xy 104.71912 98.3234) (xy 104.5591 98.37166) (xy 104.394 98.3869)
+ (xy 96.9899 98.3869) (xy 96.94672 98.4504) (xy 96.7486 98.6409) (xy 96.51746 98.78568) (xy 96.26346 98.88474)
+ (xy 95.99168 98.933) (xy 95.7199 98.92792) (xy 95.4532 98.8695) (xy 95.20174 98.76028) (xy 94.97822 98.6028)
+ (xy 94.78772 98.40722) (xy 94.63786 98.17608) (xy 94.5388 97.92208) (xy 94.488 97.65284) (xy 94.49308 97.38106)
+ (xy 94.54896 97.11182) (xy 94.65818 96.86036) (xy 94.81058 96.63684) (xy 95.00616 96.4438) (xy 95.23476 96.29394)
+ (xy 95.48876 96.19234) (xy 95.758 96.14154) (xy 95.92056 96.14154) (xy 95.92056 92.075) (xy 95.50146 92.04198)
+ (xy 95.0976 91.92514) (xy 94.72168 91.73464) (xy 94.60738 91.37142) (xy 95.885 90.09634) (xy 97.16262 91.37142)
+ (xy 97.04832 91.73464) (xy 96.74098 91.89974) (xy 96.33966 92.02674) (xy 95.92056 92.075) (xy 95.92056 96.14154)
+ (xy 96.02978 96.14154) (xy 96.29902 96.19742) (xy 96.55048 96.3041) (xy 96.77908 96.4565) (xy 96.96958 96.64954)
+ (xy 96.99244 96.6851) (xy 97.34042 96.6851) (xy 97.34042 91.19362) (xy 96.06534 89.916) (xy 97.34042 88.63838)
+ (xy 97.70364 88.75268) (xy 97.86874 89.06002) (xy 97.99574 89.46134) (xy 98.044 89.88044) (xy 98.01098 90.29954)
+ (xy 97.89414 90.7034) (xy 97.70364 91.07932) (xy 97.34042 91.19362) (xy 97.34042 96.6851) (xy 101.09454 96.6851)
+ (xy 101.09454 95.95612) (xy 100.77704 95.8977) (xy 100.67544 95.70212) (xy 100.56622 95.38462) (xy 100.5205 95.05442)
+ (xy 100.54336 94.71914) (xy 100.62972 94.39656) (xy 100.77704 94.0943) (xy 101.09454 94.03588) (xy 102.05466 94.996)
+ (xy 101.09454 95.95612) (xy 101.09454 96.6851) (xy 101.94798 96.6851) (xy 101.63556 96.60128) (xy 101.3333 96.45396)
+ (xy 101.27488 96.13646) (xy 102.235 95.17634) (xy 102.235 94.81566) (xy 101.27488 93.85554) (xy 101.3333 93.53804)
+ (xy 101.52888 93.43644) (xy 101.84638 93.32722) (xy 102.17658 93.2815) (xy 102.51186 93.30436) (xy 102.83444 93.39072)
+ (xy 103.1367 93.53804) (xy 103.19512 93.85554) (xy 102.235 94.81566) (xy 102.235 95.17634) (xy 103.19512 96.13646)
+ (xy 103.1367 96.45396) (xy 102.94112 96.55556) (xy 102.62362 96.66478) (xy 102.4763 96.6851) (xy 103.37546 96.6851)
+ (xy 103.37546 95.95612) (xy 102.41534 94.996) (xy 103.37546 94.03588) (xy 103.69296 94.0943) (xy 103.79456 94.28988)
+ (xy 103.90378 94.60738) (xy 103.9495 94.93758) (xy 103.92664 95.27286) (xy 103.84028 95.59544) (xy 103.69296 95.8977)
+ (xy 103.37546 95.95612) (xy 103.37546 96.6851) (xy 104.0384 96.6851) (xy 104.4321 96.29394) (xy 104.4321 93.7133)
+ (xy 104.44988 93.5482) (xy 104.47274 93.46692) (xy 104.4956 93.39834) (xy 104.57434 93.25102) (xy 104.68102 93.12402)
+ (xy 105.57002 92.23248) (xy 105.57764 92.2274) (xy 105.70718 92.12326) (xy 105.75798 92.09532) (xy 105.84688 92.0496)
+ (xy 106.0069 92.00134) (xy 106.172 91.9861) (xy 109.1057 91.9861) (xy 109.2708 92.00388) (xy 109.34954 92.02928)
+ (xy 109.42066 92.0496) (xy 109.56798 92.12834) (xy 109.69498 92.23502) (xy 110.4646 93.00464) (xy 110.56874 93.13418)
+ (xy 110.61192 93.22054) (xy 110.6424 93.27388) (xy 110.6424 93.27896) (xy 110.64494 93.2815) (xy 110.64748 93.29674)
+ (xy 110.69066 93.4339) (xy 110.7059 93.599) (xy 110.7059 94.7674) (xy 110.75924 94.8055) (xy 110.95736 95.00362)
+ (xy 111.10976 95.23476) (xy 111.125 95.27032) (xy 111.14786 95.21952) (xy 111.30534 94.99092) (xy 111.50346 94.79534)
+ (xy 111.5441 94.7674) (xy 111.5441 90.14206) (xy 111.49076 90.1065) (xy 111.29264 89.90838) (xy 111.14024 89.67724)
+ (xy 111.12246 89.63914) (xy 111.10214 89.69248) (xy 110.94466 89.92108) (xy 110.74654 90.11666) (xy 110.5154 90.26906)
+ (xy 110.25632 90.3732) (xy 109.98454 90.424) (xy 109.70768 90.424) (xy 109.4359 90.36812) (xy 109.17936 90.26144)
+ (xy 108.95076 90.1065) (xy 108.75264 89.90838) (xy 108.60024 89.67724) (xy 108.58246 89.63914) (xy 108.56214 89.69248)
+ (xy 108.40466 89.92108) (xy 108.20654 90.11666) (xy 107.9754 90.26906) (xy 107.71632 90.3732) (xy 107.44454 90.424)
+ (xy 107.16768 90.424) (xy 106.8959 90.36812) (xy 106.63936 90.26144) (xy 106.41076 90.1065) (xy 106.21264 89.90838)
+ (xy 106.06024 89.67724) (xy 106.02214 89.5858) (xy 99.6823 89.5858) (xy 99.49942 89.56548) (xy 99.33686 89.51214)
+ (xy 99.33178 89.51214) (xy 99.32924 89.5096) (xy 99.32416 89.5096) (xy 99.2251 89.45372) (xy 99.17176 89.42578)
+ (xy 99.16668 89.4207) (xy 99.16414 89.4207) (xy 99.08286 89.35212) (xy 99.02952 89.30894) (xy 97.90684 88.1888)
+ (xy 97.07626 88.1888) (xy 97.16262 88.46058) (xy 95.885 89.73566) (xy 94.60738 88.46058) (xy 94.6912 88.1888)
+ (xy 92.6084 88.1888) (xy 92.6084 98.88982) (xy 92.69476 98.83394) (xy 92.94876 98.73234) (xy 93.218 98.68154)
+ (xy 93.48978 98.68154) (xy 93.75902 98.73742) (xy 94.01048 98.8441) (xy 94.23908 98.9965) (xy 94.42958 99.18954)
+ (xy 94.42958 91.19362) (xy 94.06636 91.07932) (xy 93.90126 90.77198) (xy 93.77426 90.37066) (xy 93.726 89.95156)
+ (xy 93.75902 89.53246) (xy 93.87586 89.1286) (xy 94.06636 88.75268) (xy 94.42958 88.63838) (xy 95.70466 89.916)
+ (xy 94.42958 91.19362) (xy 94.42958 99.18954) (xy 94.58198 99.41814) (xy 94.68612 99.6696) (xy 94.73946 99.93884)
+ (xy 94.73438 100.25126) (xy 94.73184 100.25888) (xy 96.23552 101.7651) (xy 101.04882 101.7651) (xy 101.11232 101.67366)
+ (xy 101.31806 101.473) (xy 101.55682 101.31806) (xy 101.82098 101.21138) (xy 102.10292 101.15804) (xy 102.38994 101.15804)
+ (xy 102.66934 101.21646) (xy 102.9335 101.32822) (xy 103.16972 101.48824) (xy 103.37038 101.69144) (xy 103.52786 101.92766)
+ (xy 103.63708 102.19436) (xy 103.69296 102.47376) (xy 103.68788 102.80142) (xy 103.62438 103.07828) (xy 103.50754 103.3399)
+ (xy 103.34244 103.57358) (xy 103.1367 103.7717) (xy 102.8954 103.9241) (xy 102.6287 104.02824) (xy 102.34676 104.0765)
+ (xy 102.05974 104.07142) (xy 101.78288 104.00792) (xy 101.51872 103.89362) (xy 101.28504 103.73106) (xy 101.08692 103.52532)
+ (xy 101.04882 103.46436) (xy 95.87992 103.4669) (xy 95.8723 103.4669) (xy 95.7072 103.44912) (xy 95.63608 103.42626)
+ (xy 95.55734 103.4034) (xy 95.41002 103.32212) (xy 95.28302 103.21798) (xy 93.5228 101.45776) (xy 93.45168 101.473)
+ (xy 93.1799 101.46792) (xy 92.9132 101.4095) (xy 92.66174 101.30028) (xy 92.6084 101.26218) (xy 92.6084 112.36198)
+ (xy 92.71 112.46358) (xy 92.71 110.16742) (xy 92.7354 110.0455) (xy 92.78366 109.9312) (xy 92.85478 109.8296)
+ (xy 92.94368 109.7407) (xy 93.04782 109.67212) (xy 93.16212 109.62386) (xy 93.28404 109.601) (xy 93.7641 109.601)
+ (xy 93.7641 108.1913) (xy 93.78188 108.0262) (xy 93.80474 107.94492) (xy 93.8276 107.87634) (xy 93.90634 107.72902)
+ (xy 94.01302 107.60202) (xy 95.16364 106.4514) (xy 95.29318 106.34726) (xy 95.37954 106.30154) (xy 95.43288 106.2736)
+ (xy 95.43796 106.27106) (xy 95.4405 106.27106) (xy 95.45574 106.26598) (xy 95.5929 106.22534) (xy 95.758 106.2101)
+ (xy 101.04882 106.2101) (xy 101.11232 106.11866) (xy 101.31806 105.918) (xy 101.55682 105.76306) (xy 101.82098 105.65638)
+ (xy 102.10292 105.60304) (xy 102.38994 105.60304) (xy 102.66934 105.66146) (xy 102.9335 105.77322) (xy 103.16972 105.93324)
+ (xy 103.37038 106.13644) (xy 103.52786 106.37266) (xy 103.63708 106.63936) (xy 103.69296 106.91876) (xy 103.68788 107.24642)
+ (xy 103.62438 107.52328) (xy 103.50754 107.7849) (xy 103.34244 108.01858) (xy 103.1367 108.2167) (xy 102.8954 108.3691)
+ (xy 102.6287 108.47324) (xy 102.34676 108.5215) (xy 102.05974 108.51642) (xy 101.78288 108.45292) (xy 101.51872 108.33862)
+ (xy 101.28504 108.17606) (xy 101.08692 107.97032) (xy 101.04882 107.9119) (xy 96.10852 107.9119) (xy 95.4659 108.55706)
+ (xy 95.4659 109.601) (xy 95.95358 109.601) (xy 96.0755 109.6264) (xy 96.1898 109.67466) (xy 96.2914 109.74578)
+ (xy 96.3803 109.83468) (xy 96.44888 109.93882) (xy 96.49714 110.05312) (xy 96.52 110.17504) (xy 96.52 112.84458)
+ (xy 96.4946 112.9665) (xy 96.44634 113.0808) (xy 96.37522 113.1824) (xy 96.28632 113.2713) (xy 96.18218 113.33988)
+ (xy 96.06788 113.38814) (xy 95.94596 113.411) (xy 95.6945 113.411) (xy 97.9297 115.6462) (xy 106.0196 115.6462)
+ (xy 106.06786 115.53952) (xy 106.22534 115.31092) (xy 106.42346 115.11534) (xy 106.6546 114.96294) (xy 106.91368 114.8588)
+ (xy 107.18546 114.808) (xy 107.46232 114.808) (xy 107.7341 114.86388) (xy 107.99064 114.97056) (xy 108.21924 115.1255)
+ (xy 108.41736 115.32362) (xy 108.56976 115.55476) (xy 108.585 115.59032) (xy 108.60786 115.53952) (xy 108.76534 115.31092)
+ (xy 108.96346 115.11534) (xy 109.1946 114.96294) (xy 109.45368 114.8588) (xy 109.72546 114.808) (xy 110.00232 114.808)
+ (xy 110.2741 114.86388) (xy 110.53064 114.97056) (xy 110.75924 115.1255) (xy 110.95736 115.32362) (xy 111.10976 115.55476)
+ (xy 111.125 115.59032) (xy 111.14786 115.53952) (xy 111.30534 115.31092) (xy 111.50346 115.11534) (xy 111.7346 114.96294)
+ (xy 111.99368 114.8588) (xy 112.26546 114.808) (xy 112.54232 114.808) (xy 112.8141 114.86388) (xy 113.07064 114.97056)
+ (xy 113.29924 115.1255) (xy 113.49736 115.32362) (xy 113.64976 115.55476) (xy 113.665 115.59032) (xy 113.68786 115.53952)
+ (xy 113.84534 115.31092) (xy 114.04346 115.11534) (xy 114.0841 115.0874) (xy 114.0841 114.39906) (xy 113.78946 114.10442)
+ (xy 113.7666 114.1095) (xy 113.50498 114.10442) (xy 113.25098 114.04854) (xy 113.01222 113.94186) (xy 112.79632 113.79454)
+ (xy 112.63376 113.6269) (xy 111.0361 113.6269) (xy 110.96244 113.73358) (xy 110.7567 113.9317) (xy 110.5154 114.0841)
+ (xy 110.2487 114.18824) (xy 109.96676 114.2365) (xy 109.67974 114.23142) (xy 109.40288 114.16792) (xy 109.13872 114.05362)
+ (xy 108.90504 113.89106) (xy 108.70692 113.68532) (xy 108.55198 113.44402) (xy 108.44784 113.17986) (xy 108.3945 112.89792)
+ (xy 108.39958 112.6109) (xy 108.458 112.3315) (xy 108.5723 112.06988) (xy 108.73232 111.83366) (xy 108.93806 111.633)
+ (xy 109.12856 111.506) (xy 108.90504 111.35106) (xy 108.70692 111.14532) (xy 108.66882 111.0869) (xy 105.12806 111.0869)
+ (xy 103.67264 112.53978) (xy 103.69296 112.63376) (xy 103.68788 112.96142) (xy 103.62438 113.23828) (xy 103.50754 113.4999)
+ (xy 103.34244 113.73358) (xy 103.1367 113.9317) (xy 102.8954 114.0841) (xy 102.6287 114.18824) (xy 102.34676 114.2365)
+ (xy 102.05974 114.23142) (xy 101.78288 114.16792) (xy 101.51872 114.05362) (xy 101.28504 113.89106) (xy 101.08692 113.68532)
+ (xy 100.93198 113.44402) (xy 100.82784 113.17986) (xy 100.7745 112.89792) (xy 100.77958 112.6109) (xy 100.838 112.3315)
+ (xy 100.9523 112.06988) (xy 101.11232 111.83366) (xy 101.31806 111.633) (xy 101.50856 111.506) (xy 101.28504 111.35106)
+ (xy 101.08692 111.14532) (xy 100.93198 110.90402) (xy 100.82784 110.63986) (xy 100.7745 110.35792) (xy 100.77958 110.0709)
+ (xy 100.838 109.7915) (xy 100.9523 109.52988) (xy 101.11232 109.29366) (xy 101.31806 109.093) (xy 101.55682 108.93806)
+ (xy 101.82098 108.83138) (xy 102.10292 108.77804) (xy 102.38994 108.77804) (xy 102.66934 108.83646) (xy 102.9335 108.94822)
+ (xy 103.01986 109.00664) (xy 105.55986 106.46918) (xy 105.57256 106.45648) (xy 105.5751 106.4514) (xy 105.57764 106.4514)
+ (xy 105.70718 106.34726) (xy 105.79354 106.30154) (xy 105.84688 106.2736) (xy 105.85196 106.27106) (xy 105.8545 106.27106)
+ (xy 105.85958 106.26852) (xy 106.0069 106.22534) (xy 106.172 106.2101) (xy 108.66882 106.2101) (xy 108.73232 106.11866)
+ (xy 108.93806 105.918) (xy 109.17682 105.76306) (xy 109.44098 105.65638) (xy 109.72292 105.60304) (xy 110.00994 105.60304)
+ (xy 110.28934 105.66146) (xy 110.5535 105.77322) (xy 110.78972 105.93324) (xy 110.99038 106.13644) (xy 111.03864 106.2101)
+ (xy 112.63376 106.20756) (xy 112.64138 106.19994) (xy 112.8268 106.01706) (xy 113.04524 105.87482) (xy 113.28908 105.7783)
+ (xy 113.54562 105.73004) (xy 113.8047 105.73004) (xy 114.06124 105.78338) (xy 114.30254 105.88498) (xy 114.51844 106.0323)
+ (xy 114.70132 106.21772) (xy 114.8461 106.43362) (xy 114.94516 106.67492) (xy 114.99596 106.93146) (xy 114.99088 107.22864)
+ (xy 114.93246 107.48518) (xy 114.82832 107.7214) (xy 114.67592 107.93476) (xy 114.48796 108.1151) (xy 114.26698 108.2548)
+ (xy 114.02314 108.34878) (xy 113.7666 108.3945) (xy 113.50498 108.38942) (xy 113.25098 108.33354) (xy 113.01222 108.22686)
+ (xy 112.79632 108.07954) (xy 112.63376 107.9119) (xy 111.0361 107.9119) (xy 110.96244 108.01858) (xy 110.7567 108.2167)
+ (xy 110.5154 108.3691) (xy 110.2487 108.47324) (xy 109.96676 108.5215) (xy 109.67974 108.51642) (xy 109.40288 108.45292)
+ (xy 109.13872 108.33862) (xy 108.90504 108.17606) (xy 108.70692 107.97032) (xy 108.66882 107.9119) (xy 106.52252 107.9119)
+ (xy 105.04932 109.3851) (xy 108.66882 109.3851) (xy 108.73232 109.29366) (xy 108.93806 109.093) (xy 109.17682 108.93806)
+ (xy 109.44098 108.83138) (xy 109.72292 108.77804) (xy 110.00994 108.77804) (xy 110.28934 108.83646) (xy 110.5535 108.94822)
+ (xy 110.78972 109.10824) (xy 110.99038 109.31144) (xy 111.03864 109.3851) (xy 112.63376 109.3851) (xy 112.64138 109.37494)
+ (xy 112.8268 109.19206) (xy 113.04524 109.04982) (xy 113.28908 108.9533) (xy 113.54562 108.90504) (xy 113.8047 108.90504)
+ (xy 114.06124 108.95838) (xy 114.30254 109.05998) (xy 114.51844 109.2073) (xy 114.70132 109.39272) (xy 114.8461 109.60862)
+ (xy 114.94516 109.84992) (xy 114.99596 110.10646) (xy 114.99088 110.40364) (xy 114.93246 110.66018) (xy 114.82832 110.8964)
+ (xy 114.67592 111.10976) (xy 114.48796 111.2901) (xy 114.26698 111.4298) (xy 114.07394 111.50346) (xy 114.30254 111.59998)
+ (xy 114.51844 111.7473) (xy 114.70132 111.93272) (xy 114.8461 112.14862) (xy 114.94516 112.38992) (xy 114.99596 112.64646)
+ (xy 114.99088 112.89792) (xy 115.53952 113.44656) (xy 115.59794 113.51768) (xy 115.64874 113.58118) (xy 115.69192 113.66754)
+ (xy 115.7224 113.72088) (xy 115.7224 113.72596) (xy 115.72494 113.7285) (xy 115.72748 113.74374) (xy 115.77066 113.8809)
+ (xy 115.7859 114.046) (xy 115.7859 115.0874) (xy 115.83924 115.1255) (xy 116.03736 115.32362) (xy 116.18976 115.55476)
+ (xy 116.29644 115.8113) (xy 116.34978 116.08308) (xy 116.35232 116.82984) (xy 116.34724 117.1067) (xy 116.29136 117.37848)
+ (xy 116.18214 117.63248) (xy 116.02466 117.86108) (xy 115.82654 118.05666) (xy 115.5954 118.20906) (xy 115.33632 118.3132)
+ (xy 115.06454 118.364) (xy 114.78768 118.364) (xy 114.5159 118.30812) (xy 114.25936 118.20144) (xy 114.03076 118.0465)
+ (xy 113.83264 117.84838) (xy 113.68024 117.61724) (xy 113.66246 117.57914) (xy 113.64214 117.63248) (xy 113.48466 117.86108)
+ (xy 113.28654 118.05666) (xy 113.2459 118.08206) (xy 113.2459 120.3071) (xy 116.35994 120.3071) (xy 117.7671 118.89994)
+ (xy 117.7671 116.04752) (xy 116.11102 114.39398) (xy 116.10848 114.39144) (xy 116.00434 114.2619) (xy 116.0018 114.25682)
+ (xy 115.99926 114.25428) (xy 115.97894 114.21872) (xy 115.9256 114.11712) (xy 115.87734 113.9571) (xy 115.8621 113.792)
+ (xy 115.8621 111.4933) (xy 115.87988 111.3282) (xy 115.90274 111.24946) (xy 115.9256 111.17834) (xy 116.00434 111.03102)
+ (xy 116.11102 110.90402) (xy 117.38102 109.63402) (xy 117.38102 109.63148) (xy 117.38356 109.63148) (xy 117.45468 109.57052)
+ (xy 117.51818 109.52226) (xy 117.60454 109.47654) (xy 117.65788 109.4486) (xy 117.66296 109.44606) (xy 117.6655 109.44606)
+ (xy 117.67312 109.44098) (xy 117.69344 109.41304) (xy 117.69344 107.93222) (xy 117.39118 107.8865) (xy 117.30482 107.7214)
+ (xy 117.20322 107.4293) (xy 117.16004 107.12196) (xy 117.17782 106.81208) (xy 117.25656 106.51236) (xy 117.39118 106.2355)
+ (xy 117.69344 106.18978) (xy 118.56466 107.061) (xy 117.69344 107.93222) (xy 117.69344 109.41304) (xy 117.72138 109.37494)
+ (xy 117.9068 109.19206) (xy 118.12524 109.04982) (xy 118.36908 108.9533) (xy 118.62562 108.90504) (xy 118.80596 108.90504)
+ (xy 118.80596 108.64596) (xy 118.49608 108.62818) (xy 118.19636 108.54944) (xy 117.9195 108.41482) (xy 117.87378 108.11256)
+ (xy 118.745 107.24134) (xy 118.745 106.88066) (xy 117.87378 106.00944) (xy 117.9195 105.70718) (xy 118.04142 105.64114)
+ (xy 118.04142 103.0605) (xy 117.70614 103.03764) (xy 117.38356 102.95128) (xy 117.0813 102.80396) (xy 117.02288 102.48646)
+ (xy 117.983 101.52634) (xy 118.94312 102.48646) (xy 118.8847 102.80396) (xy 118.68912 102.90556) (xy 118.37162 103.01478)
+ (xy 118.04142 103.0605) (xy 118.04142 105.64114) (xy 118.0846 105.62082) (xy 118.3767 105.51922) (xy 118.68404 105.47604)
+ (xy 118.99392 105.49382) (xy 119.12346 105.52684) (xy 119.12346 102.30612) (xy 118.16334 101.346) (xy 119.12346 100.38588)
+ (xy 119.44096 100.4443) (xy 119.54256 100.63988) (xy 119.65178 100.95738) (xy 119.6975 101.28758) (xy 119.67464 101.62286)
+ (xy 119.58828 101.94544) (xy 119.44096 102.2477) (xy 119.12346 102.30612) (xy 119.12346 105.52684) (xy 119.29364 105.57256)
+ (xy 119.5705 105.70718) (xy 119.61622 106.00944) (xy 118.745 106.88066) (xy 118.745 107.24134) (xy 119.61622 108.11256)
+ (xy 119.5705 108.41482) (xy 119.4054 108.50118) (xy 119.1133 108.60278) (xy 118.80596 108.64596) (xy 118.80596 108.90504)
+ (xy 118.8847 108.90504) (xy 119.14124 108.95838) (xy 119.38254 109.05998) (xy 119.59844 109.2073) (xy 119.78132 109.39272)
+ (xy 119.79656 109.41304) (xy 119.79656 107.93222) (xy 118.92534 107.061) (xy 119.79656 106.18978) (xy 120.09882 106.2355)
+ (xy 120.18518 106.4006) (xy 120.28678 106.6927) (xy 120.32996 107.00004) (xy 120.31218 107.30992) (xy 120.23344 107.60964)
+ (xy 120.09882 107.8865) (xy 119.79656 107.93222) (xy 119.79656 109.41304) (xy 119.9261 109.60862) (xy 120.02516 109.84992)
+ (xy 120.07596 110.10646) (xy 120.07088 110.40364) (xy 120.01246 110.66018) (xy 119.90832 110.8964) (xy 119.75592 111.10976)
+ (xy 119.56796 111.2901) (xy 119.44858 111.36376) (xy 119.5705 111.42218) (xy 119.61622 111.72444) (xy 118.745 112.59566)
+ (xy 118.56466 112.776) (xy 118.745 112.95634) (xy 119.61622 113.82756) (xy 119.5705 114.12982) (xy 119.4054 114.21618)
+ (xy 119.1133 114.31778) (xy 118.80596 114.36096) (xy 118.49608 114.34318) (xy 118.45544 114.33048) (xy 119.20728 115.08486)
+ (xy 119.22252 115.09756) (xy 119.2276 115.10264) (xy 119.33174 115.23218) (xy 119.37492 115.31854) (xy 119.4054 115.37188)
+ (xy 119.4054 115.37696) (xy 119.40794 115.3795) (xy 119.40794 115.38458) (xy 119.45366 115.5319) (xy 119.4689 115.697)
+ (xy 119.4689 119.2657) (xy 119.45112 119.4308) (xy 119.40286 119.57558) (xy 119.40286 119.58066) (xy 119.40032 119.58066)
+ (xy 119.40032 119.58828) (xy 119.3673 119.64416) (xy 119.32412 119.72798) (xy 119.21744 119.85498) (xy 117.31498 121.75998)
+ (xy 117.31244 121.75998) (xy 117.31244 121.76252) (xy 117.1829 121.86666) (xy 117.17782 121.86666) (xy 117.17528 121.87174)
+ (xy 117.13972 121.88952) (xy 117.03812 121.9454) (xy 116.8781 121.99366) (xy 116.713 122.0089) (xy 113.2459 122.0089)
+ (xy 113.2459 122.7074) (xy 113.29924 122.7455) (xy 113.49736 122.94362) (xy 113.64976 123.17476) (xy 113.665 123.21032)
+ (xy 113.68786 123.15952) (xy 113.84534 122.93092) (xy 114.04346 122.73534) (xy 114.2746 122.58294) (xy 114.53368 122.4788)
+ (xy 114.80546 122.428) (xy 115.08232 122.428) (xy 115.3541 122.48388) (xy 115.61064 122.59056) (xy 115.83924 122.7455)
+ (xy 116.03736 122.94362) (xy 116.18976 123.17476) (xy 116.29644 123.4313) (xy 116.34978 123.70308) (xy 116.35232 124.44984)
+ (xy 116.34724 124.7267) (xy 116.30406 124.93498) (xy 116.74856 124.49048) (xy 116.7511 124.4854) (xy 116.75364 124.4854)
+ (xy 116.88318 124.38126) (xy 116.96954 124.33554) (xy 117.02288 124.3076) (xy 117.02796 124.30506) (xy 117.0305 124.30506)
+ (xy 117.03558 124.30252) (xy 117.1829 124.25934) (xy 117.348 124.2441) (xy 119.79656 124.2441) (xy 119.79656 113.64722)
+ (xy 118.92534 112.776) (xy 119.79656 111.90478) (xy 120.09882 111.9505) (xy 120.18518 112.1156) (xy 120.28678 112.4077)
+ (xy 120.32996 112.71504) (xy 120.31218 113.02492) (xy 120.23344 113.32464) (xy 120.09882 113.6015) (xy 119.79656 113.64722)
+ (xy 119.79656 124.2441) (xy 121.41454 124.2441) (xy 121.41454 123.89612) (xy 121.09704 123.8377) (xy 120.99544 123.64212)
+ (xy 120.88622 123.32462) (xy 120.8405 122.99442) (xy 120.86336 122.65914) (xy 120.94972 122.33656) (xy 121.09704 122.0343)
+ (xy 121.41454 121.97588) (xy 122.37466 122.936) (xy 121.41454 123.89612) (xy 121.41454 124.2441) (xy 121.62536 124.2441)
+ (xy 121.59488 124.07646) (xy 122.555 123.11634) (xy 122.555 122.75566) (xy 121.59488 121.79554) (xy 121.6533 121.47804)
+ (xy 121.84888 121.37644) (xy 122.16638 121.26722) (xy 122.49658 121.2215) (xy 122.83186 121.24436) (xy 123.15444 121.33072)
+ (xy 123.4567 121.47804) (xy 123.51512 121.79554) (xy 122.555 122.75566) (xy 122.555 123.11634) (xy 122.73534 122.936)
+ (xy 123.69546 121.97588) (xy 124.01296 122.0343) (xy 124.11456 122.22988) (xy 124.22378 122.54738) (xy 124.2695 122.87758)
+ (xy 124.25426 123.0757) (xy 126.01194 121.32056) (xy 125.92304 121.25706) (xy 125.72492 121.05132) (xy 125.56998 120.81002)
+ (xy 125.46584 120.54586) (xy 125.4125 120.26392) (xy 125.41758 119.9769) (xy 125.476 119.6975) (xy 125.5903 119.43588)
+ (xy 125.75032 119.19966) (xy 125.95606 118.999) (xy 126.19482 118.84406) (xy 126.45898 118.73738) (xy 126.74092 118.68404)
+ (xy 127.02794 118.68404) (xy 127.30734 118.74246) (xy 127.5715 118.85422) (xy 127.80772 119.01424) (xy 128.00838 119.21744)
+ (xy 128.16586 119.45366) (xy 128.27508 119.72036) (xy 128.33096 119.99976) (xy 128.32588 120.32742) (xy 128.30048 120.42902)
+ (xy 130.81 117.9195) (xy 129.40792 117.9195) (xy 129.286 117.8941) (xy 129.1717 117.84584) (xy 129.0701 117.77472)
+ (xy 128.9812 117.68582) (xy 128.91262 117.58168) (xy 128.86436 117.46738) (xy 128.8415 117.34546) (xy 128.8415 115.81892)
+ (xy 128.8669 115.697) (xy 128.91516 115.5827) (xy 128.98628 115.4811) (xy 129.07518 115.3922) (xy 129.17932 115.32362)
+ (xy 129.29362 115.27536) (xy 129.33426 115.2652) (xy 129.34188 115.20678) (xy 129.37236 115.10772) (xy 129.30632 115.06454)
+ (xy 129.12598 114.87658) (xy 128.98628 114.6556) (xy 128.88976 114.4143) (xy 128.8415 114.15776) (xy 128.84658 113.89614)
+ (xy 128.89992 113.6396) (xy 129.00406 113.40084) (xy 129.15138 113.18494) (xy 129.2987 113.03508) (xy 127.63246 111.36884)
+ (xy 123.98248 115.02136) (xy 124.01296 115.17376) (xy 124.00788 115.50142) (xy 123.94438 115.77828) (xy 123.82754 116.0399)
+ (xy 123.66244 116.27358) (xy 123.4567 116.4717) (xy 123.2154 116.6241) (xy 122.9487 116.72824) (xy 122.66676 116.7765)
+ (xy 122.37974 116.77142) (xy 122.10288 116.70792) (xy 121.83872 116.59362) (xy 121.60504 116.43106) (xy 121.40692 116.22532)
+ (xy 121.25198 115.98402) (xy 121.14784 115.71986) (xy 121.0945 115.43792) (xy 121.09958 115.1509) (xy 121.158 114.8715)
+ (xy 121.2723 114.60988) (xy 121.43232 114.37366) (xy 121.63806 114.173) (xy 121.87682 114.01806) (xy 122.14098 113.91138)
+ (xy 122.42292 113.85804) (xy 122.70994 113.85804) (xy 122.7328 113.86312) (xy 126.48438 110.109) (xy 126.4412 110.06582)
+ (xy 126.37262 109.96168) (xy 126.32436 109.84738) (xy 126.3015 109.72546) (xy 126.3015 108.19892) (xy 126.3269 108.077)
+ (xy 126.37516 107.9627) (xy 126.44628 107.8611) (xy 126.53518 107.7722) (xy 126.63932 107.70362) (xy 126.65456 107.696)
+ (xy 126.6317 107.68584) (xy 126.5301 107.61472) (xy 126.4412 107.52582) (xy 126.37262 107.42168) (xy 126.32436 107.30738)
+ (xy 126.3015 107.18546) (xy 126.3015 105.65892) (xy 126.3269 105.537) (xy 126.37516 105.4227) (xy 126.44628 105.3211)
+ (xy 126.53518 105.2322) (xy 126.63932 105.16362) (xy 126.75362 105.11536) (xy 126.87554 105.0925) (xy 128.40208 105.0925)
+ (xy 128.524 105.1179) (xy 128.6383 105.16616) (xy 128.7399 105.23728) (xy 128.8288 105.32618) (xy 128.89738 105.43032)
+ (xy 128.94564 105.54462) (xy 128.95326 105.58526) (xy 129.01422 105.59288) (xy 129.09296 105.61828) (xy 129.16408 105.6386)
+ (xy 129.3114 105.71734) (xy 129.4384 105.82402) (xy 131.72694 108.1151) (xy 134.22376 108.1151) (xy 134.23138 108.10494)
+ (xy 134.4168 107.92206) (xy 134.63524 107.77982) (xy 134.83844 107.696) (xy 134.60222 107.59186) (xy 134.38632 107.44454)
+ (xy 134.20598 107.25658) (xy 134.06628 107.0356) (xy 133.96976 106.7943) (xy 133.9215 106.53776) (xy 133.92658 106.27614)
+ (xy 133.97992 106.0196) (xy 134.08406 105.78084) (xy 134.23138 105.56494) (xy 134.4168 105.38206) (xy 134.63524 105.23982)
+ (xy 134.87908 105.1433) (xy 135.13562 105.09504) (xy 135.3947 105.09504) (xy 135.65124 105.14838) (xy 135.89254 105.24998)
+ (xy 136.10844 105.3973) (xy 136.1948 105.4862) (xy 140.82268 105.4862) (xy 140.84554 105.48366) (xy 141.02842 105.50398)
+ (xy 141.0335 105.50398) (xy 141.03858 105.50652) (xy 141.07668 105.51668) (xy 141.20368 105.55732) (xy 141.2113 105.55986)
+ (xy 141.21384 105.5624) (xy 141.22654 105.56748) (xy 141.36624 105.64368) (xy 141.50848 105.76052) (xy 142.07744 106.32948)
+ (xy 142.07744 105.01122) (xy 141.77518 104.9655) (xy 141.68882 104.8004) (xy 141.58722 104.5083) (xy 141.54404 104.20096)
+ (xy 141.56182 103.89108) (xy 141.64056 103.59136) (xy 141.77518 103.3145) (xy 142.07744 103.26878) (xy 142.94866 104.14)
+ (xy 142.07744 105.01122) (xy 142.07744 106.32948) (xy 143.18996 107.442) (xy 143.18996 105.72496) (xy 142.88008 105.70718)
+ (xy 142.58036 105.62844) (xy 142.3035 105.49382) (xy 142.25778 105.19156) (xy 143.129 104.32034) (xy 143.129 103.95966)
+ (xy 142.25778 103.08844) (xy 142.3035 102.78618) (xy 142.4686 102.69982) (xy 142.7607 102.59822) (xy 143.06804 102.55504)
+ (xy 143.37792 102.57282) (xy 143.67764 102.65156) (xy 143.9545 102.78618) (xy 144.00022 103.08844) (xy 143.129 103.95966)
+ (xy 143.129 104.32034) (xy 144.00022 105.19156) (xy 143.9545 105.49382) (xy 143.7894 105.58018) (xy 143.4973 105.68178)
+ (xy 143.18996 105.72496) (xy 143.18996 107.442) (xy 143.77162 108.0262) (xy 144.18056 108.0262) (xy 144.18056 105.01122)
+ (xy 143.30934 104.14) (xy 144.18056 103.26878) (xy 144.48282 103.3145) (xy 144.56918 103.4796) (xy 144.67078 103.7717)
+ (xy 144.71396 104.07904) (xy 144.69618 104.38892) (xy 144.61744 104.68864) (xy 144.48282 104.9655) (xy 144.18056 105.01122)
+ (xy 144.18056 108.0262) (xy 145.55978 108.0262) (xy 145.56232 108.02366) (xy 145.76806 107.823) (xy 146.00682 107.66806)
+ (xy 146.27098 107.56138) (xy 146.55292 107.50804) (xy 146.83994 107.50804) (xy 147.11934 107.56646) (xy 147.3835 107.67822)
+ (xy 147.61972 107.83824) (xy 147.82038 108.04144) (xy 147.97786 108.27766) (xy 148.08708 108.54436) (xy 148.14296 108.82376)
+ (xy 148.13788 109.08792) (xy 151.58974 112.53978) (xy 151.60498 112.55248) (xy 152.41778 113.3729) (xy 152.43302 113.3856)
+ (xy 152.54732 113.52784) (xy 152.55494 113.538) (xy 152.60066 113.63198) (xy 152.63368 113.6904) (xy 152.63368 113.69548)
+ (xy 152.63876 113.7031) (xy 152.64892 113.74374) (xy 152.68702 113.86566) (xy 152.7048 114.04854) (xy 152.7048 114.37366)
+ (xy 152.80132 114.47272) (xy 152.9461 114.68862) (xy 153.04516 114.92992) (xy 153.09596 115.18646) (xy 153.09088 115.48364)
+ (xy 153.03246 115.74018) (xy 152.92832 115.9764) (xy 152.77592 116.18976) (xy 152.58796 116.3701) (xy 152.36698 116.5098)
+ (xy 152.12314 116.60378) (xy 151.8666 116.6495) (xy 151.60498 116.64442) (xy 151.35098 116.58854) (xy 151.11222 116.48186)
+ (xy 150.89632 116.33454) (xy 150.71598 116.14658) (xy 150.57628 115.9256) (xy 150.495 115.7224) (xy 150.49246 115.74018)
+ (xy 150.38832 115.9764) (xy 150.23592 116.18976) (xy 150.04796 116.3701) (xy 149.82698 116.5098) (xy 149.79396 116.5225)
+ (xy 149.99208 116.5225) (xy 150.114 116.5479) (xy 150.2283 116.59616) (xy 150.3299 116.66728) (xy 150.4188 116.75618)
+ (xy 150.48738 116.86032) (xy 150.53564 116.97462) (xy 150.54072 117.0051) (xy 152.0444 117.0051) (xy 155.61056 113.44148)
+ (xy 155.61564 113.4364) (xy 155.74518 113.33226) (xy 155.83154 113.28654) (xy 155.88488 113.2586) (xy 155.88996 113.25606)
+ (xy 155.8925 113.25606) (xy 155.9052 113.25098) (xy 156.0449 113.21034) (xy 156.21 113.1951) (xy 163.8427 113.1951)
+ (xy 164.0078 113.21288) (xy 164.084 113.23828) (xy 164.15766 113.2586) (xy 164.30498 113.33734) (xy 164.43198 113.44402)
+ (xy 166.32428 115.33886) (xy 166.33952 115.35156) (xy 166.3446 115.35664) (xy 166.44874 115.48618) (xy 166.49192 115.57254)
+ (xy 166.5224 115.62588) (xy 166.5224 115.63096) (xy 166.52494 115.6335) (xy 166.52494 115.63858) (xy 166.57066 115.7859)
+ (xy 166.5859 115.951) (xy 166.5859 117.93982) (xy 166.66972 117.99824) (xy 166.87038 118.20144) (xy 167.02786 118.43766)
+ (xy 167.13708 118.70436) (xy 167.19296 118.98376) (xy 167.18788 119.31142) (xy 167.12438 119.58828) (xy 167.00754 119.8499)
+ (xy 166.84244 120.08358) (xy 166.6367 120.2817) (xy 166.3954 120.4341) (xy 166.1287 120.53824) (xy 165.84676 120.5865)
+ (xy 165.55974 120.58142) (xy 165.28288 120.51792) (xy 165.01872 120.40362) (xy 164.78504 120.24106) (xy 164.58692 120.03532)
+ (xy 164.54882 119.9769) (xy 162.27552 119.9769) (xy 160.8709 121.38152) (xy 160.8709 123.01982) (xy 160.95472 123.07824)
+ (xy 161.15538 123.28144) (xy 161.31286 123.51766) (xy 161.42208 123.78436) (xy 161.47796 124.06376) (xy 161.47288 124.39142)
+ (xy 161.40938 124.66828) (xy 161.29254 124.9299) (xy 161.12744 125.16358) (xy 160.9217 125.3617) (xy 160.6804 125.5141)
+ (xy 160.4137 125.61824) (xy 160.13176 125.6665) (xy 159.84474 125.66142) (xy 159.56788 125.59792) (xy 159.30372 125.48362)
+ (xy 159.07004 125.32106) (xy 158.87192 125.11532) (xy 158.71698 124.87402) (xy 158.61284 124.60986) (xy 158.5595 124.32792)
+ (xy 158.56458 124.0409) (xy 158.623 123.7615) (xy 158.7373 123.49988) (xy 158.89732 123.26366) (xy 159.10306 123.063)
+ (xy 159.1691 123.01982) (xy 159.1691 121.0183) (xy 159.18688 120.8532) (xy 159.20974 120.77446) (xy 159.2326 120.70334)
+ (xy 159.31134 120.55602) (xy 159.41802 120.42902) (xy 161.32556 118.52148) (xy 161.3281 118.5164) (xy 161.33064 118.5164)
+ (xy 161.46018 118.41226) (xy 161.54654 118.36654) (xy 161.59988 118.3386) (xy 161.60496 118.33606) (xy 161.6075 118.33606)
+ (xy 161.61258 118.33352) (xy 161.7599 118.29034) (xy 161.925 118.2751) (xy 164.54882 118.2751) (xy 164.61232 118.18366)
+ (xy 164.81806 117.983) (xy 164.8841 117.93982) (xy 164.8841 116.30152) (xy 163.47694 114.8969) (xy 156.56052 114.8969)
+ (xy 156.32938 115.12804) (xy 156.36494 115.12804) (xy 156.64434 115.18646) (xy 156.9085 115.29822) (xy 157.14472 115.45824)
+ (xy 157.34538 115.66144) (xy 157.39364 115.7351) (xy 158.83382 115.7351) (xy 158.89732 115.64366) (xy 159.10306 115.443)
+ (xy 159.34182 115.28806) (xy 159.60598 115.18138) (xy 159.88792 115.12804) (xy 160.17494 115.12804) (xy 160.45434 115.18646)
+ (xy 160.7185 115.29822) (xy 160.95472 115.45824) (xy 161.15538 115.66144) (xy 161.31286 115.89766) (xy 161.42208 116.16436)
+ (xy 161.47796 116.44376) (xy 161.47288 116.77142) (xy 161.40938 117.04828) (xy 161.29254 117.3099) (xy 161.12744 117.54358)
+ (xy 160.9217 117.7417) (xy 160.6804 117.8941) (xy 160.4137 117.99824) (xy 160.13176 118.0465) (xy 159.84474 118.04142)
+ (xy 159.56788 117.97792) (xy 159.30372 117.86362) (xy 159.07004 117.70106) (xy 158.87192 117.49532) (xy 158.83382 117.4369)
+ (xy 157.3911 117.4369) (xy 157.31744 117.54358) (xy 157.1117 117.7417) (xy 156.8704 117.8941) (xy 156.6037 117.99824)
+ (xy 156.32176 118.0465) (xy 156.03474 118.04142) (xy 155.97124 118.02618) (xy 153.00198 120.99798) (xy 152.99944 120.99798)
+ (xy 152.99944 121.00052) (xy 152.8699 121.10466) (xy 152.86482 121.10466) (xy 152.86228 121.10974) (xy 152.82672 121.12752)
+ (xy 152.72512 121.1834) (xy 152.5651 121.23166) (xy 152.4 121.2469) (xy 146.0373 121.2469) (xy 145.8722 121.22912)
+ (xy 145.72488 121.18086) (xy 145.72234 121.18086) (xy 145.7198 121.17832) (xy 145.71472 121.17832) (xy 145.6563 121.1453)
+ (xy 145.57502 121.10212) (xy 145.44802 120.99544) (xy 142.27048 117.82044) (xy 142.16634 117.6909) (xy 142.1638 117.68582)
+ (xy 142.16126 117.68328) (xy 142.14094 117.64772) (xy 142.0876 117.54612) (xy 142.03934 117.3861) (xy 142.0241 117.221)
+ (xy 142.0241 116.7638) (xy 141.98092 116.82476) (xy 141.8209 116.97716) (xy 141.8209 118.13794) (xy 145.3896 121.70664)
+ (xy 145.49374 121.83618) (xy 145.53692 121.92254) (xy 145.5674 121.97588) (xy 145.5674 121.98096) (xy 145.56994 121.9835)
+ (xy 145.57248 121.9962) (xy 145.61566 122.1359) (xy 145.62582 122.25274) (xy 145.669 122.2629) (xy 145.7833 122.31116)
+ (xy 145.8849 122.38228) (xy 145.9738 122.47118) (xy 146.04238 122.57532) (xy 146.09064 122.68962) (xy 146.1135 122.81154)
+ (xy 146.1135 124.33808) (xy 146.0881 124.46) (xy 146.03984 124.5743) (xy 145.96872 124.6759) (xy 145.87982 124.7648)
+ (xy 145.77568 124.83338) (xy 145.66138 124.88164) (xy 145.53946 124.9045) (xy 144.01292 124.9045) (xy 143.891 124.8791)
+ (xy 143.7767 124.83084) (xy 143.6751 124.75972) (xy 143.5862 124.67082) (xy 143.51762 124.56668) (xy 143.46936 124.45238)
+ (xy 143.4465 124.33046) (xy 143.4465 122.80392) (xy 143.4719 122.682) (xy 143.52016 122.5677) (xy 143.59128 122.4661)
+ (xy 143.66494 122.3899) (xy 140.36548 119.09044) (xy 140.26134 118.9609) (xy 140.2588 118.95582) (xy 140.25626 118.95328)
+ (xy 140.23594 118.91772) (xy 140.1826 118.81612) (xy 140.13434 118.6561) (xy 140.1191 118.491) (xy 140.1191 116.9797)
+ (xy 140.10132 116.96954) (xy 139.92098 116.78158) (xy 139.78128 116.5606) (xy 139.68476 116.3193) (xy 139.6365 116.06276)
+ (xy 139.64158 115.80114) (xy 139.69492 115.5446) (xy 139.79906 115.30584) (xy 139.94638 115.08994) (xy 140.1318 114.90706)
+ (xy 140.14704 114.8969) (xy 138.78306 114.8969) (xy 134.93242 118.745) (xy 135.1915 118.85422) (xy 135.42772 119.01424)
+ (xy 135.62838 119.21744) (xy 135.78586 119.45366) (xy 135.89508 119.72036) (xy 135.95096 119.99976) (xy 135.94588 120.32742)
+ (xy 135.88238 120.60428) (xy 135.76554 120.8659) (xy 135.60044 121.09958) (xy 135.3947 121.2977) (xy 135.1534 121.4501)
+ (xy 134.8867 121.55424) (xy 134.60476 121.6025) (xy 134.31774 121.59742) (xy 134.04088 121.53392) (xy 133.95706 121.49582)
+ (xy 133.5659 121.88952) (xy 133.5659 122.93854) (xy 133.60908 122.9233) (xy 133.86562 122.87504) (xy 134.1247 122.87504)
+ (xy 134.38124 122.92838) (xy 134.62254 123.02998) (xy 134.83844 123.1773) (xy 135.0137 123.3551) (xy 137.44194 123.3551)
+ (xy 137.83056 122.96648) (xy 137.8331 122.9614) (xy 137.83564 122.9614) (xy 137.96518 122.85726) (xy 138.05154 122.81154)
+ (xy 138.10488 122.7836) (xy 138.10996 122.78106) (xy 138.1125 122.78106) (xy 138.12012 122.77852) (xy 138.2649 122.73534)
+ (xy 138.43 122.7201) (xy 139.65174 122.7201) (xy 139.6619 122.682) (xy 139.71016 122.5677) (xy 139.78128 122.4661)
+ (xy 139.87018 122.3772) (xy 139.97432 122.30862) (xy 140.08862 122.26036) (xy 140.21054 122.2375) (xy 141.73708 122.2375)
+ (xy 141.859 122.2629) (xy 141.9733 122.31116) (xy 142.0749 122.38228) (xy 142.1638 122.47118) (xy 142.23238 122.57532)
+ (xy 142.28064 122.68962) (xy 142.3035 122.81154) (xy 142.3035 124.08154) (xy 144.16278 125.94082) (xy 144.26692 125.92304)
+ (xy 144.55394 125.92304) (xy 144.83334 125.98146) (xy 145.0975 126.09322) (xy 145.33372 126.25324) (xy 145.53438 126.45644)
+ (xy 145.69186 126.69266) (xy 145.80108 126.95936) (xy 145.84172 127.1651) (xy 146.90344 127.1651) (xy 146.90344 126.34722)
+ (xy 146.60118 126.3015) (xy 146.51482 126.1364) (xy 146.41322 125.8443) (xy 146.37004 125.53696) (xy 146.38782 125.22708)
+ (xy 146.46656 124.92736) (xy 146.60118 124.6505) (xy 146.90344 124.60478) (xy 147.77466 125.476) (xy 146.90344 126.34722)
+ (xy 146.90344 127.1651) (xy 148.01596 127.1651) (xy 148.01596 127.06096) (xy 147.70608 127.04318) (xy 147.40636 126.96444)
+ (xy 147.1295 126.82982) (xy 147.08378 126.52756) (xy 147.955 125.65634) (xy 147.955 125.29566) (xy 147.08378 124.42444)
+ (xy 147.1295 124.12218) (xy 147.2946 124.03582) (xy 147.5867 123.93422) (xy 147.89404 123.89104) (xy 148.20392 123.90882)
+ (xy 148.50364 123.98756) (xy 148.7805 124.12218) (xy 148.82622 124.42444) (xy 147.955 125.29566) (xy 147.955 125.65634)
+ (xy 148.82622 126.52756) (xy 148.7805 126.82982) (xy 148.6154 126.91618) (xy 148.3233 127.01778) (xy 148.01596 127.06096)
+ (xy 148.01596 127.1651) (xy 148.87194 127.1651) (xy 149.58314 126.45136) (xy 149.44598 126.30658) (xy 149.3647 126.17958)
+ (xy 149.30882 126.3015) (xy 149.00656 126.34722) (xy 148.13534 125.476) (xy 149.00656 124.60478) (xy 149.30882 124.6505)
+ (xy 149.36724 124.7648) (xy 149.47138 124.61494) (xy 149.6568 124.43206) (xy 149.87524 124.28982) (xy 149.92604 124.2695)
+ (xy 149.72792 124.2695) (xy 149.606 124.2441) (xy 149.4917 124.19584) (xy 149.3901 124.12472) (xy 149.3012 124.03582)
+ (xy 149.23262 123.93168) (xy 149.18436 123.81738) (xy 149.1615 123.69546) (xy 149.1615 122.16892) (xy 149.1869 122.047)
+ (xy 149.23516 121.9327) (xy 149.30628 121.8311) (xy 149.39518 121.7422) (xy 149.49932 121.67362) (xy 149.61362 121.62536)
+ (xy 149.73554 121.6025) (xy 151.26208 121.6025) (xy 151.384 121.6279) (xy 151.4983 121.67616) (xy 151.5999 121.74728)
+ (xy 151.6888 121.83618) (xy 151.75738 121.94032) (xy 151.80564 122.05462) (xy 151.81072 122.0851) (xy 154.9527 122.0851)
+ (xy 155.1178 122.10288) (xy 155.19654 122.12828) (xy 155.26766 122.1486) (xy 155.41498 122.22734) (xy 155.54198 122.33402)
+ (xy 155.97378 122.76582) (xy 156.07792 122.74804) (xy 156.36494 122.74804) (xy 156.64434 122.80646) (xy 156.9085 122.91822)
+ (xy 157.14472 123.07824) (xy 157.34538 123.28144) (xy 157.50286 123.51766) (xy 157.61208 123.78436) (xy 157.66796 124.06376)
+ (xy 157.66288 124.39142) (xy 157.59938 124.66828) (xy 157.48254 124.9299) (xy 157.31744 125.16358) (xy 157.1117 125.3617)
+ (xy 157.0609 125.39218) (xy 157.0609 125.75794) (xy 157.19552 125.8951) (xy 162.20694 125.8951) (xy 162.56254 125.5395)
+ (xy 164.3253 123.33732) (xy 164.2745 123.05792) (xy 164.27958 122.7709) (xy 164.338 122.4915) (xy 164.4523 122.22988)
+ (xy 164.61232 121.99366) (xy 164.81806 121.793) (xy 165.05682 121.63806) (xy 165.32098 121.53138) (xy 165.60292 121.47804)
+ (xy 165.88994 121.47804) (xy 166.16934 121.53646) (xy 166.4335 121.64822) (xy 166.66972 121.80824) (xy 166.87038 122.01144)
+ (xy 167.02786 122.24766) (xy 167.13708 122.51436) (xy 167.19296 122.79376) (xy 167.18788 123.12142) (xy 167.12438 123.39828)
+ (xy 167.00754 123.6599) (xy 166.84244 123.89358) (xy 166.6367 124.0917) (xy 166.3954 124.2441) (xy 166.1287 124.34824)
+ (xy 165.84676 124.3965) (xy 165.66134 124.39142) (xy 163.85794 126.64186) (xy 163.85032 126.65202) (xy 163.8173 126.68504)
+ (xy 163.79444 126.71298) (xy 163.16198 127.34798) (xy 163.15944 127.34798) (xy 163.15944 127.35052) (xy 163.0299 127.45466)
+ (xy 163.02482 127.45466) (xy 163.02228 127.45974) (xy 162.98672 127.47752) (xy 162.88512 127.5334) (xy 162.7251 127.58166)
+ (xy 162.56 127.5969) (xy 156.8323 127.5969) (xy 156.6672 127.57912) (xy 156.52242 127.53086) (xy 156.51734 127.53086)
+ (xy 156.5148 127.52832) (xy 156.50972 127.52832) (xy 156.4513 127.4953) (xy 156.37002 127.45212) (xy 156.24302 127.34544)
+ (xy 155.60802 126.71298) (xy 155.60548 126.71044) (xy 155.50134 126.5809) (xy 155.4988 126.57582) (xy 155.49626 126.57328)
+ (xy 155.47594 126.53772) (xy 155.4226 126.43612) (xy 155.37434 126.2761) (xy 155.3591 126.111) (xy 155.3591 125.38964)
+ (xy 155.26004 125.32106) (xy 155.06192 125.11532) (xy 154.90698 124.87402) (xy 154.80284 124.60986) (xy 154.7495 124.32792)
+ (xy 154.75458 124.0409) (xy 154.76728 123.96724) (xy 154.5844 123.7869) (xy 151.81072 123.7869) (xy 151.8031 123.825)
+ (xy 151.75484 123.9393) (xy 151.68372 124.0409) (xy 151.59482 124.1298) (xy 151.49068 124.19838) (xy 151.37638 124.24664)
+ (xy 151.25446 124.2695) (xy 151.05888 124.2695) (xy 151.13254 124.29998) (xy 151.34844 124.4473) (xy 151.53132 124.63272)
+ (xy 151.6761 124.84862) (xy 151.77516 125.08992) (xy 151.82596 125.34646) (xy 151.82088 125.64364) (xy 151.76246 125.90018)
+ (xy 151.65832 126.1364) (xy 151.50592 126.34976) (xy 151.3459 126.50216) (xy 151.3459 126.7587) (xy 151.32812 126.9238)
+ (xy 151.30526 126.99238) (xy 151.2824 127.07366) (xy 151.20366 127.22098) (xy 151.09698 127.34798) (xy 149.82698 128.61798)
+ (xy 149.82444 128.61798) (xy 149.82444 128.62052) (xy 149.6949 128.72466) (xy 149.68982 128.72466) (xy 149.68728 128.72974)
+ (xy 149.65172 128.74752) (xy 149.55012 128.8034) (xy 149.3901 128.85166) (xy 149.225 128.8669) (xy 145.4023 128.8669)
+ (xy 145.2372 128.84912) (xy 145.09242 128.80086) (xy 145.08734 128.80086) (xy 145.0848 128.79832) (xy 145.07972 128.79832)
+ (xy 145.0213 128.7653) (xy 144.95272 128.72974) (xy 144.7927 128.79324) (xy 144.51076 128.8415) (xy 144.22374 128.83642)
+ (xy 144.09674 128.80594) (xy 144.12722 128.99644) (xy 143.256 129.86766) (xy 143.256 130.22834) (xy 144.12722 131.09956)
+ (xy 144.10944 131.2164) (xy 144.30756 131.2164) (xy 144.30756 130.91922) (xy 143.43634 130.048) (xy 144.30756 129.17678)
+ (xy 144.60982 129.2225) (xy 144.69618 129.3876) (xy 144.79778 129.6797) (xy 144.84096 129.98704) (xy 144.82318 130.29692)
+ (xy 144.74444 130.59664) (xy 144.60982 130.8735) (xy 144.30756 130.91922) (xy 144.30756 131.2164) (xy 180.90642 131.2164)
+ (xy 180.90642 124.841) (xy 180.7845 124.8156) (xy 180.6702 124.76734) (xy 180.5686 124.69622) (xy 180.4797 124.60732)
+ (xy 180.41112 124.50318) (xy 180.36286 124.38888) (xy 180.34 124.26696) (xy 180.34 123.7869) (xy 174.5361 123.7869)
+ (xy 174.46244 123.89358) (xy 174.2567 124.0917) (xy 174.0154 124.2441) (xy 173.7487 124.34824) (xy 173.46676 124.3965)
+ (xy 173.17974 124.39142) (xy 172.90288 124.32792) (xy 172.63872 124.21362) (xy 172.40504 124.05106) (xy 172.20692 123.84532)
+ (xy 172.05198 123.60402) (xy 171.94784 123.33986) (xy 171.8945 123.05792) (xy 171.89958 122.7709) (xy 171.958 122.4915)
+ (xy 172.0723 122.22988) (xy 172.23232 121.99366) (xy 172.43806 121.793) (xy 172.5041 121.74982) (xy 172.5041 120.30964)
+ (xy 172.40504 120.24106) (xy 172.20692 120.03532) (xy 172.05198 119.79402) (xy 171.94784 119.52986) (xy 171.8945 119.24792)
+ (xy 171.89958 118.9609) (xy 171.958 118.6815) (xy 172.0723 118.41988) (xy 172.23232 118.18366) (xy 172.43806 117.983)
+ (xy 172.67682 117.82806) (xy 172.94098 117.72138) (xy 173.22292 117.66804) (xy 173.50994 117.66804) (xy 173.78934 117.72646)
+ (xy 174.0535 117.83822) (xy 174.28972 117.99824) (xy 174.49038 118.20144) (xy 174.64786 118.43766) (xy 174.75708 118.70436)
+ (xy 174.81296 118.98376) (xy 174.80788 119.31142) (xy 174.74438 119.58828) (xy 174.62754 119.8499) (xy 174.46244 120.08358)
+ (xy 174.2567 120.2817) (xy 174.2059 120.31218) (xy 174.2059 121.74982) (xy 174.28972 121.80824) (xy 174.49038 122.01144)
+ (xy 174.53864 122.0851) (xy 180.34 122.0851) (xy 180.34 121.59742) (xy 180.3654 121.4755) (xy 180.41366 121.3612)
+ (xy 180.48478 121.2596) (xy 180.57368 121.1707) (xy 180.67782 121.10212) (xy 180.78958 121.05386) (xy 180.78958 119.13362)
+ (xy 180.42636 119.01932) (xy 180.26126 118.71198) (xy 180.13426 118.31066) (xy 180.086 117.89156) (xy 180.11902 117.47246)
+ (xy 180.23586 117.0686) (xy 180.42636 116.69268) (xy 180.78958 116.57838) (xy 180.90642 116.69522) (xy 180.90642 101.981)
+ (xy 180.7845 101.9556) (xy 180.6702 101.90734) (xy 180.5686 101.83622) (xy 180.4797 101.74732) (xy 180.41112 101.64318)
+ (xy 180.36286 101.52888) (xy 180.34 101.40696) (xy 180.34 100.9269) (xy 173.3423 100.9269) (xy 173.1772 100.90912)
+ (xy 173.06544 100.87356) (xy 173.0248 100.86086) (xy 173.00448 100.8507) (xy 172.88002 100.78212) (xy 172.75048 100.67544)
+ (xy 172.64634 100.5459) (xy 172.5676 100.40112) (xy 172.55744 100.37064) (xy 172.51934 100.2411) (xy 172.5041 100.076)
+ (xy 172.5041 98.71964) (xy 172.40504 98.65106) (xy 172.20692 98.44532) (xy 172.05198 98.20402) (xy 171.94784 97.93986)
+ (xy 171.8945 97.65792) (xy 171.89958 97.3709) (xy 171.958 97.0915) (xy 172.0723 96.82988) (xy 172.23232 96.59366)
+ (xy 172.43806 96.393) (xy 172.5041 96.34982) (xy 172.5041 92.36964) (xy 172.40504 92.30106) (xy 172.20692 92.09532)
+ (xy 172.05198 91.85402) (xy 171.94784 91.58986) (xy 171.8945 91.30792) (xy 171.89958 91.0209) (xy 171.958 90.7415)
+ (xy 172.0723 90.47988) (xy 172.23232 90.24366) (xy 172.43806 90.043) (xy 172.67682 89.88806) (xy 172.94098 89.78138)
+ (xy 173.22292 89.72804) (xy 173.50994 89.72804) (xy 173.78934 89.78646) (xy 174.0535 89.89822) (xy 174.28972 90.05824)
+ (xy 174.49038 90.26144) (xy 174.64786 90.49766) (xy 174.75708 90.76436) (xy 174.81296 91.04376) (xy 174.80788 91.37142)
+ (xy 174.74438 91.64828) (xy 174.62754 91.9099) (xy 174.46244 92.14358) (xy 174.2567 92.3417) (xy 174.2059 92.37218)
+ (xy 174.2059 96.34982) (xy 174.28972 96.40824) (xy 174.49038 96.61144) (xy 174.64786 96.84766) (xy 174.75708 97.11436)
+ (xy 174.81296 97.39376) (xy 174.80788 97.72142) (xy 174.74438 97.99828) (xy 174.62754 98.2599) (xy 174.46244 98.49358)
+ (xy 174.2567 98.6917) (xy 174.2059 98.72218) (xy 174.2059 99.2251) (xy 180.34 99.2251) (xy 180.34 98.73742)
+ (xy 180.3654 98.6155) (xy 180.41366 98.5012) (xy 180.48478 98.3996) (xy 180.57368 98.3107) (xy 180.67782 98.24212)
+ (xy 180.78958 98.19386) (xy 180.78958 96.27362) (xy 180.42636 96.15932) (xy 180.26126 95.85198) (xy 180.13426 95.45066)
+ (xy 180.086 95.03156) (xy 180.11902 94.61246) (xy 180.23586 94.2086) (xy 180.42636 93.83268) (xy 180.78958 93.71838)
+ (xy 182.06466 94.996) (xy 180.78958 96.27362) (xy 180.78958 98.19386) (xy 180.79212 98.19386) (xy 180.91404 98.171)
+ (xy 182.28056 98.171) (xy 182.28056 97.155) (xy 181.86146 97.12198) (xy 181.4576 97.00514) (xy 181.08168 96.81464)
+ (xy 180.96738 96.45142) (xy 182.245 95.17634) (xy 182.245 94.81566) (xy 180.96738 93.54058) (xy 181.08168 93.17736)
+ (xy 181.38902 93.01226) (xy 181.79034 92.88526) (xy 182.20944 92.837) (xy 182.62854 92.87002) (xy 183.0324 92.98686)
+ (xy 183.40832 93.17736) (xy 183.52262 93.54058) (xy 182.245 94.81566) (xy 182.245 95.17634) (xy 183.52262 96.45142)
+ (xy 183.40832 96.81464) (xy 183.10098 96.97974) (xy 182.69966 97.10674) (xy 182.28056 97.155) (xy 182.28056 98.171)
+ (xy 183.58358 98.171) (xy 183.70042 98.19386) (xy 183.70042 96.27362) (xy 182.42534 94.996) (xy 183.70042 93.71838)
+ (xy 184.06364 93.83268) (xy 184.22874 94.14002) (xy 184.35574 94.54134) (xy 184.404 94.96044) (xy 184.37098 95.37954)
+ (xy 184.25414 95.7834) (xy 184.06364 96.15932) (xy 183.70042 96.27362) (xy 183.70042 98.19386) (xy 183.7055 98.1964)
+ (xy 183.8198 98.24466) (xy 183.9214 98.31578) (xy 184.0103 98.40468) (xy 184.07888 98.50882) (xy 184.12714 98.62312)
+ (xy 184.15 98.74504) (xy 184.15 101.41458) (xy 184.1246 101.5365) (xy 184.07634 101.6508) (xy 184.00522 101.7524)
+ (xy 183.91632 101.8413) (xy 183.81218 101.90988) (xy 183.69788 101.95814) (xy 183.57596 101.981) (xy 180.90642 101.981)
+ (xy 180.90642 116.69522) (xy 182.06466 117.856) (xy 180.90642 119.01424) (xy 180.78958 119.13362) (xy 180.78958 121.05386)
+ (xy 180.79212 121.05386) (xy 180.90642 121.031) (xy 182.28056 121.031) (xy 182.28056 120.015) (xy 181.86146 119.98198)
+ (xy 181.4576 119.86514) (xy 181.08168 119.67464) (xy 180.96738 119.31142) (xy 182.245 118.03634) (xy 182.245 117.67566)
+ (xy 180.96738 116.40058) (xy 181.08168 116.03736) (xy 181.38902 115.87226) (xy 181.79034 115.74526) (xy 182.20944 115.697)
+ (xy 182.62854 115.73002) (xy 183.0324 115.84686) (xy 183.40832 116.03736) (xy 183.52262 116.40058) (xy 182.245 117.67566)
+ (xy 182.245 118.03634) (xy 183.52262 119.31142) (xy 183.40832 119.67464) (xy 183.10098 119.83974) (xy 182.69966 119.96674)
+ (xy 182.28056 120.015) (xy 182.28056 121.031) (xy 183.58358 121.031) (xy 183.70042 121.05386) (xy 183.70042 119.13362)
+ (xy 182.42534 117.856) (xy 183.70042 116.57838) (xy 184.06364 116.69268) (xy 184.22874 117.00002) (xy 184.35574 117.40134)
+ (xy 184.404 117.82044) (xy 184.37098 118.23954) (xy 184.25414 118.6434) (xy 184.06364 119.01932) (xy 183.70042 119.13362)
+ (xy 183.70042 121.05386) (xy 183.7055 121.0564) (xy 183.8198 121.10466) (xy 183.9214 121.17578) (xy 184.0103 121.26468)
+ (xy 184.07888 121.36882) (xy 184.12714 121.48312) (xy 184.15 121.60504) (xy 184.15 124.27458) (xy 184.1246 124.3965)
+ (xy 184.07634 124.5108) (xy 184.00522 124.6124) (xy 183.91632 124.7013) (xy 183.81218 124.76988) (xy 183.69788 124.81814)
+ (xy 183.57596 124.841) (xy 180.90642 124.841) (xy 180.90642 131.2164)
+ )
+ )
+ (filled_polygon
+ (pts
+ (xy 113.665 95.27032) (xy 113.68786 95.21952) (xy 113.84534 94.99092) (xy 114.04346 94.79534) (xy 114.0587 94.78264)
+ (xy 114.06632 94.76232) (xy 114.06886 94.75216) (xy 114.0714 94.75216) (xy 114.07648 94.73692) (xy 114.15268 94.59976)
+ (xy 114.26952 94.45752) (xy 116.93906 91.78798) (xy 116.9416 91.7829) (xy 116.94414 91.7829) (xy 117.01272 91.72702)
+ (xy 117.0813 91.67368) (xy 117.08384 91.67114) (xy 117.08892 91.6686) (xy 117.15242 91.63304) (xy 117.24894 91.58478)
+ (xy 117.25148 91.58478) (xy 117.26672 91.5797) (xy 117.41912 91.53398) (xy 117.602 91.5162) (xy 118.80596 91.5162)
+ (xy 118.80596 86.42096) (xy 118.49608 86.40318) (xy 118.19636 86.32444) (xy 117.9195 86.18982) (xy 117.87378 85.88756)
+ (xy 118.745 85.01634) (xy 119.61622 85.88756) (xy 119.5705 86.18982) (xy 119.4054 86.27618) (xy 119.1133 86.37778)
+ (xy 118.80596 86.42096) (xy 118.80596 91.5162) (xy 119.62384 91.5162) (xy 119.79656 91.34094) (xy 119.79656 85.70722)
+ (xy 118.92534 84.836) (xy 119.79656 83.96478) (xy 120.09882 84.0105) (xy 120.18518 84.1756) (xy 120.28678 84.4677)
+ (xy 120.32996 84.77504) (xy 120.31218 85.08492) (xy 120.23344 85.38464) (xy 120.09882 85.6615) (xy 119.79656 85.70722)
+ (xy 119.79656 91.34094) (xy 121.6152 89.52484) (xy 121.6152 78.50632) (xy 121.61266 78.48346) (xy 121.63298 78.30058)
+ (xy 121.63298 78.29296) (xy 121.63552 78.29042) (xy 121.64568 78.25232) (xy 121.68632 78.12532) (xy 121.68886 78.11516)
+ (xy 121.6914 78.11516) (xy 121.69648 78.09992) (xy 121.77268 77.96276) (xy 121.88952 77.82052) (xy 122.65406 77.05598)
+ (xy 122.7963 76.94168) (xy 122.79884 76.93914) (xy 122.80392 76.9366) (xy 122.8471 76.91374) (xy 122.95886 76.85532)
+ (xy 123.13412 76.80198) (xy 123.317 76.7842) (xy 126.60884 76.7842) (xy 127.48006 75.91298) (xy 127.6223 75.79868)
+ (xy 127.62484 75.79614) (xy 127.62992 75.7936) (xy 127.6731 75.77074) (xy 127.78486 75.71232) (xy 127.96012 75.65898)
+ (xy 128.143 75.6412) (xy 131.05384 75.6412) (xy 140.30706 66.38798) (xy 140.4493 66.27368) (xy 140.45184 66.27114)
+ (xy 140.45692 66.2686) (xy 140.5001 66.24574) (xy 140.61186 66.18732) (xy 140.78712 66.13398) (xy 140.97 66.1162)
+ (xy 140.97762 66.1162) (xy 144.7165 66.11366) (xy 144.51076 65.9765) (xy 144.31264 65.77838) (xy 144.16024 65.54724)
+ (xy 144.12214 65.4558) (xy 140.08862 65.4558) (xy 134.01548 71.53148) (xy 134.01294 71.53148) (xy 134.01294 71.53402)
+ (xy 133.8707 71.64832) (xy 133.86562 71.64832) (xy 133.86308 71.6534) (xy 133.73608 71.71944) (xy 133.70814 71.73468)
+ (xy 133.7056 71.73468) (xy 133.69798 71.73976) (xy 133.6294 71.75754) (xy 133.53288 71.78802) (xy 133.35 71.8058)
+ (xy 127.3048 71.8058) (xy 127.3048 74.0537) (xy 127.28448 74.23658) (xy 127.23114 74.3966) (xy 127.23114 74.40422)
+ (xy 127.2286 74.40422) (xy 127.2286 74.41184) (xy 127.17272 74.50836) (xy 127.14478 74.56424) (xy 127.1397 74.56678)
+ (xy 127.1397 74.57186) (xy 127.07366 74.6506) (xy 127.02794 74.70648) (xy 126.39548 75.34148) (xy 126.39294 75.34148)
+ (xy 126.39294 75.34402) (xy 126.2507 75.45832) (xy 126.24562 75.45832) (xy 126.24308 75.4634) (xy 126.11608 75.5269)
+ (xy 126.08814 75.54468) (xy 126.0856 75.54468) (xy 126.07798 75.54976) (xy 126.00686 75.56754) (xy 125.91288 75.59802)
+ (xy 125.73 75.6158) (xy 119.1895 75.6158) (xy 119.1895 75.82408) (xy 119.1641 75.946) (xy 119.11584 76.0603)
+ (xy 119.04472 76.1619) (xy 118.95582 76.2508) (xy 118.85168 76.31938) (xy 118.73738 76.36764) (xy 118.61546 76.3905)
+ (xy 116.32692 76.3905) (xy 116.205 76.3651) (xy 116.0907 76.31684) (xy 115.9891 76.24572) (xy 115.9002 76.15682)
+ (xy 115.83162 76.05268) (xy 115.78336 75.93838) (xy 115.7605 75.81646) (xy 115.7605 73.52792) (xy 115.7859 73.406)
+ (xy 115.83416 73.2917) (xy 115.90528 73.1901) (xy 115.99418 73.1012) (xy 116.09832 73.03262) (xy 116.21262 72.98436)
+ (xy 116.33454 72.9615) (xy 118.62308 72.9615) (xy 118.745 72.9869) (xy 118.8593 73.03516) (xy 118.9609 73.10628)
+ (xy 119.0498 73.19518) (xy 119.11838 73.29932) (xy 119.16664 73.41362) (xy 119.1895 73.53554) (xy 119.1895 73.7362)
+ (xy 123.35002 73.7362) (xy 123.35002 72.4535) (xy 123.08078 72.45096) (xy 122.90552 72.45096) (xy 122.73534 72.4154)
+ (xy 122.57532 72.34682) (xy 122.43054 72.2503) (xy 122.30862 72.12584) (xy 122.2121 71.98106) (xy 122.14606 71.82104)
+ (xy 122.11304 71.65086) (xy 122.1105 71.21398) (xy 122.33148 70.993) (xy 123.571 70.993) (xy 123.571 72.23252)
+ (xy 123.35002 72.4535) (xy 123.35002 73.7362) (xy 125.33884 73.7362) (xy 125.4252 73.64984) (xy 125.4252 71.80834)
+ (xy 125.31598 71.69658) (xy 125.28296 71.64324) (xy 125.28296 71.65086) (xy 125.24994 71.82104) (xy 125.1839 71.98106)
+ (xy 125.08738 72.12584) (xy 124.96546 72.2503) (xy 124.82068 72.34682) (xy 124.66066 72.4154) (xy 124.49048 72.45096)
+ (xy 124.31522 72.45096) (xy 124.04598 72.4535) (xy 123.825 72.23252) (xy 123.825 70.993) (xy 123.825 70.739)
+ (xy 123.571 70.739) (xy 122.33148 70.739) (xy 122.1105 70.51802) (xy 122.11304 70.08114) (xy 122.14606 69.91096)
+ (xy 122.2121 69.75094) (xy 122.30862 69.60616) (xy 122.43054 69.4817) (xy 122.57532 69.38518) (xy 122.73534 69.3166)
+ (xy 122.89536 69.28104) (xy 122.77598 69.15658) (xy 122.63628 68.9356) (xy 122.53976 68.6943) (xy 122.4915 68.43776)
+ (xy 122.49658 68.17614) (xy 122.54992 67.9196) (xy 122.65406 67.68084) (xy 122.80138 67.46494) (xy 122.8852 67.38112)
+ (xy 122.8852 64.68872) (xy 122.88012 64.68872) (xy 118.5799 68.99148) (xy 118.57736 68.99148) (xy 118.57736 68.99402)
+ (xy 118.43512 69.10832) (xy 118.43004 69.10832) (xy 118.4275 69.1134) (xy 118.3005 69.1769) (xy 118.27256 69.19468)
+ (xy 118.27002 69.19468) (xy 118.2624 69.19976) (xy 118.19382 69.21754) (xy 118.0973 69.24802) (xy 117.91442 69.2658)
+ (xy 117.856 69.2658) (xy 117.856 69.41058) (xy 117.8306 69.5325) (xy 117.78234 69.6468) (xy 117.71122 69.7484)
+ (xy 117.62232 69.8373) (xy 117.51818 69.90588) (xy 117.40388 69.95414) (xy 117.28196 69.977) (xy 117.1448 69.977)
+ (xy 117.1448 70.04812) (xy 117.12448 70.231) (xy 117.07114 70.39102) (xy 117.07114 70.39864) (xy 117.0686 70.39864)
+ (xy 117.0686 70.40626) (xy 117.01272 70.50532) (xy 116.98478 70.55866) (xy 116.9797 70.5612) (xy 116.9797 70.56628)
+ (xy 116.91112 70.64502) (xy 116.86794 70.7009) (xy 110.2868 77.28204) (xy 110.51794 77.28204) (xy 110.79734 77.34046)
+ (xy 111.0615 77.45222) (xy 111.29772 77.61224) (xy 111.49838 77.81544) (xy 111.54664 77.8891) (xy 112.4712 77.8891)
+ (xy 112.49406 77.84084) (xy 112.64138 77.62494) (xy 112.8268 77.44206) (xy 113.04524 77.29982) (xy 113.28908 77.2033)
+ (xy 113.54562 77.15504) (xy 113.8047 77.15504) (xy 114.06124 77.20838) (xy 114.30254 77.30998) (xy 114.51844 77.4573)
+ (xy 114.70132 77.64272) (xy 114.8461 77.85862) (xy 114.94516 78.09992) (xy 114.99596 78.35646) (xy 114.99088 78.65364)
+ (xy 114.93246 78.91018) (xy 114.82832 79.1464) (xy 114.67592 79.35976) (xy 114.48796 79.5401) (xy 114.26698 79.6798)
+ (xy 114.07394 79.75346) (xy 114.30254 79.84998) (xy 114.51844 79.9973) (xy 114.70132 80.18272) (xy 114.8461 80.39862)
+ (xy 114.94516 80.63992) (xy 114.99596 80.89646) (xy 114.99088 81.19364) (xy 114.93246 81.45018) (xy 114.82832 81.6864)
+ (xy 114.67592 81.89976) (xy 114.48796 82.0801) (xy 114.26698 82.2198) (xy 114.02314 82.31378) (xy 113.7666 82.3595)
+ (xy 113.50498 82.35442) (xy 113.25098 82.29854) (xy 113.01222 82.19186) (xy 112.79632 82.04454) (xy 112.63376 81.8769)
+ (xy 111.25708 81.8769) (xy 111.31296 82.15376) (xy 111.30788 82.48142) (xy 111.24438 82.75828) (xy 111.12754 83.0199)
+ (xy 110.96244 83.25358) (xy 110.7567 83.4517) (xy 110.7059 83.48218) (xy 110.7059 83.64982) (xy 110.78972 83.70824)
+ (xy 110.99038 83.91144) (xy 111.14786 84.14766) (xy 111.25708 84.41436) (xy 111.31296 84.69376) (xy 111.30788 85.02142)
+ (xy 111.24438 85.29828) (xy 111.14786 85.51164) (xy 112.43056 84.23148) (xy 112.43564 84.2264) (xy 112.5093 84.16544)
+ (xy 112.64138 83.97494) (xy 112.8268 83.79206) (xy 113.04524 83.64982) (xy 113.28908 83.5533) (xy 113.54562 83.50504)
+ (xy 113.8047 83.50504) (xy 114.06124 83.55838) (xy 114.30254 83.65998) (xy 114.51844 83.8073) (xy 114.70132 83.99272)
+ (xy 114.8461 84.20862) (xy 114.94516 84.44992) (xy 114.99596 84.70646) (xy 114.99088 85.00364) (xy 114.93246 85.26018)
+ (xy 114.8461 85.44814) (xy 115.53952 86.14156) (xy 115.5954 86.21268) (xy 115.64874 86.27618) (xy 115.69192 86.36254)
+ (xy 115.7224 86.41588) (xy 115.7224 86.42096) (xy 115.72494 86.4235) (xy 115.72748 86.43874) (xy 115.77066 86.5759)
+ (xy 115.7859 86.741) (xy 115.7859 87.1474) (xy 115.83924 87.1855) (xy 115.9891 87.33536) (xy 115.9891 83.5533)
+ (xy 116.00688 83.3882) (xy 116.03228 83.30692) (xy 116.0526 83.23834) (xy 116.13134 83.09102) (xy 116.23802 82.96402)
+ (xy 117.55882 81.64068) (xy 117.55628 81.6356) (xy 117.45976 81.3943) (xy 117.4115 81.13776) (xy 117.41658 80.87614)
+ (xy 117.46992 80.6196) (xy 117.57406 80.38084) (xy 117.69344 80.20558) (xy 117.69344 79.35722) (xy 117.39118 79.3115)
+ (xy 117.30482 79.1464) (xy 117.20322 78.8543) (xy 117.16004 78.54696) (xy 117.17782 78.23708) (xy 117.25656 77.93736)
+ (xy 117.39118 77.6605) (xy 117.69344 77.61478) (xy 118.56466 78.486) (xy 117.69344 79.35722) (xy 117.69344 80.20558)
+ (xy 117.72138 80.16494) (xy 117.9068 79.98206) (xy 118.03634 79.8957) (xy 117.9195 79.83982) (xy 117.87378 79.53756)
+ (xy 118.745 78.66634) (xy 118.745 78.30566) (xy 117.87378 77.43444) (xy 117.9195 77.13218) (xy 118.0846 77.04582)
+ (xy 118.3767 76.94422) (xy 118.68404 76.90104) (xy 118.99392 76.91882) (xy 119.29364 76.99756) (xy 119.5705 77.13218)
+ (xy 119.61622 77.43444) (xy 118.745 78.30566) (xy 118.745 78.66634) (xy 119.61622 79.53756) (xy 119.5705 79.83982)
+ (xy 119.45366 79.89824) (xy 119.59844 79.9973) (xy 119.78132 80.18272) (xy 119.79656 80.20304) (xy 119.79656 79.35722)
+ (xy 118.92534 78.486) (xy 119.79656 77.61478) (xy 120.09882 77.6605) (xy 120.18518 77.8256) (xy 120.28678 78.1177)
+ (xy 120.32996 78.42504) (xy 120.31218 78.73492) (xy 120.23344 79.03464) (xy 120.09882 79.3115) (xy 119.79656 79.35722)
+ (xy 119.79656 80.20304) (xy 119.9261 80.39862) (xy 120.02516 80.63992) (xy 120.07596 80.89646) (xy 120.07088 81.19364)
+ (xy 120.01246 81.45018) (xy 119.90832 81.6864) (xy 119.75592 81.89976) (xy 119.56796 82.0801) (xy 119.4181 82.17408)
+ (xy 119.34444 82.26298) (xy 118.27764 83.32724) (xy 118.3767 83.29422) (xy 118.68404 83.25104) (xy 118.99392 83.26882)
+ (xy 119.29364 83.34756) (xy 119.5705 83.48218) (xy 119.61622 83.78444) (xy 118.745 84.65566) (xy 117.87378 83.78444)
+ (xy 117.8814 83.72348) (xy 117.84584 83.75904) (xy 118.745 84.65566) (xy 118.56466 84.836) (xy 117.6909 83.9597)
+ (xy 117.6909 83.96478) (xy 117.69344 83.96478) (xy 118.56466 84.836) (xy 117.69344 85.70722) (xy 117.6909 85.70468)
+ (xy 117.6909 85.70976) (xy 118.56466 84.836) (xy 118.745 85.01634) (xy 117.79758 85.96122) (xy 117.6909 85.85454)
+ (xy 117.6909 89.9287) (xy 117.67312 90.0938) (xy 117.65026 90.16238) (xy 117.6274 90.24366) (xy 117.54612 90.39098)
+ (xy 117.44198 90.51798) (xy 116.67998 91.27998) (xy 116.67744 91.27998) (xy 116.67744 91.28252) (xy 116.5479 91.38666)
+ (xy 116.54282 91.38666) (xy 116.54028 91.39174) (xy 116.50472 91.40952) (xy 116.40312 91.4654) (xy 116.2431 91.51366)
+ (xy 116.078 91.5289) (xy 114.4143 91.5289) (xy 114.2492 91.51112) (xy 114.10188 91.46286) (xy 114.09934 91.46286)
+ (xy 114.0968 91.46032) (xy 114.09172 91.46032) (xy 114.0333 91.4273) (xy 113.95202 91.38412) (xy 113.82502 91.27998)
+ (xy 113.2459 90.70086) (xy 113.2459 94.7674) (xy 113.29924 94.8055) (xy 113.49736 95.00362) (xy 113.64976 95.23476)
+ )
+ )
+ )
+)
diff --git a/qa/test.py b/qa/test.py
new file mode 100644
index 0000000..3ebb937
--- /dev/null
+++ b/qa/test.py
@@ -0,0 +1,18 @@
+import unittest
+import platform
+import sys
+
+if platform.python_version() < '2.7':
+ unittest = __import__('unittest2')
+else:
+ import unittest
+
+if __name__ == '__main__':
+ testsuite = unittest.TestLoader().discover('testcases',pattern="*.py")
+ results = unittest.TextTestRunner(verbosity=100).run(testsuite)
+
+ # Return an error code if any of the testsuite tests fail
+ if not results.wasSuccessful():
+ sys.exit(1)
+
+
diff --git a/qa/testcases/test_000_qa_works.py b/qa/testcases/test_000_qa_works.py
new file mode 100644
index 0000000..3757c19
--- /dev/null
+++ b/qa/testcases/test_000_qa_works.py
@@ -0,0 +1,17 @@
+import unittest
+
+class TestQAWorks(unittest.TestCase):
+
+ def setUp(self):
+ self.pcb = None
+
+ def test_assert_true( self ):
+ self.assertTrue( True )
+
+ def test_assert_equal( self ):
+ self.assertEqual(2, 1+1)
+
+
+if __name__ == '__main__':
+ unittest.main()
+ \ No newline at end of file
diff --git a/qa/testcases/test_001_pcb_load.py b/qa/testcases/test_001_pcb_load.py
new file mode 100644
index 0000000..8e9aea9
--- /dev/null
+++ b/qa/testcases/test_001_pcb_load.py
@@ -0,0 +1,48 @@
+import code
+import unittest
+import pcbnew
+import pdb
+
+class TestPCBLoad(unittest.TestCase):
+
+ def setUp(self):
+ self.pcb = pcbnew.LoadBoard("data/complex_hierarchy.kicad_pcb")
+
+ def test_pcb_load(self):
+ self.assertNotEqual(self.pcb,None)
+
+ def test_pcb_track_count(self):
+ tracks = list(self.pcb.GetTracks())
+ self.assertEqual(len(tracks),361)
+
+ def test_pcb_modules(self):
+ modules = list(self.pcb.GetModules())
+ self.assertEqual(len(modules), 72)
+
+ def test_pcb_module_references(self):
+ board_refs = list(module.GetReference() for
+ module in self.pcb.GetModules())
+
+ known_refs = [u'P1', u'P3', u'C2', u'C1', u'D1', u'Q3', u'Q5', u'Q7',
+ u'Q6', u'Q1', u'Q2', u'Q4', u'Q8', u'P2', u'U1', u'U4',
+ u'P4', u'P5', u'P6', u'U3', u'R9', u'R15', u'RV1', u'RV2',
+ u'C3', u'C4', u'C5', u'C6', u'C7', u'C8', u'C9', u'D2',
+ u'D3', u'D4', u'D5', u'D6', u'D7', u'R3', u'R4', u'R5',
+ u'R6', u'R7', u'R8', u'R10', u'R11', u'R12', u'R13',
+ u'R14', u'R16', u'R17', u'R18', u'R19', u'R20', u'R21',
+ u'R22', u'MIRE', u'C10', u'C11',
+ u'U2', u'C14', u'C12', u'R23', u'R24', u'D9', u'D8', u'R25',
+ u'R26', u'R27', u'R28']
+
+ for ref in known_refs:
+ self.assertTrue(ref in board_refs)
+
+ def test_pcb_netcount(self):
+ self.assertEqual(self.pcb.GetNetCount(),51)
+
+ #def test_interactive(self):
+ # code.interact(local=locals())
+
+if __name__ == '__main__':
+ unittest.main()
+ \ No newline at end of file
diff --git a/qa/testcases/test_002_board_class.py b/qa/testcases/test_002_board_class.py
new file mode 100644
index 0000000..70c46d6
--- /dev/null
+++ b/qa/testcases/test_002_board_class.py
@@ -0,0 +1,132 @@
+import code
+import unittest
+import os
+import pcbnew
+import pdb
+import tempfile
+
+
+from pcbnew import *
+
+
+BACK_COPPER = 'Back_Copper'
+B_CU = 'B.Cu'
+NEW_NAME = 'My_Fancy_Layer_Name'
+
+
+class TestBoardClass(unittest.TestCase):
+
+ def setUp(self):
+ self.pcb = LoadBoard("data/complex_hierarchy.kicad_pcb")
+ self.TITLE="Test Board"
+ self.COMMENT1="For load/save test"
+ self.FILENAME=tempfile.mktemp()+".kicad_pcb"
+
+ def test_pcb_find_module(self):
+ module = self.pcb.FindModule('P1')
+ self.assertEqual(module.GetReference(),'P1')
+
+ def test_pcb_get_track_count(self):
+ pcb = BOARD()
+
+ self.assertEqual(pcb.GetNumSegmTrack(),0)
+
+ track0 = TRACK(pcb)
+ pcb.Add(track0)
+ self.assertEqual(pcb.GetNumSegmTrack(),1)
+
+ track1 = TRACK(pcb)
+ pcb.Add(track1)
+ self.assertEqual(pcb.GetNumSegmTrack(),2)
+
+ def test_pcb_bounding_box(self):
+ pcb = BOARD()
+ track = TRACK(pcb)
+ pcb.Add(track)
+
+ #track.SetStartEnd(wxPointMM(10.0, 10.0),
+ # wxPointMM(20.0, 30.0))
+
+ track.SetStart(wxPointMM(10.0, 10.0))
+ track.SetEnd(wxPointMM(20.0, 30.0))
+
+ track.SetWidth(FromMM(0.5))
+
+ #!!! THIS FAILS? == 0.0 x 0.0 ??
+ #height, width = ToMM(pcb.ComputeBoundingBox().GetSize())
+ bounding_box = pcb.ComputeBoundingBox()
+ height, width = ToMM(bounding_box.GetSize())
+
+ clearance = ToMM(track.GetClearance()*2)
+ self.assertAlmostEqual(width, (30-10) + 0.5 + clearance, 2)
+ self.assertAlmostEqual(height, (20-10) + 0.5 + clearance, 2)
+
+ def test_pcb_get_pad(self):
+ pcb = BOARD()
+ module = MODULE(pcb)
+ pcb.Add(module)
+ pad = D_PAD(module)
+ module.Add(pad)
+
+ pad.SetShape(PAD_OVAL)
+ pad.SetSize(wxSizeMM(2.0, 3.0))
+ pad.SetPosition(wxPointMM(0,0))
+
+ # easy case
+ p1 = pcb.GetPad(wxPointMM(0,0))
+
+ # top side
+ p2 = pcb.GetPad(wxPointMM(0.9,0.0))
+
+ # bottom side
+ p3 = pcb.GetPad(wxPointMM(0,1.4))
+
+ # TODO: get pad == p1 evaluated as true instead
+ # of relying in the internal C++ object pointer
+ self.assertEqual(pad.this, p1.this)
+ self.assertEqual(pad.this, p2.this)
+ self.assertEqual(pad.this, p3.this)
+
+ def test_pcb_save_and_load(self):
+ pcb = BOARD()
+ pcb.GetTitleBlock().SetTitle(self.TITLE)
+ pcb.GetTitleBlock().SetComment1(self.COMMENT1)
+ result = SaveBoard(self.FILENAME,pcb)
+ self.assertTrue(result)
+
+ pcb2 = LoadBoard(self.FILENAME)
+ self.assertNotEqual(pcb2,None)
+
+ tb = pcb2.GetTitleBlock()
+ self.assertEqual(tb.GetTitle(),self.TITLE)
+ self.assertEqual(tb.GetComment1(),self.COMMENT1)
+
+ os.remove(self.FILENAME)
+
+ def test_pcb_layer_name_set_get(self):
+ pcb = BOARD()
+ pcb.SetLayerName(31, BACK_COPPER)
+ self.assertEqual(pcb.GetLayerName(31), BACK_COPPER)
+
+ def test_pcb_layer_name_set_get(self):
+ pcb = BOARD()
+ pcb.SetLayerName(31, BACK_COPPER)
+ self.assertEqual(pcb.GetLayerName(31), BACK_COPPER)
+
+ def test_pcb_layer_id_get(self):
+ pcb = BOARD()
+ b_cu_id = pcb.GetLayerID(B_CU)
+ pcb.SetLayerName(b_cu_id, NEW_NAME)
+
+ # ensure we can get the ID for the new name
+ self.assertEqual(pcb.GetLayerID(NEW_NAME), b_cu_id)
+
+ # ensure we can get to the ID via the STD name too
+ self.assertEqual(pcb.GetLayerID(B_CU), b_cu_id)
+
+ #def test_interactive(self):
+ # code.interact(local=locals())
+
+if __name__ == '__main__':
+ unittest.main()
+
diff --git a/resources/linux/mime/applications/bitmap2component.desktop b/resources/linux/mime/applications/bitmap2component.desktop
new file mode 100644
index 0000000..f2ff024
--- /dev/null
+++ b/resources/linux/mime/applications/bitmap2component.desktop
@@ -0,0 +1,10 @@
+[Desktop Entry]
+Categories=Development;Electronics;
+Comment=Create a component from a bitmap for use with KiCad
+Exec=bitmap2component
+GenericName=EDA Suite
+Icon=bitmap2component
+MimeType=application/x-bitmap2component-project;
+Name=Bitmap to Component Converter
+Type=Application
+Name[en_US]=Bitmap to Component Converter
diff --git a/resources/linux/mime/applications/cvpcb.desktop b/resources/linux/mime/applications/cvpcb.desktop
new file mode 100644
index 0000000..4610839
--- /dev/null
+++ b/resources/linux/mime/applications/cvpcb.desktop
@@ -0,0 +1,10 @@
+[Desktop Entry]
+Categories=Development;Electronics;
+Comment=Assign footprints to symbols (part of KiCad)
+Exec=cvpcb
+GenericName=EDA Suite
+Icon=cvpcb
+MimeType=application/x-cvpcb-project;
+Name=CvPcb
+Type=Application
+Name[en_US]=CvPcb
diff --git a/resources/linux/mime/applications/eeschema.desktop b/resources/linux/mime/applications/eeschema.desktop
new file mode 100644
index 0000000..41b1ba3
--- /dev/null
+++ b/resources/linux/mime/applications/eeschema.desktop
@@ -0,0 +1,11 @@
+[Desktop Entry]
+Categories=Development;Electronics;
+Comment=Design an electronic schematic
+Comment[fr]=Dessiner des schémas électroniques
+Exec=eeschema %f
+GenericName=Electronic schematic design
+GenericName[fr]=Saisie de schéma électronique
+Icon=eeschema
+MimeType=application/x-eeschema-schematic;
+Name=Eeschema
+Type=Application
diff --git a/resources/linux/mime/applications/gerbview.desktop b/resources/linux/mime/applications/gerbview.desktop
new file mode 100644
index 0000000..42e3a83
--- /dev/null
+++ b/resources/linux/mime/applications/gerbview.desktop
@@ -0,0 +1,10 @@
+[Desktop Entry]
+Categories=Development;Electronics;
+Comment=View gerber files
+Exec=gerbview
+GenericName=EDA Suite
+Icon=gerbview
+MimeType=application/x-gerbview-project;
+Name=GerbView
+Type=Application
+Name[en_US]=GerbView
diff --git a/resources/linux/mime/applications/kicad.desktop b/resources/linux/mime/applications/kicad.desktop
new file mode 100644
index 0000000..e9df0f0
--- /dev/null
+++ b/resources/linux/mime/applications/kicad.desktop
@@ -0,0 +1,11 @@
+[Desktop Entry]
+Categories=Development;Electronics;
+Comment=Design a printed circuit board
+Comment[fr]=Concevoir un circuit imprimé
+Exec=kicad %f
+GenericName=EDA Suite
+GenericName[fr]=Suite logicielle de conception Ă©lectronique
+Icon=kicad
+MimeType=application/x-kicad-project;
+Name=KiCad
+Type=Application
diff --git a/resources/linux/mime/applications/pcbcalculator.desktop b/resources/linux/mime/applications/pcbcalculator.desktop
new file mode 100644
index 0000000..ce37e64
--- /dev/null
+++ b/resources/linux/mime/applications/pcbcalculator.desktop
@@ -0,0 +1,10 @@
+[Desktop Entry]
+Categories=Development;Electronics;
+Comment=Calculator for various electronics related computations
+Exec=pcb_calculator
+GenericName=EDA Suite
+Icon=pcbcalculator
+MimeType=application/x-pcbcalculator-project;
+Name=Pcb Calculator
+Type=Application
+Name[en_US]=Pcb Calculator
diff --git a/resources/linux/mime/applications/pcbnew.desktop b/resources/linux/mime/applications/pcbnew.desktop
new file mode 100644
index 0000000..120231e
--- /dev/null
+++ b/resources/linux/mime/applications/pcbnew.desktop
@@ -0,0 +1,10 @@
+[Desktop Entry]
+Categories=Development;Electronics;
+Comment=Layout a printed circuit board
+Exec=pcbnew %f
+GenericName=EDA Suite
+Icon=pcbnew
+MimeType=application/x-pcbnew-pcb;
+Name=Pcbnew
+Type=Application
+Name[en_US]=Pcbnew
diff --git a/resources/linux/mime/icons/hicolor/128x128/apps/3d.png b/resources/linux/mime/icons/hicolor/128x128/apps/3d.png
new file mode 100644
index 0000000..cfb90da
--- /dev/null
+++ b/resources/linux/mime/icons/hicolor/128x128/apps/3d.png
Binary files differ
diff --git a/resources/linux/mime/icons/hicolor/128x128/apps/bitmap2component.png b/resources/linux/mime/icons/hicolor/128x128/apps/bitmap2component.png
new file mode 100644
index 0000000..d87323d
--- /dev/null
+++ b/resources/linux/mime/icons/hicolor/128x128/apps/bitmap2component.png
Binary files differ
diff --git a/resources/linux/mime/icons/hicolor/128x128/apps/cvpcb.png b/resources/linux/mime/icons/hicolor/128x128/apps/cvpcb.png
new file mode 100644
index 0000000..9bd762e
--- /dev/null
+++ b/resources/linux/mime/icons/hicolor/128x128/apps/cvpcb.png
Binary files differ
diff --git a/resources/linux/mime/icons/hicolor/128x128/apps/eeschema.png b/resources/linux/mime/icons/hicolor/128x128/apps/eeschema.png
new file mode 100644
index 0000000..9d85ac9
--- /dev/null
+++ b/resources/linux/mime/icons/hicolor/128x128/apps/eeschema.png
Binary files differ
diff --git a/resources/linux/mime/icons/hicolor/128x128/apps/gerbview.png b/resources/linux/mime/icons/hicolor/128x128/apps/gerbview.png
new file mode 100644
index 0000000..54b66cd
--- /dev/null
+++ b/resources/linux/mime/icons/hicolor/128x128/apps/gerbview.png
Binary files differ
diff --git a/resources/linux/mime/icons/hicolor/128x128/apps/kicad.png b/resources/linux/mime/icons/hicolor/128x128/apps/kicad.png
new file mode 100644
index 0000000..57d15ec
--- /dev/null
+++ b/resources/linux/mime/icons/hicolor/128x128/apps/kicad.png
Binary files differ
diff --git a/resources/linux/mime/icons/hicolor/128x128/apps/pcbcalculator.png b/resources/linux/mime/icons/hicolor/128x128/apps/pcbcalculator.png
new file mode 100644
index 0000000..0d66b76
--- /dev/null
+++ b/resources/linux/mime/icons/hicolor/128x128/apps/pcbcalculator.png
Binary files differ
diff --git a/resources/linux/mime/icons/hicolor/128x128/apps/pcbnew.png b/resources/linux/mime/icons/hicolor/128x128/apps/pcbnew.png
new file mode 100644
index 0000000..d95d0b2
--- /dev/null
+++ b/resources/linux/mime/icons/hicolor/128x128/apps/pcbnew.png
Binary files differ
diff --git a/resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-3d-project.png b/resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-3d-project.png
new file mode 100644
index 0000000..cfb90da
--- /dev/null
+++ b/resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-3d-project.png
Binary files differ
diff --git a/resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-bitmap2component-project.png b/resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-bitmap2component-project.png
new file mode 100644
index 0000000..d87323d
--- /dev/null
+++ b/resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-bitmap2component-project.png
Binary files differ
diff --git a/resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-cvpcb-project.png b/resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-cvpcb-project.png
new file mode 100644
index 0000000..9bd762e
--- /dev/null
+++ b/resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-cvpcb-project.png
Binary files differ
diff --git a/resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-eeschema-project.png b/resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-eeschema-project.png
new file mode 100644
index 0000000..9d85ac9
--- /dev/null
+++ b/resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-eeschema-project.png
Binary files differ
diff --git a/resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-gerbview-project.png b/resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-gerbview-project.png
new file mode 100644
index 0000000..54b66cd
--- /dev/null
+++ b/resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-gerbview-project.png
Binary files differ
diff --git a/resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-kicad-project.png b/resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-kicad-project.png
new file mode 100644
index 0000000..57d15ec
--- /dev/null
+++ b/resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-kicad-project.png
Binary files differ
diff --git a/resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-pcbcalculator-project.png b/resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-pcbcalculator-project.png
new file mode 100644
index 0000000..0d66b76
--- /dev/null
+++ b/resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-pcbcalculator-project.png
Binary files differ
diff --git a/resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-pcbnew-project.png b/resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-pcbnew-project.png
new file mode 100644
index 0000000..d95d0b2
--- /dev/null
+++ b/resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-pcbnew-project.png
Binary files differ
diff --git a/resources/linux/mime/icons/hicolor/16x16/mimetypes/application-x-kicad-project.png b/resources/linux/mime/icons/hicolor/16x16/mimetypes/application-x-kicad-project.png
new file mode 100644
index 0000000..11217a2
--- /dev/null
+++ b/resources/linux/mime/icons/hicolor/16x16/mimetypes/application-x-kicad-project.png
Binary files differ
diff --git a/resources/linux/mime/icons/hicolor/22x22/apps/kicad.png b/resources/linux/mime/icons/hicolor/22x22/apps/kicad.png
new file mode 100644
index 0000000..57daaa6
--- /dev/null
+++ b/resources/linux/mime/icons/hicolor/22x22/apps/kicad.png
Binary files differ
diff --git a/resources/linux/mime/icons/hicolor/22x22/mimetypes/application-x-kicad-project.png b/resources/linux/mime/icons/hicolor/22x22/mimetypes/application-x-kicad-project.png
new file mode 100644
index 0000000..57daaa6
--- /dev/null
+++ b/resources/linux/mime/icons/hicolor/22x22/mimetypes/application-x-kicad-project.png
Binary files differ
diff --git a/resources/linux/mime/icons/hicolor/24x24/apps/kicad.png b/resources/linux/mime/icons/hicolor/24x24/apps/kicad.png
new file mode 100644
index 0000000..667a6ce
--- /dev/null
+++ b/resources/linux/mime/icons/hicolor/24x24/apps/kicad.png
Binary files differ
diff --git a/resources/linux/mime/icons/hicolor/24x24/mimetypes/application-x-kicad-project.png b/resources/linux/mime/icons/hicolor/24x24/mimetypes/application-x-kicad-project.png
new file mode 100644
index 0000000..667a6ce
--- /dev/null
+++ b/resources/linux/mime/icons/hicolor/24x24/mimetypes/application-x-kicad-project.png
Binary files differ
diff --git a/resources/linux/mime/icons/hicolor/32x32/apps/kicad.png b/resources/linux/mime/icons/hicolor/32x32/apps/kicad.png
new file mode 100644
index 0000000..74acea8
--- /dev/null
+++ b/resources/linux/mime/icons/hicolor/32x32/apps/kicad.png
Binary files differ
diff --git a/resources/linux/mime/icons/hicolor/32x32/mimetypes/application-x-kicad-project.png b/resources/linux/mime/icons/hicolor/32x32/mimetypes/application-x-kicad-project.png
new file mode 100644
index 0000000..74acea8
--- /dev/null
+++ b/resources/linux/mime/icons/hicolor/32x32/mimetypes/application-x-kicad-project.png
Binary files differ
diff --git a/resources/linux/mime/icons/hicolor/48x48/apps/bitmap2component.png b/resources/linux/mime/icons/hicolor/48x48/apps/bitmap2component.png
new file mode 100644
index 0000000..4d21f4c
--- /dev/null
+++ b/resources/linux/mime/icons/hicolor/48x48/apps/bitmap2component.png
Binary files differ
diff --git a/resources/linux/mime/icons/hicolor/48x48/apps/cvpcb.png b/resources/linux/mime/icons/hicolor/48x48/apps/cvpcb.png
new file mode 100644
index 0000000..56bba83
--- /dev/null
+++ b/resources/linux/mime/icons/hicolor/48x48/apps/cvpcb.png
Binary files differ
diff --git a/resources/linux/mime/icons/hicolor/48x48/apps/eeschema.png b/resources/linux/mime/icons/hicolor/48x48/apps/eeschema.png
new file mode 100644
index 0000000..edd4e5b
--- /dev/null
+++ b/resources/linux/mime/icons/hicolor/48x48/apps/eeschema.png
Binary files differ
diff --git a/resources/linux/mime/icons/hicolor/48x48/apps/gerbview.png b/resources/linux/mime/icons/hicolor/48x48/apps/gerbview.png
new file mode 100644
index 0000000..87c9ce3
--- /dev/null
+++ b/resources/linux/mime/icons/hicolor/48x48/apps/gerbview.png
Binary files differ
diff --git a/resources/linux/mime/icons/hicolor/48x48/apps/kicad.png b/resources/linux/mime/icons/hicolor/48x48/apps/kicad.png
new file mode 100644
index 0000000..39969ef
--- /dev/null
+++ b/resources/linux/mime/icons/hicolor/48x48/apps/kicad.png
Binary files differ
diff --git a/resources/linux/mime/icons/hicolor/48x48/apps/pcbcalculator.png b/resources/linux/mime/icons/hicolor/48x48/apps/pcbcalculator.png
new file mode 100644
index 0000000..7e2bd1f
--- /dev/null
+++ b/resources/linux/mime/icons/hicolor/48x48/apps/pcbcalculator.png
Binary files differ
diff --git a/resources/linux/mime/icons/hicolor/48x48/apps/pcbnew.png b/resources/linux/mime/icons/hicolor/48x48/apps/pcbnew.png
new file mode 100644
index 0000000..90bfdae
--- /dev/null
+++ b/resources/linux/mime/icons/hicolor/48x48/apps/pcbnew.png
Binary files differ
diff --git a/resources/linux/mime/icons/hicolor/48x48/mimetypes/application-x-bitmap2component-project.png b/resources/linux/mime/icons/hicolor/48x48/mimetypes/application-x-bitmap2component-project.png
new file mode 100644
index 0000000..4d21f4c
--- /dev/null
+++ b/resources/linux/mime/icons/hicolor/48x48/mimetypes/application-x-bitmap2component-project.png
Binary files differ
diff --git a/resources/linux/mime/icons/hicolor/48x48/mimetypes/application-x-cvpcb-project.png b/resources/linux/mime/icons/hicolor/48x48/mimetypes/application-x-cvpcb-project.png
new file mode 100644
index 0000000..56bba83
--- /dev/null
+++ b/resources/linux/mime/icons/hicolor/48x48/mimetypes/application-x-cvpcb-project.png
Binary files differ
diff --git a/resources/linux/mime/icons/hicolor/48x48/mimetypes/application-x-eeschema-project.png b/resources/linux/mime/icons/hicolor/48x48/mimetypes/application-x-eeschema-project.png
new file mode 100644
index 0000000..edd4e5b
--- /dev/null
+++ b/resources/linux/mime/icons/hicolor/48x48/mimetypes/application-x-eeschema-project.png
Binary files differ
diff --git a/resources/linux/mime/icons/hicolor/48x48/mimetypes/application-x-gerbview-project.png b/resources/linux/mime/icons/hicolor/48x48/mimetypes/application-x-gerbview-project.png
new file mode 100644
index 0000000..87c9ce3
--- /dev/null
+++ b/resources/linux/mime/icons/hicolor/48x48/mimetypes/application-x-gerbview-project.png
Binary files differ
diff --git a/resources/linux/mime/icons/hicolor/48x48/mimetypes/application-x-kicad-project.png b/resources/linux/mime/icons/hicolor/48x48/mimetypes/application-x-kicad-project.png
new file mode 100644
index 0000000..39969ef
--- /dev/null
+++ b/resources/linux/mime/icons/hicolor/48x48/mimetypes/application-x-kicad-project.png
Binary files differ
diff --git a/resources/linux/mime/icons/hicolor/48x48/mimetypes/application-x-pcbcalculator-project.png b/resources/linux/mime/icons/hicolor/48x48/mimetypes/application-x-pcbcalculator-project.png
new file mode 100644
index 0000000..7e2bd1f
--- /dev/null
+++ b/resources/linux/mime/icons/hicolor/48x48/mimetypes/application-x-pcbcalculator-project.png
Binary files differ
diff --git a/resources/linux/mime/icons/hicolor/48x48/mimetypes/application-x-pcbnew-project.png b/resources/linux/mime/icons/hicolor/48x48/mimetypes/application-x-pcbnew-project.png
new file mode 100644
index 0000000..90bfdae
--- /dev/null
+++ b/resources/linux/mime/icons/hicolor/48x48/mimetypes/application-x-pcbnew-project.png
Binary files differ
diff --git a/resources/linux/mime/icons/hicolor/scalable/apps/3d.svg b/resources/linux/mime/icons/hicolor/scalable/apps/3d.svg
new file mode 100644
index 0000000..1d4f04c
--- /dev/null
+++ b/resources/linux/mime/icons/hicolor/scalable/apps/3d.svg
@@ -0,0 +1,32 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs>
+ <linearGradient id="a">
+ <stop stop-color="#fbffff" stop-opacity="0" offset="0"/>
+ <stop stop-color="#fff" offset="1"/>
+ </linearGradient>
+ <linearGradient id="f" y2="16.549" xlink:href="#a" gradientUnits="userSpaceOnUse" x2="110.17" gradientTransform="matrix(.18304 0 0 .35849 2.8651 3.4851)" y1="79.338" x1="157.21"/>
+ <linearGradient id="g" y2="11.503" xlink:href="#a" gradientUnits="userSpaceOnUse" x2="22.394" gradientTransform="matrix(.21564 0 0 .30429 2.8651 3.4851)" y1="74.459" x1="78.703"/>
+ <filter id="e" color-interpolation-filters="sRGB">
+ <feGaussianBlur stdDeviation="2.6423077"/>
+ </filter>
+ </defs>
+ <path opacity=".29297" d="m84.5 33 69.5-9 12-13.5 3.5-57.5-149 56z" transform="matrix(.31556 0 0 .33013 -2.7527 33.927)" filter="url(#e)"/>
+ <path stroke-linejoin="round" d="m23.485 5.6161-18.67 4.9429 0.0392 25.808h0.0078v0.04909l0.070485-0.01638 18.505 7.1761 17.385-7.5033v-25.513l-17.338-4.9424z" stroke-opacity=".5" stroke="#000" stroke-width="3.202" fill="none"/>
+ <path stroke-linejoin="round" d="m40.8 10.563v24.014l-16.778-3.856-0.562-25.097 17.34 4.939z" fill-rule="evenodd" stroke="#000" stroke-linecap="round" stroke-width="1.9265" fill="#515151"/>
+ <path stroke-linejoin="round" d="m4.7875 10.563 18.674-4.938 1.195 25.952-19.82 4.84l-0.05-25.854z" fill-rule="evenodd" stroke="#000" stroke-linecap="round" stroke-width="1.9265" fill="#4a494d"/>
+ <path stroke-linejoin="round" d="m23.415 16.567 17.385-6.004-17.339-4.938-18.674 4.938 18.628 6.004z" fill-rule="evenodd" stroke="#000" stroke-linecap="round" stroke-width="1.9265" fill="#9db8d2"/>
+ <path stroke-linejoin="round" d="m40.8 10.563v25.515l-17.386 7.504v-27.017l17.386-6.003z" fill-rule="evenodd" stroke="#000" stroke-linecap="round" stroke-width="1.9265" fill="#4b6983"/>
+ <path stroke-linejoin="round" d="m23.415 16.567v27.016l-18.589-7.214-0.0384-25.806 18.627 6.004z" fill-rule="evenodd" stroke="#000" stroke-linecap="round" stroke-width="1.9265" fill="#7590ae"/>
+ <path fill-rule="evenodd" fill="url(#g)" d="m23.392 6.6501-17.585 4.6919v24.002c18.303 7.503 21.351 12.426 16.673-19.471 16.577-6.3815 0.912-9.2226 0.912-9.2226z"/>
+ <path d="m24.39 17.326v24.541c0.06887 0 7.3698-2.8787 10.745-6.4769 3.4437-2.9506 4.6147-8.9239 4.6147-8.9239v-14.682l-15.36 5.5416z" fill-opacity=".75" fill-rule="evenodd" fill="url(#f)"/>
+ <path stroke-linejoin="round" d="m23.415 16.567v27.016l-18.589-7.214-0.0384-25.806 18.627 6.004z" stroke="#000" stroke-linecap="round" stroke-width="1.9265" fill="none"/>
+ <g transform="matrix(.27133 .094049 .0019799 .4195 36.941 31.118)">
+ <rect fill-rule="evenodd" rx="2.825" ry="7.866" height="46.063" width="38.976" stroke="#000" y="-13.622" x="-102.76" stroke-width="2.2011" fill="#fff"/>
+ <text style="word-spacing:0px;letter-spacing:0px" xml:space="preserve" font-size="40px" line-height="125%" y="23.416523" x="-94.562927" font-family="Bitstream Vera Sans" fill="#000000"><tspan y="23.416523" x="-94.562927" font-weight="bold" fill="#f80d0d">3</tspan></text>
+ </g>
+ <g transform="matrix(.36014 .11661 .0026279 .52013 13.318 26.471)">
+ <rect transform="matrix(.89114 -.45373 -.028839 .99958 0 0)" fill-rule="evenodd" rx="2.6473" ry="6.2048" height="36.335" width="36.525" stroke="#000" y="-2.7531" x="41.151" stroke-width="1.8925" fill="#fff"/>
+ <text style="word-spacing:0px;letter-spacing:0px" xml:space="preserve" transform="matrix(1.0067 -.60333 -.017083 1.0035 0 0)" line-height="125%" font-size="31.689px" y="31.395872" x="39.189945" font-family="Bitstream Vera Sans" fill="#000000"><tspan y="31.395872" x="39.189945" font-weight="bold" fill="#f80d0d">D</tspan></text>
+ </g>
+ <text style="word-spacing:0px;letter-spacing:0px" xml:space="preserve" font-size="40px" line-height="125%" y="34.097565" x="-6.9512191" font-family="Bitstream Vera Sans" fill="#000000"><tspan/></text>
+</svg>
diff --git a/resources/linux/mime/icons/hicolor/scalable/apps/bitmap2component.svg b/resources/linux/mime/icons/hicolor/scalable/apps/bitmap2component.svg
new file mode 100644
index 0000000..fe6cb4d
--- /dev/null
+++ b/resources/linux/mime/icons/hicolor/scalable/apps/bitmap2component.svg
@@ -0,0 +1,1125 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ height="48"
+ width="48"
+ version="1.1"
+ id="svg2"
+ inkscape:version="0.47 r22583"
+ sodipodi:docname="icon_bitmap2component.svg">
+ <metadata
+ id="metadata347">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1280"
+ inkscape:window-height="949"
+ id="namedview345"
+ showgrid="false"
+ inkscape:zoom="4.9166667"
+ inkscape:cx="24"
+ inkscape:cy="23.59322"
+ inkscape:window-x="0"
+ inkscape:window-y="25"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg2" />
+ <defs
+ id="defs4">
+ <inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 24 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="48 : 24 : 1"
+ inkscape:persp3d-origin="24 : 16 : 1"
+ id="perspective349" />
+ <linearGradient
+ id="aw">
+ <stop
+ offset="0"
+ id="stop7" />
+ <stop
+ stop-opacity="0"
+ offset="1"
+ id="stop9" />
+ </linearGradient>
+ <linearGradient
+ id="au">
+ <stop
+ stop-color="#555753"
+ stop-opacity="0"
+ offset="0"
+ id="stop12" />
+ <stop
+ stop-color="#eeeeec"
+ offset="1"
+ id="stop14" />
+ </linearGradient>
+ <linearGradient
+ id="ay">
+ <stop
+ stop-color="#2e3436"
+ offset="0"
+ id="stop17" />
+ <stop
+ stop-color="#555753"
+ stop-opacity="0"
+ offset="1"
+ id="stop19" />
+ </linearGradient>
+ <linearGradient
+ id="az">
+ <stop
+ stop-color="#eeeeec"
+ offset="0"
+ id="stop22" />
+ <stop
+ stop-color="#fff"
+ stop-opacity="0"
+ offset="1"
+ id="stop24" />
+ </linearGradient>
+ <linearGradient
+ id="ax">
+ <stop
+ stop-color="#888a85"
+ offset="0"
+ id="stop27" />
+ <stop
+ stop-color="#fff"
+ offset="1"
+ id="stop29" />
+ </linearGradient>
+ <filter
+ id="ck"
+ height="1.6824"
+ width="1.6824"
+ color-interpolation-filters="sRGB"
+ y="-.34118"
+ x="-.34118">
+ <feGaussianBlur
+ stdDeviation="0.28648224"
+ id="feGaussianBlur32" />
+ </filter>
+ <linearGradient
+ id="at">
+ <stop
+ stop-color="#dee3e0"
+ offset="0"
+ id="stop35" />
+ <stop
+ stop-color="#dee3e0"
+ stop-opacity="0"
+ offset="1"
+ id="stop37" />
+ </linearGradient>
+ <radialGradient
+ id="ba"
+ xlink:href="#aw"
+ gradientUnits="userSpaceOnUse"
+ cy="486.65"
+ cx="605.71"
+ gradientTransform="matrix(-.062854 0 0 .020588 46.705 34.451)"
+ r="117.14" />
+ <linearGradient
+ id="av"
+ y2="609.51"
+ gradientUnits="userSpaceOnUse"
+ x2="302.86"
+ gradientTransform="matrix(.062854 0 0 .020588 1.2826 34.451)"
+ y1="366.65"
+ x1="302.86">
+ <stop
+ stop-opacity="0"
+ offset="0"
+ id="stop41" />
+ <stop
+ offset=".5"
+ id="stop43" />
+ <stop
+ stop-opacity="0"
+ offset="1"
+ id="stop45" />
+ </linearGradient>
+ <radialGradient
+ id="bb"
+ xlink:href="#aw"
+ gradientUnits="userSpaceOnUse"
+ cy="486.65"
+ cx="605.71"
+ gradientTransform="matrix(.062854 0 0 .020588 1.295 34.451)"
+ r="117.14" />
+ <radialGradient
+ id="bl"
+ gradientUnits="userSpaceOnUse"
+ cy="188.5"
+ cx="171.25"
+ gradientTransform="matrix(.23274 0 0 .23274 -13.152 -9.0643)"
+ r="19">
+ <stop
+ stop-color="#b100cb"
+ offset="0"
+ id="stop49" />
+ <stop
+ stop-color="#204a87"
+ stop-opacity="0"
+ offset="1"
+ id="stop51" />
+ </radialGradient>
+ <linearGradient
+ id="cl"
+ y2="53.914"
+ gradientUnits="userSpaceOnUse"
+ x2="11.692"
+ gradientTransform="matrix(.97498 0 0 .9583 -.89967 3.1462)"
+ y1="20.521"
+ x1="10.666">
+ <stop
+ stop-color="#fff"
+ offset="0"
+ id="stop54" />
+ <stop
+ stop-color="#fff"
+ stop-opacity="0"
+ offset="1"
+ id="stop56" />
+ </linearGradient>
+ <radialGradient
+ id="bm"
+ xlink:href="#at"
+ gradientUnits="userSpaceOnUse"
+ cy="12.007"
+ cx="14.739"
+ gradientTransform="matrix(-3.3976e-6,-2.2552,3.3832,-5.402e-6,-27.32,54.059)"
+ r=".54688" />
+ <radialGradient
+ id="bn"
+ gradientUnits="userSpaceOnUse"
+ cy="17.109"
+ cx="9.2366"
+ gradientTransform="matrix(1.2675 -4.6716e-7 1.8899e-7 .44533 1.7922 12.443)"
+ r="2.961">
+ <stop
+ stop-color="#e9e9e9"
+ offset="0"
+ id="stop60" />
+ <stop
+ stop-color="#a7a7a7"
+ offset="0"
+ id="stop62" />
+ <stop
+ stop-color="#bebebe"
+ offset="0.529"
+ id="stop64" />
+ <stop
+ stop-color="#e7e7e7"
+ offset="1"
+ id="stop66" />
+ </radialGradient>
+ <radialGradient
+ id="bo"
+ xlink:href="#at"
+ gradientUnits="userSpaceOnUse"
+ cy="20.823"
+ cx="9.119"
+ gradientTransform="matrix(0 -.60512 2.7541 0 -44.001 27.997)"
+ r="3.177" />
+ <linearGradient
+ id="bc"
+ y2="30.191"
+ gradientUnits="userSpaceOnUse"
+ x2="6.5596"
+ gradientTransform="matrix(1.3054 0 0 .96884 27.658 -4.2992)"
+ y1="28.781"
+ x1="6.5596">
+ <stop
+ stop-color="#fff"
+ offset="0"
+ id="stop70" />
+ <stop
+ stop-color="#d7dbc7"
+ offset="1"
+ id="stop72" />
+ </linearGradient>
+ <linearGradient
+ id="bd"
+ y2="22.713"
+ gradientUnits="userSpaceOnUse"
+ x2="8.5602"
+ gradientTransform="matrix(1.1646 0 0 .37791 27.782 13.975)"
+ y1="29.18"
+ x1="5.3348">
+ <stop
+ stop-color="#eee"
+ offset="0"
+ id="stop75" />
+ <stop
+ stop-color="#a2a2a2"
+ offset="1"
+ id="stop77" />
+ </linearGradient>
+ <linearGradient
+ id="be"
+ y2="24.938"
+ xlink:href="#ax"
+ gradientUnits="userSpaceOnUse"
+ x2="24"
+ gradientTransform="matrix(.67368 0 0 .67368 6.6323 9.8582)"
+ y1="22.125"
+ x1="22.062" />
+ <linearGradient
+ id="bf"
+ y2="26.868"
+ xlink:href="#ax"
+ gradientUnits="userSpaceOnUse"
+ x2="24.082"
+ gradientTransform="matrix(1.0732 0 0 1.0757 2.6528 -.50307)"
+ y1="21.016"
+ x1="21.568" />
+ <radialGradient
+ id="bp"
+ gradientUnits="userSpaceOnUse"
+ cy="62.526"
+ cx="442.29"
+ gradientTransform="matrix(-1.3017e-5,-1.3896,0.25862,0,12.335,648.41)"
+ r="77.923">
+ <stop
+ stop-color="#777"
+ stop-opacity="0"
+ offset="0"
+ id="stop82" />
+ <stop
+ stop-color="#2b2b2b"
+ offset="1"
+ id="stop84" />
+ </radialGradient>
+ <radialGradient
+ id="bq"
+ xlink:href="#az"
+ gradientUnits="userSpaceOnUse"
+ cy="183.64"
+ cx="258.76"
+ gradientTransform="matrix(.17369 -.0023476 .0017845 .13208 -14.213 13.883)"
+ r="18.578" />
+ <radialGradient
+ id="br"
+ gradientUnits="userSpaceOnUse"
+ cy="171.79"
+ cx="251.69"
+ gradientTransform="matrix(.040643 -.16438 .25253 .063961 -21.612 69.314)"
+ r="21.531">
+ <stop
+ stop-color="#eeeeec"
+ offset="0"
+ id="stop88" />
+ <stop
+ stop-color="#090908"
+ stop-opacity=".96622"
+ offset="1"
+ id="stop90" />
+ </radialGradient>
+ <linearGradient
+ id="bg"
+ y2="179.04"
+ xlink:href="#az"
+ gradientUnits="userSpaceOnUse"
+ x2="222.73"
+ gradientTransform="matrix(.13883 0 0 .13367 -4.9888 13.067)"
+ y1="171.62"
+ x1="236.75" />
+ <radialGradient
+ id="bs"
+ gradientUnits="userSpaceOnUse"
+ cy="111.3"
+ cx="439.05"
+ gradientTransform="matrix(.071034 -.056703 .11549 .15363 -18.07 41.978)"
+ r="75.752">
+ <stop
+ stop-color="#eeeeec"
+ offset="0"
+ id="stop94" />
+ <stop
+ stop-color="#babdb6"
+ offset="1"
+ id="stop96" />
+ </radialGradient>
+ <linearGradient
+ id="bh"
+ y2="114.23"
+ gradientUnits="userSpaceOnUse"
+ x2="457.2"
+ gradientTransform="matrix(.072459 0 0 .10109 -4.6279 19.41)"
+ y1="289.78"
+ x1="457.2">
+ <stop
+ stop-color="#2e3436"
+ offset="0"
+ id="stop99" />
+ <stop
+ stop-color="#fff"
+ offset="1"
+ id="stop101" />
+ </linearGradient>
+ <radialGradient
+ id="bt"
+ xlink:href="#ay"
+ gradientUnits="userSpaceOnUse"
+ cy="98.975"
+ cx="434.13"
+ gradientTransform="matrix(-.15022 .15417 -.1987 -.20393 113.38 -16.437)"
+ r="74.908" />
+ <radialGradient
+ id="bu"
+ gradientUnits="userSpaceOnUse"
+ cy="130.89"
+ cx="441.36"
+ gradientTransform="matrix(-.012867 -.088952 .29168 -.044437 -3.9993 74.887)"
+ r="75.756">
+ <stop
+ stop-color="#3a3a3a"
+ offset="0"
+ id="stop105" />
+ <stop
+ stop-color="#3a3a3a"
+ stop-opacity="0"
+ offset="1"
+ id="stop107" />
+ </radialGradient>
+ <radialGradient
+ id="bv"
+ xlink:href="#ay"
+ gradientUnits="userSpaceOnUse"
+ cy="131.83"
+ cx="469.91"
+ gradientTransform="matrix(-.14051 -.13746 .12306 -.13043 81.917 108.28)"
+ r="74.908" />
+ <radialGradient
+ id="bw"
+ xlink:href="#au"
+ gradientUnits="userSpaceOnUse"
+ cy="218.66"
+ cx="344.26"
+ gradientTransform="matrix(-.013724 -.24747 .093642 -.0055498 12.749 122.21)"
+ r="74.908" />
+ <linearGradient
+ id="bi"
+ y2="225.83"
+ xlink:href="#au"
+ gradientUnits="userSpaceOnUse"
+ x2="280.02"
+ gradientTransform="matrix(.093448 0 0 .13038 -14.224 11.368)"
+ y1="126.84"
+ x1="469.36" />
+ <radialGradient
+ id="bx"
+ gradientUnits="userSpaceOnUse"
+ cy="95.382"
+ cx="529.4"
+ gradientTransform="matrix(0 .10037 -.10096 0 38.13 -18.815)"
+ r="74.908">
+ <stop
+ stop-color="#2e3436"
+ stop-opacity="0"
+ offset="0"
+ id="stop113" />
+ <stop
+ stop-color="#2e3436"
+ stop-opacity="0"
+ offset=".47336"
+ id="stop115" />
+ <stop
+ stop-color="#0f1112"
+ offset="1"
+ id="stop117" />
+ </radialGradient>
+ <radialGradient
+ id="by"
+ gradientUnits="userSpaceOnUse"
+ cy="165.52"
+ cx="449.88"
+ gradientTransform="matrix(0 -.096503 .10561 0 11.021 76.299)"
+ r="74.908">
+ <stop
+ stop-color="#eeeeec"
+ stop-opacity="0"
+ offset="0"
+ id="stop120" />
+ <stop
+ stop-color="#eeeeec"
+ stop-opacity=".49804"
+ offset=".86670"
+ id="stop122" />
+ <stop
+ stop-color="#eeeeec"
+ stop-opacity="0"
+ offset="1"
+ id="stop124" />
+ </radialGradient>
+ <radialGradient
+ id="bz"
+ gradientUnits="userSpaceOnUse"
+ cy="170.41"
+ cx="459.45"
+ gradientTransform="matrix(.15094 -2.3254e-8 1.3013e-8 .12469 -40.912 13.001)"
+ r="74.908">
+ <stop
+ stop-color="#eeeeec"
+ stop-opacity=".95270"
+ offset="0"
+ id="stop127" />
+ <stop
+ stop-color="#eeeeec"
+ stop-opacity="0"
+ offset=".47989"
+ id="stop129" />
+ <stop
+ stop-color="#eeeeec"
+ stop-opacity=".99324"
+ offset=".52296"
+ id="stop131" />
+ <stop
+ stop-color="#eeeeec"
+ stop-opacity="0"
+ offset=".63154"
+ id="stop133" />
+ <stop
+ stop-color="#eeeeec"
+ stop-opacity=".23529"
+ offset=".73835"
+ id="stop135" />
+ <stop
+ stop-color="#fff"
+ stop-opacity=".71622"
+ offset=".83401"
+ id="stop137" />
+ <stop
+ stop-color="#f6f6f5"
+ stop-opacity=".27027"
+ offset=".90514"
+ id="stop139" />
+ <stop
+ stop-color="#f2f2f0"
+ stop-opacity=".27703"
+ offset=".90514"
+ id="stop141" />
+ <stop
+ stop-color="#eeeeec"
+ stop-opacity="0"
+ offset="1"
+ id="stop143" />
+ </radialGradient>
+ <radialGradient
+ id="ca"
+ gradientUnits="userSpaceOnUse"
+ cy="217.46"
+ cx="502.53"
+ gradientTransform="matrix(.10492 -.072831 .035871 .057009 -25.672 64.354)"
+ r="74.908">
+ <stop
+ stop-color="#eeeeec"
+ offset="0"
+ id="stop146" />
+ <stop
+ stop-color="#eeeeec"
+ stop-opacity="0"
+ offset="1"
+ id="stop148" />
+ </radialGradient>
+ <radialGradient
+ id="cb"
+ gradientUnits="userSpaceOnUse"
+ cy="33.605"
+ cx="427.8"
+ gradientTransform="matrix(-.44827 .10225 -.081708 -.35819 230.04 -5.144)"
+ r="74.908">
+ <stop
+ stop-color="#eeeeec"
+ offset="0"
+ id="stop151" />
+ <stop
+ stop-color="#eeeeec"
+ stop-opacity="0"
+ offset="1"
+ id="stop153" />
+ </radialGradient>
+ <radialGradient
+ id="cc"
+ gradientUnits="userSpaceOnUse"
+ cy="127.25"
+ cx="399.88"
+ gradientTransform="matrix(.942 -.093118 .12194 .43761 -363.99 7.6542)"
+ r="2.0222">
+ <stop
+ stop-color="#fff"
+ offset="0"
+ id="stop156" />
+ <stop
+ stop-color="#fff"
+ stop-opacity="0"
+ offset="1"
+ id="stop158" />
+ </radialGradient>
+ <radialGradient
+ id="cd"
+ gradientUnits="userSpaceOnUse"
+ cy="127.65"
+ cx="400.3"
+ gradientTransform="matrix(1.0867,3.1905e-5,-1.4474e-5,0.51097,-406.97,-39.597)"
+ r="2.0222">
+ <stop
+ stop-color="#fff"
+ offset="0"
+ id="stop161" />
+ <stop
+ stop-color="#eeeeec"
+ stop-opacity="0"
+ offset="1"
+ id="stop163" />
+ </radialGradient>
+ <linearGradient
+ id="bj"
+ y2="496.34"
+ gradientUnits="userSpaceOnUse"
+ x2="700.41"
+ gradientTransform="matrix(.15392 0 0 .1533 -88.254 -28.438)"
+ y1="327.78"
+ x1="697.91">
+ <stop
+ stop-color="#f0f0f4"
+ offset="0"
+ id="stop166" />
+ <stop
+ stop-color="#eeeeec"
+ offset=".037441"
+ id="stop168" />
+ <stop
+ stop-color="#eeeeec"
+ stop-opacity=".69595"
+ offset=".39254"
+ id="stop170" />
+ <stop
+ stop-color="#a1a29f"
+ offset="0.908"
+ id="stop172" />
+ <stop
+ stop-color="#555753"
+ offset="1"
+ id="stop174" />
+ </linearGradient>
+ <radialGradient
+ id="ce"
+ gradientUnits="userSpaceOnUse"
+ cy="14.782"
+ cx="76.166"
+ gradientTransform="matrix(4.2066 1.9379e-8 0 .1402 -297.74 19.928)"
+ r="21">
+ <stop
+ stop-color="#7a7c7c"
+ offset="0"
+ id="stop177" />
+ <stop
+ stop-color="#33393a"
+ offset="1"
+ id="stop179" />
+ </radialGradient>
+ <linearGradient
+ id="bk"
+ y2="45.818"
+ gradientUnits="userSpaceOnUse"
+ x2="8.5625"
+ gradientTransform="translate(-53.5,9.8117)"
+ y1="4.6468"
+ x1="8.5625">
+ <stop
+ stop-color="#2f3537"
+ offset="0"
+ id="stop182" />
+ <stop
+ stop-color="#8a8e8e"
+ offset="0.3"
+ id="stop184" />
+ <stop
+ stop-color="#2f3537"
+ offset="1"
+ id="stop186" />
+ </linearGradient>
+ <radialGradient
+ id="cf"
+ gradientUnits="userSpaceOnUse"
+ cy="114.57"
+ cx="20.892"
+ gradientTransform="matrix(0.2297,0,0,0.2297,4.6135,3.9798)"
+ r="5.256">
+ <stop
+ stop-color="#f0f0f0"
+ offset="0"
+ id="stop189" />
+ <stop
+ stop-color="#9a9a9a"
+ offset="1"
+ id="stop191" />
+ </radialGradient>
+ <radialGradient
+ id="cg"
+ gradientUnits="userSpaceOnUse"
+ cy="64.568"
+ cx="20.892"
+ gradientTransform="matrix(0.2297,0,0,0.2297,4.6135,3.9798)"
+ r="5.257">
+ <stop
+ stop-color="#f0f0f0"
+ offset="0"
+ id="stop194" />
+ <stop
+ stop-color="#9a9a9a"
+ offset="1"
+ id="stop196" />
+ </radialGradient>
+ <radialGradient
+ id="ch"
+ gradientUnits="userSpaceOnUse"
+ cy="7.2679"
+ cx="8.1436"
+ gradientTransform="matrix(0.83037,0,0,0.95552,-34.846,-1.8031)"
+ r="38.159">
+ <stop
+ stop-color="#fff"
+ offset="0"
+ id="stop199" />
+ <stop
+ stop-color="#f8f8f8"
+ offset="1"
+ id="stop201" />
+ </radialGradient>
+ <radialGradient
+ id="ci"
+ gradientUnits="userSpaceOnUse"
+ cy="35.737"
+ cx="33.967"
+ gradientTransform="matrix(0.8327,0,0,0.97109,-37.62,-2.5748)"
+ r="86.708">
+ <stop
+ stop-color="#fafafa"
+ offset="0"
+ id="stop204" />
+ <stop
+ stop-color="#bbb"
+ offset="1"
+ id="stop206" />
+ </radialGradient>
+ <radialGradient
+ id="cj"
+ gradientUnits="userSpaceOnUse"
+ cy="3.7561"
+ cx="8.8244"
+ gradientTransform="matrix(0.83945,0,0,0.96329,-34.713,-1.9718)"
+ r="37.752">
+ <stop
+ stop-color="#a3a3a3"
+ offset="0"
+ id="stop209" />
+ <stop
+ stop-color="#4c4c4c"
+ offset="1"
+ id="stop211" />
+ </radialGradient>
+ </defs>
+ <g
+ fill="none"
+ id="g213">
+ <path
+ d="m11.506 5.4943v37.907"
+ stroke-opacity=".017544"
+ stroke="#000"
+ stroke-width=".98855"
+ id="path215" />
+ <path
+ stroke-opacity=".20468"
+ d="m12.5 5.0205v38.018"
+ stroke="#fff"
+ id="path217" />
+ </g>
+ <g
+ opacity=".3"
+ transform="matrix(.9138 0 0 1 4.4269 -9.3837)"
+ id="g219">
+ <path
+ fill="url(#ba)"
+ d="m8.8251 42v4.9997c-3.2368 0.009-7.8251-1.12-7.8251-2.5s3.6121-2.4995 7.8251-2.4995z"
+ id="path221" />
+ <rect
+ height="5"
+ width="30.35"
+ y="42"
+ x="8.8251"
+ fill="url(#av)"
+ id="rect223" />
+ <path
+ fill="url(#bb)"
+ d="m39.175 42v4.9997c3.2369 0.0094 7.8251-1.1202 7.8251-2.5002s-3.612-2.5-7.825-2.5z"
+ id="path225" />
+ </g>
+ <g
+ transform="translate(46,33.47034)"
+ id="g227">
+ <rect
+ style="color:#000000;fill:url(#ci);stroke:url(#cj);stroke-width:0.89924002;stroke-linecap:round;stroke-linejoin:round;display:block"
+ display="block"
+ transform="matrix(0,1,-1,0,0,0)"
+ ry="1.0718"
+ width="30.235001"
+ y="0.82638001"
+ x="-31.895"
+ height="38.167999"
+ id="rect229" />
+ <rect
+ style="color:#000000;fill:none;stroke:url(#ch);stroke-width:0.89074999;stroke-linecap:round;stroke-linejoin:round;display:block"
+ transform="matrix(0,1,-1,0,0,0)"
+ display="block"
+ rx="0.12782"
+ ry="0.13789999"
+ height="36.033001"
+ width="28.108"
+ y="1.8398"
+ x="-31.148001"
+ id="rect231" />
+ <g
+ transform="matrix(0,1.3362,-1.0851,0,9.545,-39.685)"
+ id="g233">
+ <g
+ transform="matrix(0.2297,0,0,0.2297,4.9671,4.245)"
+ id="g235"
+ style="fill:#ffffff;stroke:#000000">
+ <path
+ d="m 23.428,113.07 c 0,1.973 -1.6,3.572 -3.573,3.572 -1.974,0 -3.573,-1.6 -3.573,-3.572 0,-1.974 1.6,-3.573 3.573,-3.573 1.973,0 3.573,1.6 3.573,3.573 z"
+ id="path237" />
+ <path
+ d="m 23.428,63.07 c 0,1.973 -1.6,3.573 -3.573,3.573 -1.974,0 -3.573,-1.6 -3.573,-3.573 0,-1.974 1.6,-3.573 3.573,-3.573 1.973,0 3.573,1.6 3.573,3.573 z"
+ id="path239" />
+ </g>
+ <path
+ d="m 9.995,29.952 c 0,0.4532 -0.36752,0.8205 -0.82073,0.8205 -0.45343,0 -0.82073,-0.36752 -0.82073,-0.8205 0,-0.45343 0.36752,-0.82073 0.82073,-0.82073 0.4532,0 0.82073,0.36752 0.82073,0.82073 z"
+ id="path241"
+ style="fill:url(#cf)" />
+ <path
+ d="m 9.995,18.467 c 0,0.4532 -0.36752,0.82073 -0.82073,0.82073 -0.45343,0 -0.82073,-0.36752 -0.82073,-0.82073 0,-0.45343 0.36752,-0.82073 0.82073,-0.82073 0.4532,0 0.82073,0.36752 0.82073,0.82073 z"
+ id="path243"
+ style="fill:url(#cg)" />
+ </g>
+ <path
+ style="color:#000000;fill:none;stroke:#9b9b9b;stroke-width:1.20410001;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.74510001"
+ d="M -22.444,-23.794 H -33.295"
+ id="path245" />
+ <path
+ style="color:#000000;fill:none;stroke:#9b9b9b;stroke-width:1.20410001;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.74510001"
+ d="m -22.444,-17.113 -3.2554,-6.6808"
+ id="path247" />
+ <path
+ style="color:#000000;fill:none;stroke:#9b9b9b;stroke-width:1.20410001;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.74510001"
+ d="m -33.295,-17.113 3.2554,-6.6808"
+ id="path249" />
+ <path
+ style="color:#000000;fill:none;stroke:#9b9b9b;stroke-width:1.1494;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.74510001"
+ d="m -27.825,-24.512 v -4.7243"
+ id="path251" />
+ <path
+ style="color:#000000;fill:none;stroke:#9b9b9b;stroke-width:1.20410001;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.74510001"
+ d="m -33.295,-17.113 h -3.2554"
+ id="path253" />
+ <path
+ style="color:#000000;fill:none;stroke:#9b9b9b;stroke-width:1.20410001;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.74510001"
+ d="m -36.639,-17.148 v -5.3447"
+ id="path255" />
+ <path
+ style="color:#000000;fill:none;stroke:#9b9b9b;stroke-width:1.20410001;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.74510001"
+ d="m -22.444,-17.113 h 6.5107"
+ id="path257" />
+ <path
+ style="color:#000000;fill:none;stroke:#9b9b9b;stroke-width:1.20410001;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.74510001"
+ d="m -15.934,-15.777 v -2.6723 h 7.5958 v 2.6723 h -7.5958"
+ id="path259" />
+ <path
+ style="color:#000000;fill:none;stroke:#9b9b9b;stroke-width:0.99326003;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.74510001"
+ d="m -19.189,-17.306 v 7.2734"
+ id="path261" />
+ <path
+ style="color:#000000;fill:none;stroke:#9b9b9b;stroke-width:1.20410001;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.74510001"
+ d="M -5.0823,-17.113 H -8.3377"
+ id="path263" />
+ <path
+ style="color:#000000;fill:#9b9b9b;fill-opacity:0.74510001;fill-rule:evenodd"
+ d="m -33.295,-17.113 3.0519,-2.9229 -2.645,-1.5032 -0.40692,4.426 z"
+ id="path265" />
+ </g>
+ <g
+ transform="matrix(.92391 0 0 .875 -.53806 5.8515)"
+ id="g267">
+ <g
+ opacity=".3"
+ transform="translate(-1.5002,1.8117)"
+ id="g269">
+ <path
+ fill="url(#ba)"
+ d="m8.8251 42v4.9997c-3.2368 0.009-7.8251-1.12-7.8251-2.5s3.6121-2.4995 7.8251-2.4995z"
+ id="path271" />
+ <rect
+ height="5"
+ width="30.35"
+ y="42"
+ x="8.8251"
+ fill="url(#av)"
+ id="rect273" />
+ <path
+ fill="url(#bb)"
+ d="m39.175 42v4.9997c3.2369 0.0094 7.8251-1.1202 7.8251-2.5002s-3.612-2.5-7.825-2.5z"
+ id="path275" />
+ </g>
+ <rect
+ stroke-linejoin="round"
+ rx="2.4749"
+ ry="2.1004"
+ height="26.99"
+ width="40.99"
+ stroke="url(#bk)"
+ stroke-linecap="round"
+ y="19.317"
+ x="2.005"
+ stroke-width="1.01"
+ fill="url(#ce)"
+ id="rect277" />
+ <rect
+ rx="1.6573"
+ ry="1.6573"
+ height="24"
+ width="40"
+ y="21.812"
+ x="2.4998"
+ fill="url(#bj)"
+ id="rect279" />
+ <path
+ opacity=".27228"
+ fill="url(#cd)"
+ d="m30.74 24.195a2.7165 2.6372 0 0 1 -5.433 0 2.7165 2.6372 0 1 1 5.433 0z"
+ id="path281" />
+ <path
+ fill="url(#cc)"
+ d="m30.727 25.473a2.7042 1.8094 0 0 1 -5.4083 0 2.7042 1.8094 0 1 1 5.4083 0z"
+ id="path283" />
+ <path
+ fill="url(#cb)"
+ d="m38.5 33.812a10 10 0 0 1 -20 0 10 10 0 1 1 20 0z"
+ id="path285" />
+ <path
+ fill="#888a85"
+ d="m37.5 33.812a9 9 0 0 1 -18 0 9 9 0 1 1 18 0z"
+ id="path287" />
+ <path
+ fill="url(#ca)"
+ d="m39.5 33.812a11 11 0 0 1 -22 0 11 11 0 1 1 22 0z"
+ id="path289" />
+ <path
+ fill="url(#bz)"
+ d="m38.437 34.25a10 10 0 0 1 -20 0 10 10 0 1 1 20 0z"
+ id="path291" />
+ <path
+ fill="url(#by)"
+ d="m36.569 33.475a8.0691 7.8189 0 0 1 -16.138 0 8.0691 7.8189 0 1 1 16.138 0z"
+ id="path293" />
+ <path
+ opacity=".85149"
+ fill="url(#bx)"
+ d="m35.5 33.812a7 7 0 0 1 -14 0 7 7 0 1 1 14 0z"
+ id="path295" />
+ <path
+ fill="url(#bi)"
+ d="m35.5 33.812a7 7 0 0 1 -14 0 7 7 0 1 1 14 0z"
+ id="path297" />
+ <path
+ fill="url(#bw)"
+ d="m35.5 34.812a7 7 0 0 1 -14 0 7 7 0 1 1 14 0z"
+ id="path299" />
+ <path
+ opacity=".53960"
+ fill="url(#bv)"
+ d="m34.234 34.76a5.7665 6.0002 0 0 1 -11.533 0 5.7665 6.0002 0 1 1 11.533 0z"
+ id="path301" />
+ <path
+ stroke-linejoin="round"
+ d="m34.176 35.312a5.676 5.426 0 0 1 -11.352 0 5.676 5.426 0 1 1 11.352 0z"
+ stroke="url(#bu)"
+ stroke-linecap="round"
+ stroke-width=".1485"
+ fill="url(#bt)"
+ id="path303" />
+ <path
+ stroke-linejoin="round"
+ d="m33.928 36.812c0 2.9961-2.4316 5.4277-5.4277 5.4277s-5.4277-2.4316-5.4277-5.4277 2.4316-5.4277 5.4277-5.4277 5.4277 2.4316 5.4277 5.4277z"
+ stroke="url(#bh)"
+ stroke-linecap="round"
+ stroke-width=".14459"
+ fill="url(#bs)"
+ id="path305" />
+ <path
+ fill="#2e3436"
+ d="m33 36.812a4.5 4.5 0 0 1 -9 0 4.5 4.5 0 1 1 9 0z"
+ id="path307" />
+ <path
+ opacity=".23267"
+ fill="url(#bg)"
+ d="m27.724 34.278c-0.08496 0.02653-0.16639 0.05752-0.24728 0.0919 0.08079-0.03461 0.1624-0.06518 0.24728-0.0919zm1.77 0.05013c0.035 0.01321 0.06988 0.02718 0.10412 0.04177-0.03437-0.01461-0.06898-0.02856-0.10412-0.04177zm-2.308 0.18798c-0.05662 0.03268-0.11114 0.06787-0.16486 0.10443 0.05331-0.03657 0.10869-0.07167 0.16486-0.10443zm-0.16486 0.10443c-0.06461 0.04398-0.12648 0.08859-0.18655 0.13785 0.05929-0.04879 0.12287-0.09417 0.18655-0.13785zm3.2754 0.18798c0.03712 0.0324 0.07334 0.06589 0.10846 0.10026-0.03511-0.03423-0.07136-0.06799-0.10846-0.10026zm-3.5271 0.0084c-0.04762 0.0418-0.09456 0.08446-0.13883 0.12949 0.04437-0.04514 0.09114-0.08753 0.13883-0.12949zm-0.13883 0.12949c-0.33737 0.34318-0.57623 0.77514-0.6681 1.2574-0.000256 0.0014 0.000257 0.0028 0 0.0042v0.88976c0.13918 0.7375 0.61747 1.3607 1.2755 1.721h2.5987c0.63044-0.34525 1.0935-0.93177 1.2538-1.6291v-1.0652c-0.072-0.315-0.203-0.61-0.387-0.868-0.142-0.184-0.307-0.317-0.485-0.389h-3.488c-0.03512 0.02614-0.06746 0.05237-0.09978 0.07937zm-0.6681 2.1513c-0.01352-0.07159-0.02313-0.14322-0.03037-0.21722 0.007 0.07375 0.01683 0.14545 0.03037 0.21722zm0-0.88976c-0.01357 0.07192-0.0234 0.14331-0.03037 0.21722 0.0072-0.07297 0.01685-0.14567 0.03037-0.21722zm4.5596-1.1822c0.02542 0.02829 0.04966 0.05836 0.07375 0.08772-0.02444-0.02974-0.04796-0.05908-0.07375-0.08772zm-2.828 3.991c0.05283 0.01722 0.10623 0.03193 0.16052 0.04595-0.05432-0.01407-0.10765-0.02866-0.16052-0.04595zm1.6876 0c-0.05312 0.01729-0.10592 0.03188-0.16052 0.04595 0.05429-0.01402 0.10769-0.02874 0.16052-0.04595z"
+ id="path309" />
+ <path
+ opacity=".37557"
+ fill="url(#br)"
+ d="m26.234 33.812c-0.535 0.385-0.965 0.904-1.234 1.501v2.946c0.28195 0.62634 0.73926 1.1627 1.3106 1.5527h4.4296c0.538-0.369 0.975-0.868 1.259-1.45v-3.1465c-0.271-0.557-0.682-1.041-1.189-1.404h-4.5769z"
+ id="path311" />
+ <path
+ fill="#2e3436"
+ d="m27.734 34.215c-0.08393 0.02688-0.16438 0.05827-0.24429 0.0931 0.07981-0.03506 0.16044-0.06603 0.24429-0.0931zm1.7486 0.05078c0.03457 0.01338 0.06903 0.02754 0.10286 0.04232-0.03396-0.0148-0.06815-0.02893-0.10286-0.04232zm-2.28 0.19043c-0.05593 0.03311-0.1098 0.06876-0.16286 0.1058 0.05266-0.03705 0.10737-0.07261 0.16286-0.1058zm-0.16286 0.1058c-0.06383 0.04455-0.12495 0.08974-0.18429 0.13965 0.05858-0.04943 0.12138-0.0954 0.18429-0.13965zm3.2357 0.19043c0.03667 0.03282 0.07245 0.06675 0.10715 0.10157-0.03468-0.03468-0.07049-0.06888-0.10715-0.10157zm-3.4843 0.0085c-0.04705 0.04235-0.09341 0.08557-0.13714 0.13119 0.04384-0.04573 0.09004-0.08867 0.13714-0.13119zm-0.138 0.132c-0.33328 0.34766-0.56925 0.78528-0.66001 1.2738-0.000253 0.0014 0.000254 0.0029 0 0.0042v0.90138c0.13749 0.74714 0.60999 1.3785 1.26 1.7435h2.5672c0.6228-0.34977 1.0802-0.94395 1.2386-1.6504v-1.0792c-0.07047-0.31936-0.2005-0.61814-0.38143-0.88022-0.14074-0.1859-0.30357-0.32067-0.48-0.39356h-3.4458c-0.03469 0.02648-0.06665 0.05306-0.09857 0.0804zm-0.66001 2.1794c-0.01335-0.07252-0.02285-0.14509-0.03-0.22006 0.0069 0.07472 0.01662 0.14735 0.03 0.22006zm0-0.90138c-0.01341 0.07286-0.02312 0.14518-0.03 0.22006 0.0071-0.07392 0.01665-0.14757 0.03-0.22006zm4.5043-1.1976c0.02511 0.02866 0.04905 0.05912 0.07286 0.08887-0.02414-0.03013-0.04738-0.05985-0.07286-0.08887zm-2.7943 4.0414c0.05219 0.01744 0.10494 0.03235 0.15857 0.04655-0.05367-0.01425-0.10635-0.02904-0.15857-0.04655zm1.6672 0c-0.05248 0.01752-0.10464 0.0323-0.15857 0.04655 0.05363-0.0142 0.10638-0.02911 0.15857-0.04655z"
+ id="path313" />
+ <path
+ opacity=".23267"
+ fill="url(#bq)"
+ d="m27.734 34.153c-0.08393 0.02723-0.16438 0.05903-0.24429 0.0943 0.07981-0.03551 0.16044-0.06688 0.24429-0.0943zm1.7486 0.05144c0.03457 0.01355 0.06903 0.02789 0.10286 0.04286-0.034-0.015-0.069-0.029-0.103-0.043zm-2.281 0.193c-0.05593 0.03353-0.1098 0.06965-0.16286 0.10716 0.05266-0.03753 0.10737-0.07354 0.16286-0.10716zm-0.16286 0.10716c-0.06383 0.04513-0.12495 0.0909-0.18429 0.14145 0.05858-0.05006 0.12138-0.09663 0.18429-0.14145zm3.2357 0.19289c0.03667 0.03325 0.07245 0.06761 0.10715 0.10288-0.03468-0.03512-0.07049-0.06977-0.10715-0.10288zm-3.4843 0.0086c-0.04705 0.0429-0.09341 0.08667-0.13714 0.13288 0.04384-0.04632 0.09004-0.08982 0.13714-0.13288zm-0.138 0.133c-0.33328 0.35214-0.56925 0.7954-0.66001 1.2902-0.000253 0.0014 0.000254 0.0029 0 0.0043v0.91301c0.13749 0.75678 0.60999 1.3962 1.26 1.766h2.5672c0.6228-0.35428 1.0802-0.95612 1.2386-1.6717v-1.0931c-0.071-0.324-0.201-0.627-0.382-0.892-0.141-0.188-0.303-0.325-0.48-0.399h-3.4458c-0.03469 0.02682-0.06665 0.05374-0.09857 0.08144zm-0.66 2.207c-0.01335-0.07346-0.02285-0.14697-0.03-0.22289 0.0069 0.07568 0.01662 0.14925 0.03 0.22289zm0-0.91301c-0.01341 0.0738-0.02312 0.14705-0.03 0.2229 0.0071-0.07488 0.01665-0.14948 0.03-0.2229zm4.5043-1.2131c0.02511 0.02903 0.04905 0.05988 0.07286 0.09002-0.02414-0.03052-0.04738-0.06063-0.07286-0.09002zm-2.7943 4.0935c0.05219 0.01767 0.10494 0.03277 0.15857 0.04715-0.054-0.015-0.107-0.03-0.159-0.048zm1.6672 0c-0.05248 0.01774-0.10464 0.03271-0.15857 0.04715 0.05363-0.01438 0.10638-0.02949 0.15857-0.04715z"
+ id="path315" />
+ <path
+ fill-opacity=".50543"
+ d="m28.512 34.806c-1.0298-0.000002-1.8752 0.77295-1.9851 1.7632-0.0046 0.04174-0.008 0.08347-0.0099 0.12595-0.0014 0.0304-0.005 0.05994-0.005 0.09068 0 0.0067-0.000067 0.01348 0 0.02015-0.000067 0.0067 0 0.01346 0 0.02015 0.000605 0.03001 0.003 0.06102 0.005 0.09068 0.002 0.04247 0.0053 0.08421 0.0099 0.12595 0.10987 0.99028 0.95525 1.7632 1.9851 1.7632s1.8752-0.77295 1.9851-1.7632c0.0046-0.04174 0.008-0.08347 0.0099-0.12595 0.0024-0.03624 0.0046-0.07407 0.005-0.11083-0.00037-0.03675-0.0026-0.0746-0.005-0.11083-0.002-0.042-0.005-0.084-0.01-0.126-0.11-0.99-0.955-1.763-1.985-1.763z"
+ id="path317" />
+ <path
+ stroke-linejoin="round"
+ d="m39.016 33.812a10.516 10.516 0 0 1 -21.033 0 10.516 10.516 0 1 1 21.033 0z"
+ stroke="url(#bp)"
+ stroke-linecap="round"
+ fill="none"
+ id="path319" />
+ <path
+ d="m29.013 24.307a2.0122 2.017 0 0 1 -4.0244 0 2.0122 2.017 0 1 1 4.0244 0z"
+ fill-rule="evenodd"
+ stroke="url(#bf)"
+ stroke-miterlimit="10"
+ stroke-width=".97678"
+ fill="#2e3436"
+ id="path321" />
+ <path
+ opacity=".34706"
+ fill-rule="evenodd"
+ d="m27.834 24.314a0.81921 0.82118 0 0 1 -1.6384 0 0.81921 0.82118 0 1 1 1.6384 0z"
+ id="path323" />
+ <path
+ opacity=".30588"
+ d="m28.417 16.445a0.8397 0.8397 0 1 1 -1.6794 0 0.8397 0.8397 0 1 1 1.6794 0z"
+ fill-rule="evenodd"
+ transform="matrix(.6932 0 0 .69486 8.2648 13.585)"
+ filter="url(#ck)"
+ fill="#fff"
+ id="path325" />
+ <path
+ d="m23.18 25.395a1.2632 1.2632 0 0 1 -2.5263 0 1.2632 1.2632 0 1 1 2.5263 0z"
+ fill-rule="evenodd"
+ stroke="url(#be)"
+ stroke-miterlimit="10"
+ stroke-width=".47368"
+ fill="#2e3436"
+ id="path327" />
+ <rect
+ rx=".93042"
+ ry=".79550"
+ height="2.2404"
+ width="8.2404"
+ stroke="url(#bd)"
+ y="23.192"
+ x="32.88"
+ stroke-width=".75957"
+ fill="url(#bc)"
+ id="rect329" />
+ <path
+ d="m17.228 20.683a3.7307 1.6168 0 0 1 -7.4614 0 3.7307 1.6168 0 1 1 7.4614 0z"
+ stroke-opacity=".99608"
+ fill-rule="evenodd"
+ stroke="url(#bo)"
+ stroke-miterlimit="10"
+ stroke-width=".4957"
+ fill="#2e3436"
+ id="path331" />
+ <path
+ fill-rule="evenodd"
+ fill="url(#bn)"
+ d="m16.5 20.062a3 1.25 0 0 1 -6 0 3 1.25 0 1 1 6 0z"
+ id="path333" />
+ <rect
+ stroke-linejoin="round"
+ rx=".51201"
+ ry=".55650"
+ height="1.4607"
+ width="1.4429"
+ stroke="url(#bm)"
+ stroke-miterlimit="0"
+ y="20.772"
+ x="12.662"
+ stroke-width=".30218"
+ fill="#2e3436"
+ id="rect335" />
+ <rect
+ opacity=".4"
+ stroke-linejoin="round"
+ rx="1.3157"
+ ry="1.3157"
+ height="22.999"
+ width="38.999"
+ stroke="url(#cl)"
+ stroke-linecap="round"
+ y="22.312"
+ x="3.0002"
+ stroke-width="1.0008"
+ fill="none"
+ id="rect337" />
+ <path
+ opacity=".8"
+ d="m32 37.11c0 1.933-1.567 3.5-3.5 3.5s-3.5-1.567-3.5-3.5 1.567-3.5 3.5-3.5 3.5 1.567 3.5 3.5z"
+ fill-rule="evenodd"
+ fill="url(#bl)"
+ id="path339" />
+ <rect
+ opacity=".5"
+ rx="1"
+ ry="1"
+ height="10"
+ width="2"
+ y="29.812"
+ x="9.4998"
+ fill="#eeeeec"
+ id="rect341" />
+ <rect
+ opacity=".2"
+ rx="1"
+ ry="1"
+ height="8"
+ width="2"
+ y="30.812"
+ x="13.5"
+ fill="#555753"
+ id="rect343" />
+ </g>
+</svg>
diff --git a/resources/linux/mime/icons/hicolor/scalable/apps/cvpcb.svg b/resources/linux/mime/icons/hicolor/scalable/apps/cvpcb.svg
new file mode 100644
index 0000000..e4bc2f0
--- /dev/null
+++ b/resources/linux/mime/icons/hicolor/scalable/apps/cvpcb.svg
@@ -0,0 +1,41 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs>
+ <linearGradient id="d" y2="-151.35" gradientUnits="userSpaceOnUse" x2="682.52" gradientTransform="matrix(-1,0,0,1,921.6,341.5)" y1="-167.43" x1="747">
+ <stop stop-color="#fcaf3e" offset="0"/>
+ <stop stop-color="#fcaf3e" stop-opacity="0.65" offset="1"/>
+ </linearGradient>
+ <linearGradient id="e" y2="-2857.7" gradientUnits="userSpaceOnUse" x2="-1416.8" gradientTransform="matrix(.2344 0 0 .2344 550.77 853.66)" y1="-2902.3" x1="-1597">
+ <stop stop-color="#fcaf3e" offset="0"/>
+ <stop stop-color="#f2983d" stop-opacity=".75660" offset=".49680"/>
+ <stop stop-color="#e77c3c" stop-opacity="0.51" offset="1"/>
+ </linearGradient>
+ <radialGradient id="f" gradientUnits="userSpaceOnUse" cy="-969.15" cx="-1035.3" gradientTransform="matrix(.4465 -.2703 -.2019 -.3361 447.65 -605.27)" r="76.859">
+ <stop stop-color="#fffbd7" offset="0"/>
+ <stop stop-color="#e77c3c" offset="1"/>
+ </radialGradient>
+ </defs>
+ <rect style="color:#000000" height=".67940" width="0" stroke="#000" y="32.209" x="7.913" stroke-width=".69494"/>
+ <image xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAABHNCSVQICAgIfAhkiAAAFaFJREFU eJztXWuQHNV1/rp7uqdnemZ2dlerfeixi0AIbD0WUY5Rgc26RMqmqBRKuUj8I0nZVU4oV8WxKomN YjBag7FXwQ6UHUgUUgT/MZIpqpZKiVAVGwTCNmALpBhsQNZKSNqHVjuP3Xl2973d+dGP6Xnt7uxs z/Zo5tu62zNz+3af7j59zrnnnD7N6LqONloX7FoT0Mbaos0ALY42A7Q42gzQ4mgzQIujzQAtjjYD tDjaDNDiaDNAi6PNAC2ONgO0ONoM0OJoM0CLw+fmxufnF2pa/+jRIwCA7du3Y9u2bRAEAff82Rd2 AXiGZZlhAGAYBgDAgCkeXPK1v78fd911F3w+4xA5jgMAsGxlnreiotaSUgoAIITg2LFjmJ6eLhlQ tACs8eYvmqafArD/uZ8eeVVRFHzwwQd49913AQD33nvvEmeiMm1ugHFz47UwwNGjRxCNRnHLLbeg s7MTAPD5z9+zi+W4U/39/fjEJ24G4LzwizPAu7/9LW697TabAawLX50BrKUGANA0Y0kIwcsvv4wd 27dD8PsdA8q2YP43lr/+9UlMT09Do3T4+eefOw0AiUQCb7zxBpLJZE1M4OY18oQKsC7+3r177Yt/ 4cIFMCw76vP54PNx4Dif2Tij+Yxm9Pvg44qbDgaJRAKKokBRFBBKQCgBpRSUUmiaVtQoJaCUgBCj WePi8Ti2XrcVGzduKt6HuV+LDpsus/lM2hiWHU2lUgCAzs5O7N27F9FoFIcPH17LU25jzSXA0aNH wPM8Pve5zyEUCgEAxsbGcOjQIezde4fu43kADhFuqgBYqoBhyjdqYuvW63HttVsAoEwVWONKRb/z zgeAs2cnMDy8CxcufIQ333yrbB96QXQY40tViKpizy23MLfediv27NkDXdeRTqfx0ksvQVXVZUkC N6+RqzbAcsnevXu3ffG/8pWv4NlnnzUuEMOCZY0LVtDhXNHYxRhgcvISenrWgfP5wFdhAJtW8yQT x4UjhCIQEBGJRDAxcQ4cV366yi6OOd7+mTEkzmuvvoZkMok777wToVAIu3fvxptvvrnoeWkE3FUB ur5o++nRI5AkCVu2GHfpk08+iZ/85CdF4/UqzdpGtX5d15HPyzg7MYFMJmO39BLNue6lS5dw4w03 YHp6GrFYfFl0VPpOiKF6Tr19Cid/cxIAsGXLFkiStOaqwFUJsBxs27YNAJBMJjE2NlbcyTisfoY1 l9Xv+Eq4MnsFDGDbFtUkgKYVi+5kMoHrr78eoVAIP3/5leo7sGcl5lfTyGRMVQIG0KhmH8srr7yC HTt3gOd5bNu2DW+//XZNx7PaWHMGiEajAIBjx47hm/c/AEHwA4yhs18YHy/T0SvRh5cvzyKflxGO hMGxi6sASilSqRR27tiBrdddh9dOvI5YLLbsfVWil1AChmHAgEE2k8X777+PHTt22Me+llhzBujt 7QUAKCqF3y9iaGjIsKAr6Ftg5QYRIQSTlyYRCATg431gmWLtp+kaiEoQjXZg5PZPo6u7C6+9dgJn /vCHFe2vaNsOCcCAwcTZCezcudM+9rXEmjMAAPzwR/+KYDAIAKCUWFK1oEdRvyUsyzJ27tyBcDiE WCyORCIBlmURi8XR2RmFJEnYuvU69Pf1I5VO4cUX/wfT0zM176eSBKCUFjFAPB4HwzCuWvfLhbuz gGUeIMOwYEzRfHl2FgDA84K1lZLlyqAoMt555x0IgoDBzZvxsRtvRCgUQnd3N2RZRjweRywWx7vv vocLFy7UsadyeqlGDQcWYziyNKrVbMu4BVcZ4D/PXAbDlnjunMd982dw+MMZ6B2doPkMAICQgo5e TQlgQZZlfHjmDD48c2ZVtleKShJg8ItfhW4al5QQZAE8+svf4u8/+TFXaKgFrjqCnjwzB4Zj8c7Y gQ6O4/YBGLJcsUyJI6fSHfHe734/KgiGJLAcOV65c6qhNJYgyzI+/rEbR6utV8URdZ5SOv7UU0/N O/vcgKsMcDqRwxP3fW0XgHHB7x8KBIIIBEQIggBRFOEX/OBNT19BUpiEAThy9Dn4TQbgmpgBvvDn 95QpMHs9QiArCuS8DFmRkc/lkclmIOfz5wHse+qpp043rSeQ5XlwHPcMw7BD0c4uhMNhdHV1IRIO o7OzE5FIBJIkGetWkAzPHjlqB1esIE1Z1MdjKNzRFr0aPvXp2x13sR0tBADk83ksLCwgkUgilVpA PJ5AKpXClSuXhzRKnwFwk5v0uuoJfOzv/uZuXdeHwaxwN4s7EpumrQQs54OuY/hLX/rS3SvbwvLg 9jRwWNN1cCxjR8l4Hw8fz0MQBFsVAJUlgI7VNwLdRrluBwKBQJlkoFSz1/P7/RAEHjwvwIp++jgf CKvC5/MNA3jBLXo94QeoCuct1CQMUEavx+luCAOUZe9UWseO8hbW1fRCeJWxlqtP3qrCtuZLlrWh cUfpbQkAHavlCGocSun1Nt1uM8B5wJjbatRohKggKoGiqlAUBfl8HgDAcZah6LABXHAEuY1K8/tc Lufot1LOjH5ZliErChRFhaoqdkYSoQSaRkEIOe8mvW4zwDjHskkGWFHY62phgBVuCCzDJAGMrxJp FeEqA+x68In59777tf2U0mdSqXmwHAtR9BuzAZ6zkyWAgg1QkACwEyuA5mEAlNKr60gk4g5bUHeu BkWRkUqlkcmkkc1mkcmkMZ9MIJ/LAsD+7r/4x3k3yXXVE/jYuSwA4Pff27+L47hRACMsy0aB5bmC T779th0UqpbI4T0Y55MQwxOoqgpu3r27fK3qruAkgOOU0sd7vnzw1XxqHj/Y617MoCHRwBsOPHYa wJ/WOv43f/nZ0pzLpoGT7qH932N0M0NIzhpBr3TsCgBg6MyvsGfPHiiKgv8WttjjKVEBAD6/6Cqd rjKAVudFM9wATaYCTDgdQbcn3i9fIWIub74ZiqIAAP5EmShfL+ASgSbclQCrwwGFz80EB90bNmxc W1oWgcsSoL6L1oyzAAtOugNmtpMX4W0VYP5Zn5sLDro9zLyeVgFNLQHQHHR7WgU4bQCvn8hSFML/ 3qbb0wxQpAI8fiJLYUsujysvl/0A9Y8vGNNePo3lcE4DvSwF3JUADhtg5okDdwMYLn1ev9Sz5/ze 1DaAg+6DBx88WPp76XfrqWQzl/DU2Ngh15JAnHBdBcw++U8dAI4HAoFhKRRGSJIg+P2QpCD8fj/8 ZtEF60kdKzmUYRiceP0XVwUD3HrbbaP2d83KEzAuuKqqkGUZ2WwOiiwjnUkjtZDCgQP3nQIwMjZ2 yNVYQCNcwccZlh0ORToQDoXR3d2NcDiM7u4uRCIR+7Hw0goeRjAIzRJWL4eD7ptuusnWAtadbi2t pFAjGXQBvlgMHOeDrMjDGqXHdZeTQl1lgKkffv1uv98/zPmMXDee5yH4Bfj9foiiCFEM2I+EVarh c7WogGBQsn8vZQCGYaCoKvxiFrLihyAI4HkBYiAIJZ8fPnDfN+4+fPhw0+YEDlNNB7f0ehWhF9nQ uuO/91FK94q2YTBRcyeFMgzMByNZMAwDlmXBcqyZJczad/6SEqCsLJe34aTbOjag3Oi16wqxLDiW Nc4Py4JlWDAs63r429M5gcUqwP51zeipBeV0exOuM0ChnIoGXdft/ECjWpdmP0LlXN/x5aqwASil VZ8LsKqWUU0zGi2uXub2cbvNAMn6RNjVEQ5eKcxTl1wNcqrBbQZ4Bro+qlEaJSqBqipQZAWyICOX z4Pnefh8lu4vrwV0dSSE6MhkMmUSoPBsYA75XA5yPg9ZlqEoMhRFgaoqoIQkVVV1NSnU1WcDw19+ ZB7Afo0aF1+rMTpYWpGrWVst0DQNspyHqsgAsP/R7//go5o2UCNcTQr96utTAIDM0w/czvP8fgD7 anEF/+xnPy8r8NgscNYavuOOvfbvy3QFjyuK8vijj37/VQDo7u5yjU5XGeBvT0zVNf7le/9YvxoY 4MSJE3XN5dxkAI+Hg68KT7Cn0bBo4IrQxNNA5yzAy5R7okpY1fFNWB/AQrPkMno7KdSRTNF0DOB0 A3iYdk/bAObZc3wuBs/zYDkOGqUQRRFWXX5PoEkcWE2QFVz4XIp169aBNQMpAMxqW/m69rlacKou L7OApyXAUvkArBlBcwzwjKpoFtulqRng0uQkBJ5HOBJGaiFlF5twQpIkRKNRzF65AtV8Bq8RKKLb w0zQsKzg0Av/vAvAvloqhS7FAJQQ5AhBb18fZi/PFvV1d3cjGo2C4zjMzs5CkeXVOKRlw0n3ww8/ dHCxfufS9AiOP/TQw6cbQafrfgBpfKwDwDjL8yOBYBDBYHDZlUKPHXtxUYdKNBpFNBrFwvx8xUqc xAzD6lXGuwnn/rZv3z5aiT6gvFJoNpNFJpMevf/+bx4HsO87j3y3uZNCOY4bZxh2pLN7Xc2VQscO HbKjZ5WkaEdHB86dm8DmTZsxF5sr6puLzWEuNgcpaKiAdDoNVW2kCjDphr7SSqEjGqXjAD7jJp2u RgPF5x+5W9f1kZVWCrXUZ7WWy+UwODgEhmUxODgEv1+0+3p7+zA4OISBgQF0RKNQFGXJ7bnVVgKz UujIgfu+0eyVQrHySqFL2AD5XA7UrDEEGOLUWi82N4ee9T2AruPS5KWGW+NOuldaKZRyV0Wl0DpO vPMWqnABE4kEJEkCy7HIZDLGq1lMbNgwAFEUkc/nsb6nB5eUSaiKunJaasUqOIIawbOerhS6lARY v369fZF7enpw9g9n7b6pqemiELIiN07/A/X6AdqVQgEszQDBYBDnzp2z+4PBIDIZowiTXxQhmDMM wPASrpkfwMNwPSkUunuVQnO5HIaGhuzv8UTcXk80nz6q1NcIOK3+OiuFNnVS6DjHsY+vdPBSDDA1 VT3jaGpqCpIkQZIkzM7OVl3PLRToXvEGrPckN2+l0Nk7v/7RwP8+9kVQ+kw6nQIv8Mhms8uvFFpH jaDOaCekkASBF7B+/XrjTeKN9AM46K6lUqiczyO1MG9VCv3id7835mpSqOvRwMm9+3+84eePJ/O5 7Oj0ZHb48rRx1y7LFYyldWlIMp4uzuVyoFrhIRNCCS7PXIaiKohEIpCVtXMFH3n22UX7nUvTFXwK wOhDD3/H9RoBriaF3nnkvbrG/98/fFYvfWzcic2Dm6EqKiilxhu+z03YU8HNg5tx4SPj/X/9/f1I JBIVg0VuwfkU8MmTJ+sy6/v63HvDqMczgha3AXRdRzqdBqUUftEP3scjT4yLTAlFX18fFFVBOBxe 1F5wA+18ANQ/BVqOEbhp4yYAwPzCfJG1ffHiRXR2doLlWJydOLumnkAvw9NZwcbJq34i+/r6kM1m QSlFOBzGfHIeqlrw9sXj8br2Xx/aDLBKCSH2tworGMafoiq2S1hXjPUGBvohioVKyxMTFQoxu4iC J7iOiFAD4PEycYuneFlivjMaRTwRRy5bUAGTk1Po7OrEunXrsDC/sKYqwLuX3+MSAFicAQYHB8EL vGkEishlc7YdMDQ0BEmSkEgkoOs6WJYtq0XgJkrj/16Ft7OCsbgDaHJyEr29xhRpbm4OWcN5AgCY mZkpCgYZd2IDXcFNUtPI0xJgKRWQzWVx7vw5XLvlWsiyXLRuNBpFIFCwAS5evAhCSaXNuIJ2UiiK /QA3/OJHuwCMoIY3iL2/7JOnQynx9E1NTS53N+7AkQ/wwAP3H6xhZBLA8Ye/80jzJ4Xqmo7tb/1b B4BxXhRHAoEgglIQfsEPv+iHXxDAm6+Ht3MGHD6zF154YVEJwAsCQqEQpmdmEJQko7KGGfKVQiHk cjlwHAdBEJBJp906zIpwSoA/+uQnRwsd1sL4QAiBoihGdRBZQTqdRjabwejBB48D2Df67YeaNynU VAHjDMuOSOEIwuEwOiLGMhKJIBwO2YUinWFgC0sJgE0bNyGdSaN3fS9yuRzm5q7YiR+SJAG6Dsms RJpONZoBCsvt23c4fi82DmXzoi8sLCCVSkHw+8ELAhLx2AhtQFKoqwzw8Tee2MXy/AjnE4y7XhAQ CAQRCAQRCoUQDkcgSQYDOGsDWVjKBiCEIB6LIR6LYWjomqL1WYbF+vUFH/paTgM7Ojoq/G6VijVU l2rmAeTzeSiKAt4vArI88q0H7t/19NNPu6YO3M4H2KfVUSl0qWngzMwMZFlBX18v4vEYUo67fGZm Br29veA4zp4KNhKrMQ001cQ+AE3LAGAYxqgQyrFgOc6sGeyzs4IFwawWbkf9LAnAmGK0+gkcGOjH zMwMWJa1nw7O5Yxg0KZNGxGPx0GpERRq9JPDzoQQURSrhX2h6zpkWQDP82bVNB84nw8+zgfCcq6X xnH1uYB6oS/xl81m0dvbi1gsBmKmhFt/VmqVcYLlJbfl5p+X4bmkUEtyMszSeptQio8uXDCsaLNZ Y1LptG0A6sCaeQKXrXqquIzdrhXstgSoq1KoZdRVa5Ik2Xe5pQasvmQyCUIIIpGIUUiCZZfcnltt pbgaKoWOMwwe13XNrolLiArVzAiWZRnldQOtocySTqB4LIbrrr3WvrMnL12y+64ZGgLHcVBkGVeu XGn408HOeaCRiaSX/Gy+S1g2JJdKCIiqghACSggoJdA1DYSQ5q0U+sbOv/4IwKhGSdEjXMvFUndW NptFMplEKpXC7OxsUd+ZM2fs3wYGBsDzfNNIAEKIVSl09NsPPdy8SaGaruP4tr/69qd+91/nk4n4 aDIRH7pQY32AxTAwMICZmRls2LABoigim83aL2Lu6uoCLwjI5nK2bdDIqaDTBviPw/9etb/0uzk7 OE8pHf3Wgwd/7DadriaFbvuXX9Y1/uIDd+iLMcjGjRsRCASMjGBKkUwm7XBwf38/enp6MD8/j1gs jnR6raaBOt7/4MO6LLnNm9x7+bTHU8IW719YSIEQAo7jTAlQSAiZmprG5cuzGBzcjGuuGcJ77/2u wbOA4qVX4elwsOEJrN7b1dUJRVEgiiJyuRxCIcn2BnZ3d6Gnpwe5XA4ffnjGfgClUWiWhBBXjcDV MZ70qi2bzUIUA9B1IBLpQDabtftEUQQhFDwvYMOGDRAEftFtude8Dc8/F7AYpqenAUxX7LvkmBKu BZy0NzoOUQu8nRKm67bx5+WTuBi8TrenbQCvn7zloNHTz1rh8ixAc7r2aoaXT9xyoet6ww3QWuD+ NLCOGXCbAdyHuwxAVDCsFc8uz/mr8kMRrgYmaF0G0AGmjguo6/o4jIyYZsa42qoMoCtZ6FaRyDJb YFm6YVTX9aZmAIZhRonqXQZw1RGkaXpd7a233joN4+3Zp9yk0yWcAjDy+i9+ddou/LTC5iZcDQa1 4X14OiewDffRZoAWR5sBWhxtBmhxtBmgxdFmgBZHmwFaHG0GaHG0GaDF0WaAFkebAVocbQZocbQZ oMXx/5Om/1dUVK3NAAAAAElFTkSuQmCC " transform="matrix(1.851e-4,1,-1,9.0254e-5,0,0)" height="43.358" width="30.618" y="-49.799" x="20.211"/>
+ <g transform="matrix(.35542 0 0 .3397 -57.093 12.065)">
+ <path d="m268.36 5.3407a30.675 31.406 0 0 1 -61.349 0 30.675 31.406 0 1 1 61.349 0z" stroke="#000" stroke-linecap="square" stroke-width="4" fill="none"/>
+ <path d="m241.62-16.098v42.878" stroke="#000" stroke-width="8" fill="none"/>
+ <path fill-rule="evenodd" d="m220.12-8.3133v26.29l21.37-13.145-21.37-13.145z"/>
+ <path d="m234.77 5.2219h-35.043l-6.4932 0.031719" stroke="#000" stroke-width="4" fill="none"/>
+ <path d="m287.15 19.957h-45.27" stroke="#000" stroke-width="4" fill="none"/>
+ <path d="m287.14-9.1501h-45.45" stroke="#000" stroke-width="4" fill="none"/>
+ </g>
+ <g transform="matrix(.43344 .11715 -.11752 .40543 8.0669 -13.586)">
+ <g transform="matrix(-.84925 -.56427 .49404 -.84925 117.62 338.15)">
+ <path fill="none" d="m168.24 132.25h63.709v63.708h-63.71v-63.71z"/>
+ <path opacity=".2" d="m199.09 132.53c0.77 0.38 1.247 1.148 1.247 2.008v7.581c3.479 0.669 9.808 2.597 15.847 8.141 9.58 8.798 14.438 22.806 14.438 41.633 0 1.148-0.861 2.104-2.001 2.228-1.141 0.122-2.184-0.633-2.426-1.754-2.696-12.449-7.87-21.021-15.379-25.474-4.047-2.399-7.848-3.052-10.478-3.154v7.201c0 0.857-0.478 1.627-1.247 2.008s-1.67 0.293-2.353-0.229l-23.823-18.201c-0.552-0.421-0.88-1.084-0.88-1.779 0-0.692 0.328-1.358 0.88-1.779l23.824-18.2c0.681-0.523 1.582-0.611 2.351-0.23z"/>
+ <path fill="url(#d)" d="m196.94 134.29c0.769 0.38 1.247 1.147 1.247 2.008v7.58c3.479 0.669 9.808 2.597 15.846 8.142 9.581 8.795 14.438 22.804 14.438 41.633 0 1.148-0.86 2.104-2.001 2.228-1.141 0.121-2.184-0.633-2.427-1.755-2.695-12.449-7.869-21.02-15.379-25.472-4.047-2.4-7.847-3.052-10.477-3.154v7.201c0 0.856-0.479 1.626-1.247 2.008-0.77 0.38-1.67 0.292-2.354-0.229l-23.824-18.199c-0.551-0.424-0.88-1.088-0.88-1.781 0-0.691 0.329-1.357 0.88-1.779l23.824-18.201c0.684-0.524 1.585-0.611 2.354-0.23z"/>
+ <path d="m196.49 135.01c0.424 0.211 0.693 0.643 0.693 1.116v8.412c3.083 0.491 9.801 2.196 16.167 8.043 9.368 8.601 14.117 22.36 14.117 40.9 0 0.635-0.479 1.169-1.112 1.235-0.632 0.069-1.214-0.352-1.35-0.974-2.756-12.73-8.087-21.52-15.843-26.118-4.977-2.951-9.641-3.347-11.979-3.32v8.224c0 0.474-0.27 0.906-0.693 1.115-0.424 0.211-0.931 0.16-1.307-0.127l-23.82-18.2c-0.309-0.234-0.489-0.601-0.489-0.987 0-0.388 0.182-0.755 0.489-0.989l23.823-18.201c0.376-0.288 0.883-0.337 1.307-0.128z" stroke="#a66e4b" stroke-miterlimit="10" fill="none"/>
+ <path fill="url(#e)" d="m226.23 193.48c-6.72-31.037-26.985-30.646-30.285-30.369v9.414l-23.824-18.199 23.824-18.201v9.501c3.809 0.39 30.285 4.647 30.285 47.854z"/>
+ <path fill="#fef39e" d="m172.3 154.35 23.823-18.199v2.133l-23.1 16.62-0.72-0.55z"/>
+ <g transform="matrix(.99571 0 0 -.9918 8.8182 167.26)">
+ <path fill="url(#f)" d="m186.18 15.654c-10.237 1.25-14.215-0.656-19.607-4.083-0.132-0.1-0.245-0.187-0.333-0.256 2.111-1.611 17.373-13.271 20.53-15.683v8.109l-0.127-0.063c5.505 0.981 8.872-0.175 15.625-2.917-3.299 7.286-10.145 14.168-16.088 14.893z"/>
+ </g>
+ <path fill="#fef39e" d="m225.58 180.92c0.205 1.348-6.825-31.207-29.488-33.5-0.058-0.005-0.065-0.021-0.065 0.054v-1.77c-0.002-0.001 24.488 1.95 29.553 35.217z"/>
+ </g>
+ </g>
+</svg>
diff --git a/resources/linux/mime/icons/hicolor/scalable/apps/eeschema.svg b/resources/linux/mime/icons/hicolor/scalable/apps/eeschema.svg
new file mode 100644
index 0000000..b6599cb
--- /dev/null
+++ b/resources/linux/mime/icons/hicolor/scalable/apps/eeschema.svg
@@ -0,0 +1,394 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48" version="1.0" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs>
+ <filter id="bb" color-interpolation-filters="sRGB">
+ <feGaussianBlur stdDeviation="2.0786429"/>
+ </filter>
+ <clipPath id="ba">
+ <path fill-rule="evenodd" d="m72 88-32 32h-8v-40h40v8z"/>
+ </clipPath>
+ <filter id="bc" height="1.384" width="1.384" color-interpolation-filters="sRGB" y="-.192" x="-.192">
+ <feGaussianBlur stdDeviation="1.9447689"/>
+ </filter>
+ <linearGradient id="bn" y2="172.44" gradientUnits="userSpaceOnUse" x2="153" gradientTransform="translate(-143,-64)" y1="175.56" x1="153">
+ <stop stop-color="#fff" offset="0"/>
+ <stop stop-color="#ddd" offset="1"/>
+ </linearGradient>
+ <linearGradient id="bo" y2="172.9" gradientUnits="userSpaceOnUse" x2="151.9" gradientTransform="translate(-143,-64)" y1="175.1" x1="154.1">
+ <stop stop-color="#bbb" offset="0"/>
+ <stop stop-color="#616161" offset="1"/>
+ </linearGradient>
+ <linearGradient id="bp" y2="164.44" gradientUnits="userSpaceOnUse" x2="153" gradientTransform="translate(-143,-64)" y1="167.56" x1="153">
+ <stop stop-color="#fff" offset="0"/>
+ <stop stop-color="#ddd" offset="1"/>
+ </linearGradient>
+ <linearGradient id="bq" y2="164.9" gradientUnits="userSpaceOnUse" x2="151.9" gradientTransform="translate(-143,-64)" y1="167.1" x1="154.1">
+ <stop stop-color="#bbb" offset="0"/>
+ <stop stop-color="#616161" offset="1"/>
+ </linearGradient>
+ <linearGradient id="br" y2="156.44" gradientUnits="userSpaceOnUse" x2="153" gradientTransform="translate(-143,-64)" y1="159.56" x1="153">
+ <stop stop-color="#fff" offset="0"/>
+ <stop stop-color="#ddd" offset="1"/>
+ </linearGradient>
+ <linearGradient id="bs" y2="156.9" gradientUnits="userSpaceOnUse" x2="151.9" gradientTransform="translate(-143,-64)" y1="159.1" x1="154.1">
+ <stop stop-color="#bbb" offset="0"/>
+ <stop stop-color="#616161" offset="1"/>
+ </linearGradient>
+ <linearGradient id="bt" y2="148.44" gradientUnits="userSpaceOnUse" x2="153" gradientTransform="translate(-143,-64)" y1="151.56" x1="153">
+ <stop stop-color="#fff" offset="0"/>
+ <stop stop-color="#ddd" offset="1"/>
+ </linearGradient>
+ <linearGradient id="bu" y2="148.9" gradientUnits="userSpaceOnUse" x2="151.9" gradientTransform="translate(-143,-64)" y1="151.1" x1="154.1">
+ <stop stop-color="#bbb" offset="0"/>
+ <stop stop-color="#616161" offset="1"/>
+ </linearGradient>
+ <linearGradient id="bv" y2="140.44" gradientUnits="userSpaceOnUse" x2="153" gradientTransform="translate(-143,-64)" y1="143.56" x1="153">
+ <stop stop-color="#fff" offset="0"/>
+ <stop stop-color="#ddd" offset="1"/>
+ </linearGradient>
+ <linearGradient id="bw" y2="140.9" gradientUnits="userSpaceOnUse" x2="151.9" gradientTransform="translate(-143,-64)" y1="143.1" x1="154.1">
+ <stop stop-color="#bbb" offset="0"/>
+ <stop stop-color="#616161" offset="1"/>
+ </linearGradient>
+ <linearGradient id="bx" y2="132.44" gradientUnits="userSpaceOnUse" x2="153" gradientTransform="translate(-143,-64)" y1="135.56" x1="153">
+ <stop stop-color="#fff" offset="0"/>
+ <stop stop-color="#ddd" offset="1"/>
+ </linearGradient>
+ <linearGradient id="by" y2="132.9" gradientUnits="userSpaceOnUse" x2="151.9" gradientTransform="translate(-143,-64)" y1="135.1" x1="154.1">
+ <stop stop-color="#bbb" offset="0"/>
+ <stop stop-color="#616161" offset="1"/>
+ </linearGradient>
+ <linearGradient id="bz" y2="124.44" gradientUnits="userSpaceOnUse" x2="153" gradientTransform="translate(-143,-64)" y1="127.56" x1="153">
+ <stop stop-color="#fff" offset="0"/>
+ <stop stop-color="#ddd" offset="1"/>
+ </linearGradient>
+ <linearGradient id="ca" y2="124.9" gradientUnits="userSpaceOnUse" x2="151.9" gradientTransform="translate(-143,-64)" y1="127.1" x1="154.1">
+ <stop stop-color="#bbb" offset="0"/>
+ <stop stop-color="#616161" offset="1"/>
+ </linearGradient>
+ <linearGradient id="cb" y2="116.44" gradientUnits="userSpaceOnUse" x2="153" gradientTransform="translate(-143,-64)" y1="119.56" x1="153">
+ <stop stop-color="#fff" offset="0"/>
+ <stop stop-color="#ddd" offset="1"/>
+ </linearGradient>
+ <linearGradient id="cc" y2="116.9" gradientUnits="userSpaceOnUse" x2="151.9" gradientTransform="translate(-143,-64)" y1="119.1" x1="154.1">
+ <stop stop-color="#bbb" offset="0"/>
+ <stop stop-color="#616161" offset="1"/>
+ </linearGradient>
+ <linearGradient id="cd" y2="108.44" gradientUnits="userSpaceOnUse" x2="153" gradientTransform="translate(-143,-64)" y1="111.56" x1="153">
+ <stop stop-color="#fff" offset="0"/>
+ <stop stop-color="#ddd" offset="1"/>
+ </linearGradient>
+ <linearGradient id="ce" y2="108.9" gradientUnits="userSpaceOnUse" x2="151.9" gradientTransform="translate(-143,-64)" y1="111.1" x1="154.1">
+ <stop stop-color="#bbb" offset="0"/>
+ <stop stop-color="#616161" offset="1"/>
+ </linearGradient>
+ <linearGradient id="cf" y2="100.44" gradientUnits="userSpaceOnUse" x2="153" gradientTransform="translate(-143,-64)" y1="103.56" x1="153">
+ <stop stop-color="#fff" offset="0"/>
+ <stop stop-color="#ddd" offset="1"/>
+ </linearGradient>
+ <linearGradient id="as" y2="100.9" gradientUnits="userSpaceOnUse" x2="151.9" gradientTransform="translate(-143,-64)" y1="103.1" x1="154.1">
+ <stop stop-color="#bbb" offset="0"/>
+ <stop stop-color="#616161" offset="1"/>
+ </linearGradient>
+ <linearGradient id="at" y2="92.444" gradientUnits="userSpaceOnUse" x2="153" gradientTransform="translate(-143,-64)" y1="95.557" x1="153">
+ <stop stop-color="#fff" offset="0"/>
+ <stop stop-color="#ddd" offset="1"/>
+ </linearGradient>
+ <linearGradient id="au" y2="92.9" gradientUnits="userSpaceOnUse" x2="151.9" gradientTransform="translate(-143,-64)" y1="95.101" x1="154.1">
+ <stop stop-color="#bbb" offset="0"/>
+ <stop stop-color="#616161" offset="1"/>
+ </linearGradient>
+ <linearGradient id="av" y2="84.444" gradientUnits="userSpaceOnUse" x2="153" gradientTransform="translate(-143,-64)" y1="87.557" x1="153">
+ <stop stop-color="#fff" offset="0"/>
+ <stop stop-color="#ddd" offset="1"/>
+ </linearGradient>
+ <linearGradient id="aw" y2="84.9" gradientUnits="userSpaceOnUse" x2="151.9" gradientTransform="translate(-143,-64)" y1="87.101" x1="154.1">
+ <stop stop-color="#bbb" offset="0"/>
+ <stop stop-color="#616161" offset="1"/>
+ </linearGradient>
+ <linearGradient id="ax" y2="76.444" gradientUnits="userSpaceOnUse" x2="153" gradientTransform="translate(-143,-64)" y1="79.557" x1="153">
+ <stop stop-color="#fff" offset="0"/>
+ <stop stop-color="#ddd" offset="1"/>
+ </linearGradient>
+ <linearGradient id="ay" y2="76.9" gradientUnits="userSpaceOnUse" x2="151.9" gradientTransform="translate(-143,-64)" y1="79.101" x1="154.1">
+ <stop stop-color="#bbb" offset="0"/>
+ <stop stop-color="#616161" offset="1"/>
+ </linearGradient>
+ <linearGradient id="bd" y2="99.255" gradientUnits="userSpaceOnUse" x2="91.229" gradientTransform="matrix(.42792 0 0 .39077 -4.2833 -.67735)" y1="106.41" x1="98.617">
+ <stop stop-color="#a2a2a2" offset="0"/>
+ <stop stop-color="#fff" offset="1"/>
+ </linearGradient>
+ <radialGradient id="az" gradientUnits="userSpaceOnUse" cy="112.3" cx="102" gradientTransform="matrix(.42792 0 0 .39064 -4.2833 -.67617)" r="139.56">
+ <stop stop-color="#b7b8b9" offset="0"/>
+ <stop stop-color="#ececec" offset=".18851"/>
+ <stop stop-color="#fafafa" offset=".25718"/>
+ <stop stop-color="#fff" offset=".30111"/>
+ <stop stop-color="#fafafa" offset=".53130"/>
+ <stop stop-color="#ebecec" offset=".84490"/>
+ <stop stop-color="#e1e2e3" offset="1"/>
+ </radialGradient>
+ <linearGradient id="ar">
+ <stop stop-color="#3e3e3e" offset="0"/>
+ <stop stop-color="#828282" offset=".5"/>
+ <stop stop-color="#3c3c3c" offset="1"/>
+ </linearGradient>
+ <linearGradient id="aq">
+ <stop stop-color="#999" offset="0"/>
+ <stop stop-color="#fff" offset=".5"/>
+ <stop stop-color="#777" offset="1"/>
+ </linearGradient>
+ <linearGradient id="be" y2="31.211" gradientUnits="userSpaceOnUse" x2="23.576" gradientTransform="matrix(.64407 -.64045 .66092 .65072 61.423 86.661)" y1="25.357" x1="23.576">
+ <stop offset="0"/>
+ <stop stop-color="#c3c3c3" offset=".13483"/>
+ <stop stop-color="#8c8c8c" offset=".20224"/>
+ <stop stop-color="#fff" offset=".26966"/>
+ <stop stop-color="#757575" offset=".4465"/>
+ <stop stop-color="#7d7d7d" offset=".57114"/>
+ <stop stop-color="#b6b6b6" offset=".72038"/>
+ <stop offset="1"/>
+ </linearGradient>
+ <linearGradient id="bf" y2="30" xlink:href="#ar" gradientUnits="userSpaceOnUse" x2="30.038" gradientTransform="matrix(.62586 -.62234 .77349 .76155 59.298 83.616)" y1="24.99" x1="30.038"/>
+ <linearGradient id="bg" y2="30" xlink:href="#aq" gradientUnits="userSpaceOnUse" x2="30.038" gradientTransform="matrix(.62586 -.62234 .77349 .76155 59.119 83.794)" y1="24.99" x1="30.038"/>
+ <linearGradient id="bh" y2="30" xlink:href="#ar" gradientUnits="userSpaceOnUse" x2="30.038" gradientTransform="matrix(.62586 -.62234 .77349 .76155 59.921 82.996)" y1="24.99" x1="30.038"/>
+ <linearGradient id="bi" y2="30" xlink:href="#aq" gradientUnits="userSpaceOnUse" x2="30.038" gradientTransform="matrix(.62586 -.62234 .77349 .76155 59.742 83.175)" y1="24.99" x1="30.038"/>
+ <linearGradient id="bj" y2="30" xlink:href="#ar" gradientUnits="userSpaceOnUse" x2="30.038" gradientTransform="matrix(.62586 -.62234 .77349 .76155 60.547 82.374)" y1="24.99" x1="30.038"/>
+ <linearGradient id="bk" y2="30" xlink:href="#aq" gradientUnits="userSpaceOnUse" x2="30.038" gradientTransform="matrix(.62586 -.62234 .77349 .76155 60.367 82.552)" y1="24.99" x1="30.038"/>
+ <linearGradient id="bl" y2="26.03" gradientUnits="userSpaceOnUse" x2="9" gradientTransform="matrix(.62586 -.62234 .77349 .76155 59.06 83.852)" y1="29.057" x1="9">
+ <stop stop-color="#e4db7b" offset="0"/>
+ <stop stop-color="#f4f0c8" offset="1"/>
+ </linearGradient>
+ <linearGradient id="bm" y2="41.392" gradientUnits="userSpaceOnUse" x2="9.5221" gradientTransform="matrix(.52586 0 0 .51993 81.027 79.545)" y1="37.372" x1="5.5179">
+ <stop stop-color="#cbbd27" offset="0"/>
+ <stop stop-color="#9b901d" offset="1"/>
+ </linearGradient>
+ </defs>
+ <path opacity=".5" d="m16 8v112h63.187c0.000003 0 11.906-9.9062 17.406-15.406 5.497-5.488 15.407-17.394 15.407-17.394v-79.188h-96z" transform="matrix(.44575 0 0 .40124 -5.4244 -1.1519)" filter="url(#bb)"/>
+ <path fill="#fff" d="m2.5634 2.4488v43.767h27.022l14.059-12.839v-30.928h-41.08z"/>
+ <path fill="url(#az)" d="m3.4193 2.8396c-0.23578 0-0.42792 0.1754-0.42792 0.39064v42.189c0 0.21563 0.19214 0.39064 0.42792 0.39064h25.321c0.11254 0 1.1822 0.04616 1.2618-0.02689l13.089-12.036c0.08002-0.07305 0.12538-1.0491 0.12538-1.1519v-29.365c0-0.21524-0.19171-0.39064-0.42792-0.39064h-39.369z"/>
+ <g stroke-linejoin="round" fill-rule="evenodd" transform="matrix(.82576 0 0 .64612 -28.052 -37.166)" stroke="#d3d7cf" stroke-linecap="round" stroke-width="1.3337px" fill="#d3d7cf">
+ <path d="m43.101 70.118"/>
+ <path d="m47.211 70.118"/>
+ <path d="m51.321 70.118"/>
+ <path d="m55.431 70.118"/>
+ <path d="m59.541 70.118"/>
+ <path d="m63.651 70.118"/>
+ <path d="m67.761 70.118"/>
+ <path d="m71.871 70.118"/>
+ <path d="m75.981 70.118"/>
+ <path d="m80.091 70.118"/>
+ <path d="m84.201 70.118"/>
+ <path d="m43.101 65.009"/>
+ <path d="m47.211 65.009"/>
+ <path d="m51.321 65.009"/>
+ <path d="m55.431 65.009"/>
+ <path d="m59.541 65.009"/>
+ <path d="m63.651 65.009"/>
+ <path d="m67.761 65.009"/>
+ <path d="m71.871 65.009"/>
+ <path d="m75.981 65.009"/>
+ <path d="m80.091 65.009"/>
+ <path d="m84.201 65.009"/>
+ <path d="m43.101 85.443"/>
+ <path d="m47.211 85.443"/>
+ <path d="m51.321 85.443"/>
+ <path d="m55.431 85.443"/>
+ <path d="m59.541 85.443"/>
+ <path d="m63.651 85.443"/>
+ <path d="m67.761 85.443"/>
+ <path d="m71.871 85.443"/>
+ <path d="m75.981 85.443"/>
+ <path d="m80.091 85.443"/>
+ <path d="m84.201 85.443"/>
+ <path d="m43.101 75.226"/>
+ <path d="m47.211 75.226"/>
+ <path d="m51.321 75.226"/>
+ <path d="m55.431 75.226"/>
+ <path d="m59.541 75.226"/>
+ <path d="m63.651 75.226"/>
+ <path d="m67.761 75.226"/>
+ <path d="m71.871 75.226"/>
+ <path d="m75.981 75.226"/>
+ <path d="m80.091 75.226"/>
+ <path d="m84.201 75.226"/>
+ <path d="m43.101 90.551"/>
+ <path d="m47.211 90.551"/>
+ <path d="m51.321 90.551"/>
+ <path d="m55.431 90.551"/>
+ <path d="m59.541 90.551"/>
+ <path d="m63.651 90.551"/>
+ <path d="m67.761 90.551"/>
+ <path d="m71.871 90.551"/>
+ <path d="m75.981 90.551"/>
+ <path d="m80.091 90.551"/>
+ <path d="m84.201 90.551"/>
+ <path d="m43.101 80.335"/>
+ <path d="m47.211 80.335"/>
+ <path d="m51.321 80.335"/>
+ <path d="m55.431 80.335"/>
+ <path d="m59.541 80.335"/>
+ <path d="m63.651 80.335"/>
+ <path d="m67.761 80.335"/>
+ <path d="m71.871 80.335"/>
+ <path d="m75.981 80.335"/>
+ <path d="m80.091 80.335"/>
+ <path d="m84.201 80.335"/>
+ <path d="m43.101 95.66"/>
+ <path d="m47.211 95.66"/>
+ <path d="m51.321 95.66"/>
+ <path d="m55.431 95.66"/>
+ <path d="m59.541 95.66"/>
+ <path d="m63.651 95.66"/>
+ <path d="m67.761 95.66"/>
+ <path d="m71.871 95.66"/>
+ <path d="m75.981 95.66"/>
+ <path d="m80.091 95.66"/>
+ <path d="m84.201 95.66"/>
+ <path d="m43.101 100.77"/>
+ <path d="m47.211 100.77"/>
+ <path d="m51.321 100.77"/>
+ <path d="m55.431 100.77"/>
+ <path d="m59.541 100.77"/>
+ <path d="m63.651 100.77"/>
+ <path d="m67.761 100.77"/>
+ <path d="m71.871 100.77"/>
+ <path d="m75.981 100.77"/>
+ <path d="m80.091 100.77"/>
+ <path d="m84.201 100.77"/>
+ <path d="m43.101 105.88"/>
+ <path d="m47.211 105.88"/>
+ <path d="m51.321 105.88"/>
+ <path d="m55.431 105.88"/>
+ <path d="m59.541 105.88"/>
+ <path d="m63.651 105.88"/>
+ <path d="m67.761 105.88"/>
+ <path d="m71.871 105.88"/>
+ <path d="m75.981 105.88"/>
+ <path d="m80.091 105.88"/>
+ <path d="m84.201 105.88"/>
+ <path d="m43.101 116.1"/>
+ <path d="m47.211 116.1"/>
+ <path d="m51.321 116.1"/>
+ <path d="m55.431 116.1"/>
+ <path d="m59.541 116.1"/>
+ <path d="m63.651 116.1"/>
+ <path d="m67.761 116.1"/>
+ <path d="m71.871 116.1"/>
+ <path d="m75.981 116.1"/>
+ <path d="m80.091 116.1"/>
+ <path d="m43.101 110.99"/>
+ <path d="m47.211 110.99"/>
+ <path d="m51.321 110.99"/>
+ <path d="m55.431 110.99"/>
+ <path d="m59.541 110.99"/>
+ <path d="m63.651 110.99"/>
+ <path d="m67.761 110.99"/>
+ <path d="m71.871 110.99"/>
+ <path d="m75.981 110.99"/>
+ <path d="m80.091 110.99"/>
+ <path d="m84.201 110.99"/>
+ <path d="m43.101 121.21"/>
+ <path d="m47.211 121.21"/>
+ <path d="m51.321 121.21"/>
+ <path d="m55.431 121.21"/>
+ <path d="m59.541 121.21"/>
+ <path d="m63.651 121.21"/>
+ <path d="m67.761 121.21"/>
+ <path d="m71.871 121.21"/>
+ <path d="m75.981 121.21"/>
+ <path d="m43.101 126.31"/>
+ <path d="m47.211 126.31"/>
+ <path d="m51.321 126.31"/>
+ <path d="m55.431 126.31"/>
+ <path d="m59.541 126.31"/>
+ <path d="m63.651 126.31"/>
+ <path d="m67.761 126.31"/>
+ <path d="m71.871 126.31"/>
+ </g>
+ <path opacity=".4" d="m41.88 115.98 24.31-24.31s-9.3531 2.9131-19.603 2.9131c0 10.25-4.7065 21.396-4.7065 21.396z" clip-path="url(#ba)" transform="matrix(.42792 0 0 .39077 12.834 -.67735)" filter="url(#bc)"/>
+ <g transform="matrix(.42793 0 0 .39077 -48.788 13.39)">
+ <g transform="translate(116,-32)">
+ <circle cy="110" cx="10" r="2" fill="url(#bn)"/>
+ <circle cy="110" cx="10" r="1.556" fill="url(#bo)"/>
+ </g>
+ <g transform="translate(116,-32)">
+ <circle cy="102" cx="10" r="2" fill="url(#bp)"/>
+ <circle cy="102" cx="10" r="1.556" fill="url(#bq)"/>
+ </g>
+ <g transform="translate(116,-32)">
+ <circle cy="94" cx="10" r="2" fill="url(#br)"/>
+ <circle cy="94" cx="10" r="1.556" fill="url(#bs)"/>
+ </g>
+ <g transform="translate(116,-32)">
+ <circle cy="86" cx="10" r="2" fill="url(#bt)"/>
+ <circle cy="86" cx="10" r="1.556" fill="url(#bu)"/>
+ </g>
+ <g transform="translate(116,-32)">
+ <circle cy="78" cx="10" r="2" fill="url(#bv)"/>
+ <circle cy="78" cx="10" r="1.556" fill="url(#bw)"/>
+ </g>
+ <g transform="translate(116,-32)">
+ <circle cy="70" cx="10" r="2" fill="url(#bx)"/>
+ <circle cy="70" cx="10" r="1.556" fill="url(#by)"/>
+ </g>
+ <g transform="translate(116,-32)">
+ <circle cy="62" cx="10" r="2" fill="url(#bz)"/>
+ <circle cy="62" cx="10" r="1.556" fill="url(#ca)"/>
+ </g>
+ <g transform="translate(116,-32)">
+ <circle cy="54" cx="10" r="2" fill="url(#cb)"/>
+ <circle cy="54" cx="10" r="1.556" fill="url(#cc)"/>
+ </g>
+ <g transform="translate(116,-32)">
+ <circle cy="46" cx="10" r="2" fill="url(#cd)"/>
+ <circle cy="46" cx="10" r="1.556" fill="url(#ce)"/>
+ </g>
+ <g transform="translate(116,-32)">
+ <circle cy="38" cx="10" r="2" fill="url(#cf)"/>
+ <circle cy="38" cx="10" r="1.556" fill="url(#as)"/>
+ </g>
+ <g transform="translate(116,-32)">
+ <circle cy="30" cx="10" r="2" fill="url(#at)"/>
+ <circle cy="30" cx="10" r="1.556" fill="url(#au)"/>
+ </g>
+ <g transform="translate(116,-32)">
+ <circle cy="22" cx="10" r="2" fill="url(#av)"/>
+ <circle cy="22" cx="10" r="1.556" fill="url(#aw)"/>
+ </g>
+ <g transform="translate(116,-32)">
+ <circle cy="14" cx="10" r="2" fill="url(#ax)"/>
+ <circle cy="14" cx="10" r="1.556" fill="url(#ay)"/>
+ </g>
+ </g>
+ <path stroke-linejoin="round" style="color:#000000" d="m15.884 33.281h-6.1275" fill-rule="evenodd" fill-opacity=".75" stroke="#204a87" stroke-linecap="round" stroke-width="1.399" fill="#4e9a06"/>
+ <path stroke-linejoin="round" style="color:#000000" d="m15.884 27.147v12.267" fill-rule="evenodd" fill-opacity=".75" stroke="#4e9a06" stroke-linecap="round" stroke-width="1.399" fill="#4e9a06"/>
+ <path stroke-linejoin="round" style="color:#000000" d="m20.99 39.414v3.6801" fill-rule="evenodd" fill-opacity=".75" stroke="#204a87" stroke-linecap="round" stroke-width="1.399" fill="#4e9a06"/>
+ <path stroke-linejoin="round" style="color:#000000" d="m20.99 39.414-5.106-3.68" fill-rule="evenodd" fill-opacity=".75" stroke="#4e9a06" stroke-linecap="round" stroke-width="1.399" fill="#4e9a06"/>
+ <path stroke-linejoin="round" style="color:#000000" d="m23.033 43.094h-4.085" fill-rule="evenodd" fill-opacity=".75" stroke="#4e9a06" stroke-linecap="round" stroke-width="1.399" fill="#4e9a06"/>
+ <path stroke-linejoin="round" style="color:#000000" d="m20.99 27.147v-7.36" fill-rule="evenodd" fill-opacity=".75" stroke="#204a87" stroke-linecap="round" stroke-width="1.399" fill="#4e9a06"/>
+ <path stroke-linejoin="round" style="color:#000000" d="m20.99 27.147-5.1063 3.6801" fill-rule="evenodd" fill-opacity=".75" stroke="#4e9a06" stroke-linecap="round" stroke-width="1.399" fill="#4e9a06"/>
+ <path stroke-linejoin="round" style="color:#000000" d="m20.99 7.5199v3.6801" fill-rule="evenodd" fill-opacity=".75" stroke="#204a87" stroke-linecap="round" stroke-width="1.399" fill="#4e9a06"/>
+ <path stroke-linejoin="round" style="color:#000000" d="m22.011 19.787h-2.043v-8.587h2.0425v8.587" stroke="#4e9a06" stroke-linecap="round" stroke-width="1.399" fill="none"/>
+ <path stroke-linejoin="round" style="color:#000000" d="m20.99 23.557h8.17" fill-rule="evenodd" fill-opacity=".75" stroke="#204a87" stroke-linecap="round" stroke-width="1.399" fill="#4e9a06"/>
+ <path stroke-linejoin="round" style="color:#000000" d="m9.7564 12.427h5.1063" stroke="#888a85" stroke-linecap="round" stroke-width="1.399" fill="none"/>
+ <path stroke-linejoin="round" style="color:#000000" d="m9.7564 14.88h5.1063" stroke="#888a85" stroke-linecap="round" stroke-width="1.399" fill="none"/>
+ <path stroke-linejoin="round" style="color:#000000" d="m24.054 32.054h5.1063" stroke="#888a85" stroke-linecap="round" stroke-width="1.399" fill="none"/>
+ <path stroke-linejoin="round" style="color:#000000" d="m24.054 34.507h5.1063" stroke="#888a85" stroke-linecap="round" stroke-width="1.399" fill="none"/>
+ <path style="color:#000000" fill-rule="evenodd" fill="#4e9a06" d="m20.99 39.414-2.234-3.45-1.149 2.99 3.383 0.46z"/>
+ <path fill="#a40000" d="m9.9459 33.281a1.2631 1.2453 0 0 1 -2.5262 0 1.2631 1.2453 0 1 1 2.5262 0z"/>
+ <path stroke-linejoin="round" style="color:#000000" d="m21.001 7.1011h8.17" fill-rule="evenodd" fill-opacity=".75" stroke="#204a87" stroke-linecap="round" stroke-width="1.399" fill="#4e9a06"/>
+ <path fill="#a40000" d="m31.492 23.557a1.2631 1.2453 0 0 1 -2.5262 0 1.2631 1.2453 0 1 1 2.5262 0z"/>
+ <path fill="#a40000" d="m31.43 7.1011a1.2631 1.2453 0 0 1 -2.5262 0 1.2631 1.2453 0 1 1 2.5262 0z"/>
+ <path fill="url(#bd)" d="m29.596 46.215s5.0982-3.8741 7.4518-6.0234c2.3536-2.1492 6.596-6.8049 6.596-6.8049s-5.8839 3.4497-10.27 3.4497c0 4.0054-3.7777 9.3785-3.7777 9.3785z"/>
+ <g transform="matrix(1.1923 -.15242 .16247 1.1185 -91.73 -61.864)">
+ <path stroke-linejoin="round" d="m85.365 96.011c0.43282-0.32935 1.5837 0.20459 2.6183 1.2232 1.0321 1.0162 1.5508 2.1286 1.225 2.5608-0.0012 0.0017 0.02544 0.02217 0.02417 0.0238l15.708-15.618c0.39874-0.3965-0.14267-1.5725-1.2101-2.6235-1.0674-1.0509-2.2586-1.5808-2.6574-1.1843l-15.705 15.618z" stroke="#000" stroke-width=".55533" fill="url(#be)"/>
+ <path opacity=".8" stroke-linejoin="round" d="m99.153 82.3c0.43282-0.32935 1.5837 0.20459 2.6183 1.2232 1.0321 1.0162 1.5508 2.1285 1.225 2.5608-0.001 0.0017 0.0254 0.02217 0.0242 0.0238l1.9167-1.9059 0.0391-0.0389c0.001-0.0017-0.0254-0.02215-0.0242-0.0238 0.32575-0.43223-0.19292-1.5446-1.225-2.5608-1.0346-1.0186-2.1854-1.5525-2.6183-1.2232l-0.04 0.039-1.917 1.906z" stroke="#dd78c5" stroke-width=".55533" fill="#ff9de8"/>
+ <path opacity=".6" d="m85.365 96.011c0.43282-0.32935 1.5837 0.20459 2.6183 1.2232 1.0321 1.0162 1.5508 2.1286 1.225 2.5608-0.0012 0.0017 0.02544 0.02217 0.02417 0.0238l10.816-10.755 0.0391-0.0389c0.001-0.0017-0.0254-0.02216-0.0242-0.0238 0.32575-0.43223-0.19293-1.5446-1.225-2.5608-1.0346-1.0186-2.1854-1.5525-2.6183-1.2232l-0.03911 0.0389-10.816 10.755z"/>
+ <path fill="url(#bf)" d="m97.005 84.436c0.43282-0.32935 1.5837 0.20459 2.6183 1.2232 1.0321 1.0162 1.5508 2.1286 1.225 2.5608-0.001 0.0017 0.0254 0.02217 0.0242 0.0238l0.19558-0.19448c0.001-0.0017-0.0254-0.02215-0.0242-0.0238 0.32575-0.43222-0.19292-1.5446-1.225-2.5608-1.0346-1.0186-2.1854-1.5525-2.6183-1.2232l-0.197 0.194z"/>
+ <path fill="url(#bg)" d="m96.826 84.614c0.43282-0.32935 1.5837 0.20459 2.6183 1.2232 1.0321 1.0162 1.5508 2.1285 1.225 2.5608-0.001 0.0017 0.0254 0.02217 0.0242 0.0238l0.19558-0.19448c0.001-0.0017-0.0254-0.02215-0.0242-0.0238 0.32575-0.43222-0.19292-1.5446-1.225-2.5608-1.0346-1.0186-2.1854-1.5525-2.6183-1.2232l-0.196 0.194z"/>
+ <path fill="url(#bh)" d="m97.628 83.817c0.43282-0.32935 1.5837 0.20459 2.6183 1.2232 1.0321 1.0162 1.5508 2.1286 1.225 2.5608-0.001 0.0017 0.0254 0.02217 0.0242 0.0238l0.19558-0.19448c0.001-0.0017-0.0254-0.02215-0.0242-0.0238 0.32575-0.43222-0.19292-1.5446-1.225-2.5608-1.0346-1.0186-2.1854-1.5525-2.6183-1.2232l-0.204 0.194z"/>
+ <path fill="url(#bi)" d="m97.448 83.995c0.43282-0.32935 1.5837 0.20459 2.6183 1.2232 1.0321 1.0162 1.5508 2.1286 1.225 2.5608-0.001 0.0017 0.0254 0.02217 0.0242 0.0238l0.19558-0.19448c0.001-0.0017-0.0254-0.02215-0.0242-0.0238 0.32575-0.43222-0.19292-1.5446-1.225-2.5608-1.0346-1.0186-2.1854-1.5525-2.6183-1.2232l-0.204 0.194z"/>
+ <path fill="url(#bj)" d="m98.254 83.194c0.43282-0.32935 1.5837 0.20459 2.6183 1.2232 1.0321 1.0162 1.5508 2.1286 1.225 2.5608-0.001 0.0017 0.0254 0.02217 0.0242 0.0238l0.19558-0.19448c0.001-0.0017-0.0254-0.02216-0.0242-0.0238 0.32575-0.43223-0.19292-1.5446-1.225-2.5608-1.0346-1.0186-2.1854-1.5525-2.6183-1.2232l-0.198 0.194z"/>
+ <path fill="url(#bk)" d="m98.074 83.372c0.43282-0.32935 1.5837 0.20459 2.6183 1.2232 1.0321 1.0162 1.5508 2.1286 1.225 2.5608-0.001 0.0017 0.0254 0.02217 0.0242 0.0238l0.19558-0.19448c0.001-0.0017-0.0254-0.02216-0.0242-0.0238 0.32575-0.43223-0.19292-1.5446-1.225-2.5608-1.0346-1.0186-2.1854-1.5525-2.6183-1.2232l-0.198 0.194z"/>
+ <path d="m82.185 102.91 7.0294-3.0074c0.32575-0.43222-0.20033-1.6548-1.2324-2.671-1.0346-1.0186-2.2398-1.5494-2.6726-1.22l-3.124 6.898z" fill-rule="evenodd" stroke="url(#bm)" stroke-width=".55533" fill="url(#bl)"/>
+ <path d="m83.046 101.04-0.86146 1.8612 1.9008-0.83782c-0.14196-0.17111-0.2723-0.3455-0.44432-0.51486-0.19804-0.19498-0.39436-0.35167-0.59506-0.50847z" fill-rule="evenodd" stroke="#000" stroke-width=".55533"/>
+ </g>
+</svg>
diff --git a/resources/linux/mime/icons/hicolor/scalable/apps/gerbview.svg b/resources/linux/mime/icons/hicolor/scalable/apps/gerbview.svg
new file mode 100644
index 0000000..a60a930
--- /dev/null
+++ b/resources/linux/mime/icons/hicolor/scalable/apps/gerbview.svg
@@ -0,0 +1,109 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs>
+ <linearGradient id="ab" y2="4.2193" gradientUnits="userSpaceOnUse" x2="7.5955" gradientTransform="matrix(.88924 0 0 .78227 .62292 7.6472)" y1="43.994" x1="40.752">
+ <stop stop-color="#333" offset="0"/>
+ <stop stop-color="#474747" offset="1"/>
+ </linearGradient>
+ <linearGradient id="a" y2="19500" gradientUnits="userSpaceOnUse" x2="15000" y1="14100" x1="10100">
+ <stop stop-color="#f5fffa" offset="0"/>
+ <stop stop-color="#6f6d7f" offset="1.2"/>
+ </linearGradient>
+ <linearGradient id="b" y2="19500" gradientUnits="userSpaceOnUse" x2="15000" y1="14100" x1="10100">
+ <stop stop-color="#fff" stop-opacity="0.698" offset="0"/>
+ <stop stop-color="#5db7ff" stop-opacity="0.698" offset="1.2"/>
+ </linearGradient>
+ <linearGradient id="x" y2="19500" gradientUnits="userSpaceOnUse" x2="15000" y1="14100" x1="10100">
+ <stop stop-color="#f5ffff" offset="0"/>
+ <stop stop-color="#fff" stop-opacity="0" offset="1.2"/>
+ </linearGradient>
+ <radialGradient id="ac" xlink:href="#b" gradientUnits="userSpaceOnUse" cy="17526" cx="18632" gradientTransform="matrix(.001928 0 0 .001928 33.623 -11.394)" r="40170"/>
+ <radialGradient id="ad" xlink:href="#b" gradientUnits="userSpaceOnUse" cy="17831" cx="18969" r="23166"/>
+ <radialGradient id="ae" xlink:href="#a" gradientUnits="userSpaceOnUse" cy="27370" cx="25538" gradientTransform="translate(-1.2031e-5,1.3252e-4)" r="3682.2"/>
+ <radialGradient id="af" xlink:href="#x" gradientUnits="userSpaceOnUse" cy="17398" cx="18401" gradientTransform="translate(-7.3214e-5,-4.3295e-5)" r="11888"/>
+ <radialGradient id="ag" xlink:href="#a" gradientUnits="userSpaceOnUse" cy="15674" cx="16467" gradientTransform="matrix(.0019151 0 0 .0019411 33.623 -11.394)" r="3055.2"/>
+ <radialGradient id="ah" xlink:href="#b" gradientUnits="userSpaceOnUse" cy="17753" cx="18300" gradientTransform="matrix(.001928 0 0 .001928 33.623 -11.394)" r="28430"/>
+ <radialGradient id="ai" xlink:href="#a" gradientUnits="userSpaceOnUse" cy="33086" cx="-2182.7" gradientTransform="matrix(.0032733 0 0 .0011356 31.832 15.718)" r="11540"/>
+ <radialGradient id="aj" xlink:href="#a" gradientUnits="userSpaceOnUse" cy="18596" cx="17070" gradientTransform="matrix(.0019519 0 0 .0019044 33.623 -11.394)" r="3734.6"/>
+ <linearGradient id="ao" y2="39368" xlink:href="#x" gradientUnits="userSpaceOnUse" x2="2312.5" gradientTransform="matrix(.0032733 0 0 .0011356 31.832 15.718)" y1="39560" x1="-1083.8"/>
+ <radialGradient id="ak" xlink:href="#b" gradientUnits="userSpaceOnUse" cy="11246" cx="13190" gradientTransform="translate(-9.4474e-5,4.0964e-5)" r="16248"/>
+ <linearGradient id="ap" y2="18951" xlink:href="#a" gradientUnits="userSpaceOnUse" x2="12508" gradientTransform="matrix(.001928 0 0 .001928 33.623 -11.394)" y1="-85.143" x1="-1308.3"/>
+ <radialGradient id="al" xlink:href="#a" gradientUnits="userSpaceOnUse" cy="29167" cx="26631" gradientTransform="translate(-1.1897e-4,-1.2562e-4)" r="4284.2"/>
+ <linearGradient id="y" y2="20482" gradientUnits="userSpaceOnUse" x2="6753.4" gradientTransform="matrix(.0013312 0 0 .0027924 31.832 15.718)" y1="27356" x1="850.32">
+ <stop stop-color="#a66500" offset="0"/>
+ <stop stop-color="#da8f00" offset="1"/>
+ </linearGradient>
+ <linearGradient id="z" y2="21261" gradientUnits="userSpaceOnUse" x2="3741.1" gradientTransform="matrix(.00126 0 0 .0029504 31.832 15.718)" y1="21261" x1="-3478.9">
+ <stop stop-color="#eda700" offset="0"/>
+ <stop stop-color="#fff" offset=".38824"/>
+ <stop stop-color="#ffcb50" offset="0.7"/>
+ <stop stop-color="#ed8700" offset="1"/>
+ </linearGradient>
+ <linearGradient id="aa" y2="29618" xlink:href="#a" gradientUnits="userSpaceOnUse" x2="24590" gradientTransform="matrix(.001928 0 0 .001928 33.623 -11.394)" y1="405.03" x1="389.4"/>
+ <radialGradient id="am" gradientUnits="userSpaceOnUse" cy="10444" cx="11715" gradientTransform="matrix(.001928 0 0 .001928 33.623 -11.394)" r="15132">
+ <stop stop-color="#f5ffff" stop-opacity=".49020" offset="0"/>
+ <stop stop-color="#fff" stop-opacity="0" offset="1.2"/>
+ </radialGradient>
+ </defs>
+ <g id="w" stroke-linejoin="round" stroke-linecap="round" transform="translate(-40.995 -.010729)">
+ <rect style="color:#000000" fill-opacity=".63253" ry=".89887" height="32.011" width="31.012" stroke="url(#ab)" display="block" y="10.5" x="6.495" fill="#fff"/>
+ <rect opacity=".79121" style="color:#000000" display="block" rx=".13187" ry=".11482" height="30.004" width="28.998" stroke="#fff" y="11.496" x="7.5" fill="none"/>
+ <path style="color:#000000" d="m10.184 13.468 5.316 0.032 3 3v8" stroke="#000" display="block" fill="none"/>
+ <path style="color:#000000" d="m22.5 24.5v-8l-3-3" stroke="#000" display="block" fill="none"/>
+ <path style="color:#000000" d="m26.5 24.5v-8l-3-3" stroke="#000" display="block" fill="none"/>
+ <path style="color:#000000" d="m30.5 24.5v-11" stroke="#000" display="block" fill="none"/>
+ <path style="color:#000000" d="m18.5 24.5v2" stroke="#000" stroke-width="2" display="block" fill="none"/>
+ <path style="color:#000000" d="m22.5 24.5v2" stroke="#000" stroke-width="2" display="block" fill="none"/>
+ <path style="color:#000000" d="m26.5 24.5v2" stroke="#000" stroke-width="2" display="block" fill="none"/>
+ <path style="color:#000000" d="m30.5 24.5v2" stroke="#000" stroke-width="2" display="block" fill="none"/>
+ <path style="color:#000000" d="m30.5 31.5v2" stroke="#000" stroke-width="2" display="block" fill="none"/>
+ <path style="color:#000000" d="m26.5 31.5v2" stroke="#000" stroke-width="2" display="block" fill="none"/>
+ <path style="color:#000000" d="m22.5 31.5v2" stroke="#000" stroke-width="2" display="block" fill="none"/>
+ <path style="color:#000000" d="m18.5 31.5v2" stroke="#000" stroke-width="2" display="block" fill="none"/>
+ <path style="color:#000000" d="m18.5 33.5v3l-2 2h-6" stroke="#000" display="block" fill="none"/>
+ <path style="color:#000000" d="m22.5 33.5v5" stroke="#000" display="block" fill="none"/>
+ <path style="color:#000000" d="m26.5 33.5v3l2 2h6" stroke="#000" display="block" fill="none"/>
+ <path style="color:#000000" d="m30.5 33.5 2 2h2" stroke="#000" display="block" fill="none"/>
+ <path style="color:#000000" display="block" d="m10.5 15.5h4l2 2v18l-1 1h-5v-21z" stroke="#000"/>
+ <rect style="color:#000000" display="block" ry="2.028" height="13" width="16" stroke="#000" y="22.5" x="16.5" fill="none"/>
+ </g>
+ <g transform="matrix(1.0331,0,0,1.042,-1.5828,-1.9714)">
+ <use xlink:href="#w" transform="matrix(.88516 .46528 -.46528 .88516 55.782 10.953)" height="48" width="48" y="0" x="0"/>
+ <use xlink:href="#an" transform="matrix(.97367 .22797 -.22797 .97367 9.8015 -7.4372)" height="48" width="48" y="0" x="0"/>
+ <use id="an" xlink:href="#w" transform="translate(37,1)" height="48" width="48" y="0" x="0"/>
+ <path d="m4 26v-13h28v13c-7 6-18-6-28 0z" fill-opacity=".51807" fill-rule="evenodd" fill="#fff"/>
+ </g>
+ <g fill-rule="evenodd" transform="translate(-6,53)" stroke="#000">
+ <path style="color:#000000" d="m31 31a10 10 0 1 1 -20 0 10 10 0 1 1 20 0z" fill-opacity=".3012" transform="translate(-47,-29)" stroke-width="1.5" fill="#72a0cf"/>
+ <rect style="color:#000000" stroke-width="1.0006" transform="matrix(.3824 -.924 .90415 .42722 0 0)" ry="1.1087" width="3.1174" y="-13.376" x="-14.434" height="17.268" fill="#2e3436"/>
+ </g>
+ <g transform="matrix(.69611 .19209 -.17663 .64009 -8.9202 1.1447)">
+ <g opacity=".18470" transform="matrix(.001928 0 0 .001928 33.342 -9.8361)">
+ <path fill-rule="evenodd" d="m29524 27524a2285.7 2285.7 0 1 1 -4571.4 0 2285.7 2285.7 0 1 1 4571.4 0z" transform="matrix(1.0366,0,0,1.0366,-8223.8,-8828.8)"/>
+ <path fill-rule="evenodd" d="m19683 17385c-463.92-572.08-642.91-183.34-885.67-376.28-186.31-167 126.72-430.52-278.63-868.94l-1977.9 1967.3c337.99 333.5 523.59 91.68 730.6 375.12 155.23 225.6-17.11 519.24 482.71 842.41l1928.9-1939.6z"/>
+ <rect fill-rule="evenodd" transform="rotate(-45)" height="1619" width="4666.7" y="23097" x="-2064"/>
+ <path fill-rule="evenodd" d="m19649 9700.3c0 5058-4100.3 9158.4-9158.4 9158.4-5058 0-9158.4-4100.3-9158.4-9158.4 0-5058 4100.3-9158.4 9158.4-9158.4 5058 0 9158.4 4100.3 9158.4 9158.4zm-907.21-0.01c0 4557-3694.2 8251.2-8251.2 8251.2s-8251.1-3694.2-8251.1-8251.2 3694.2-8251.2 8251.1-8251.2c4557 0 8251.2 3694.2 8251.2 8251.2z"/>
+ <path fill-rule="evenodd" d="m29524 27524a2285.7 2285.7 0 1 1 -4571.4 0 2285.7 2285.7 0 1 1 4571.4 0z" transform="matrix(.95833 0 0 .95833 944.44 99.204)"/>
+ <rect transform="rotate(-45)" height="11583" width="5522.2" y="26814" x="-2375"/>
+ </g>
+ <path opacity="0.293" d="m68.252 9.6924c0 6.7349-5.4597 12.195-12.195 12.195-6.7348 0-12.194-5.4597-12.194-12.195 0-6.7348 5.4597-12.195 12.194-12.195 6.7349 0 12.195 5.4597 12.195 12.195zm1.8483-2.402c0 8.9894-7.2873 16.277-16.277 16.277-8.9894 0-16.277-7.2874-16.277-16.277 0-8.9894 7.2874-16.277 16.277-16.277 8.9894 0 16.277 7.2874 16.277 16.277z" fill-rule="evenodd" fill="url(#ac)"/>
+ <path opacity=".68790" d="m19395 9325.5a9226.1 9226.1 0 1 1 -18452 0 9226.1 9226.1 0 1 1 18452 0z" fill-rule="evenodd" transform="matrix(.0017972 0 0 .0017972 36.035 -9.712)" fill="url(#ad)"/>
+ <path d="m29524 27524a2285.7 2285.7 0 1 1 -4571.4 0 2285.7 2285.7 0 1 1 4571.4 0z" fill-rule="evenodd" transform="matrix(.0019986 0 0 .0019986 17.768 -28.417)" fill="#717386"/>
+ <path d="m29524 27524a2285.7 2285.7 0 1 1 -4571.4 0 2285.7 2285.7 0 1 1 4571.4 0z" fill-rule="evenodd" transform="matrix(.0018239 0 0 .0018239 22.527 -23.608)" fill="url(#ae)"/>
+ <path d="m19395 9325.5a9226.1 9226.1 0 1 1 -18452 0 9226.1 9226.1 0 1 1 18452 0z" fill-rule="evenodd" transform="matrix(.0017972 0 0 .0017972 35.852 -9.712)" fill="url(#af)"/>
+ <path fill-rule="evenodd" fill="url(#ag)" d="m71.572 22.125c-0.89446-1.103-1.2396-0.35349-1.7076-0.72548-0.35921-0.32198 0.24432-0.83006-0.53721-1.6754l-3.8136 3.793c0.65166 0.643 1.0095 0.17676 1.4086 0.72325 0.29929 0.43497-0.03299 1.0011 0.93068 1.6242l3.7191-3.7396z"/>
+ <path fill-rule="evenodd" fill="url(#ah)" d="m69.601 7.6645c0 8.3692-6.7354 15.154-15.044 15.154-8.3085 0-15.044-6.7846-15.044-15.154 0-8.3692 6.7354-15.154 15.044-15.154 8.3085 0 15.044 6.7845 15.044 15.154zm1.2321-0.61426c0 9.3909-7.6128 17.004-17.004 17.004-9.3909 0-17.004-7.6128-17.004-17.004 0-9.3909 7.6128-17.004 17.004-17.004 9.3909 0 17.004 7.6128 17.004 17.004z"/>
+ <rect opacity=".9554" fill-rule="evenodd" transform="rotate(-45)" height="3.1216" width="8.9976" y="60.251" x="27.853" fill="url(#ai)"/>
+ <path fill-rule="evenodd" fill="url(#aj)" d="m71.343 22.447c-0.89446-1.103-1.2396-0.35349-1.7076-0.72548-0.35921-0.32198 0.19842-1.1055-0.81263-2.0885l-3.5381 3.4717c0.78938 0.78072 1.5604 0.26858 1.9595 0.81506 0.29929 0.43497-0.03299 1.0011 0.93068 1.6242l3.1682-3.0969z"/>
+ <rect fill-rule="evenodd" transform="rotate(-45)" height="2.5865" width="7.4552" y="60" x="28.624" fill="url(#ao)"/>
+ <path opacity=".21020" d="m13469 7844a3569.2 3569.2 0 1 1 -7138.4 0 3569.2 3569.2 0 1 1 7138.4 0z" fill-rule="evenodd" transform="matrix(.0016277 0 0 .0016277 32.568 -13.102)" fill="url(#ak)"/>
+ <path fill-rule="evenodd" fill="url(#ap)" d="m71.507 7.3083c0 9.7521-7.9056 17.658-17.658 17.658-9.7521 0-17.658-7.9056-17.658-17.658 0-9.7521 7.9056-17.658 17.658-17.658 9.7521 0 17.658 7.9056 17.658 17.658zm-1.7491-0.000017c0 8.7861-7.1225 15.909-15.909 15.909-8.786 0-15.909-7.1225-15.909-15.909 0-8.7861 7.1225-15.909 15.909-15.909 8.7861 0 15.909 7.1225 15.909 15.909z"/>
+ <path d="m29524 27524a2285.7 2285.7 0 1 1 -4571.4 0 2285.7 2285.7 0 1 1 4571.4 0z" fill-rule="evenodd" transform="matrix(.0018477 0 0 .0018477 35.444 -11.203)" fill="#808080"/>
+ <path d="m29524 27524a2285.7 2285.7 0 1 1 -4571.4 0 2285.7 2285.7 0 1 1 4571.4 0z" fill-rule="evenodd" transform="matrix(.0016773 0 0 .0016773 39.925 -6.5347)" fill="url(#al)"/>
+ <rect transform="rotate(-45)" height="22.333" width="10.647" y="67.417" x="27.253" fill="url(#y)"/>
+ <rect transform="rotate(-45)" height="19.934" width="8.513" y="68.617" x="28.321" fill="url(#z)"/>
+ <path fill-rule="evenodd" fill="url(#aa)" d="m70.103 7.1705c0 8.9219-7.2327 16.155-16.155 16.155-8.9219 0-16.155-7.2327-16.155-16.155 0-8.9219 7.2327-16.155 16.155-16.155 8.9219 0 16.155 7.2327 16.155 16.155zm-0.48222 0.000006c0 8.6556-7.0168 15.672-15.672 15.672-8.6556 0-15.672-7.0168-15.672-15.672 0-8.6556 7.0168-15.672 15.672-15.672 8.6556 0 15.672 7.0168 15.672 15.672z"/>
+ <path opacity=".54140" d="m56.503 2.2283c0 3.6148-2.9304 6.5452-6.5451 6.5452-3.6148 0-6.5451-2.9304-6.5451-6.5452s2.9304-6.5451 6.5451-6.5451c3.6148 0 6.5451 2.9304 6.5451 6.5451zm1.1483-0.68894c0 4.6295-3.7529 8.3824-8.3824 8.3824s-8.3824-3.7529-8.3824-8.3824 3.7529-8.3824 8.3824-8.3824 8.3824 3.7529 8.3824 8.3824z" fill-rule="evenodd" fill="url(#am)"/>
+ <path fill-rule="evenodd" fill="#9a9ba9" d="m67.861 25.319 0.29067 0.23655 3.2223-3.1012-0.17592-0.28246-3.337 3.1471z"/>
+ <path fill-rule="evenodd" fill="#8a92a0" d="m65.338 23.174 0.2678 0.19753 3.4914-3.4229-0.20844-0.32179-3.5507 3.5472z"/>
+ </g>
+</svg>
diff --git a/resources/linux/mime/icons/hicolor/scalable/apps/kicad.svg b/resources/linux/mime/icons/hicolor/scalable/apps/kicad.svg
new file mode 100644
index 0000000..f4f1e94
--- /dev/null
+++ b/resources/linux/mime/icons/hicolor/scalable/apps/kicad.svg
@@ -0,0 +1,276 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ height="26"
+ width="26"
+ version="1.1"
+ id="svg2"
+ inkscape:version="0.48.1 r9760"
+ sodipodi:docname="icon_kicad.svg"
+ inkscape:export-filename="F:\kicad-launchpad\testing\bitmaps_png\sources\icon_kicad.png"
+ inkscape:export-xdpi="60"
+ inkscape:export-ydpi="60">
+ <metadata
+ id="metadata581">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1600"
+ inkscape:window-height="876"
+ id="namedview579"
+ showgrid="true"
+ inkscape:zoom="22.774716"
+ inkscape:cx="23.593222"
+ inkscape:cy="12.172521"
+ inkscape:window-x="0"
+ inkscape:window-y="24"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg2"
+ inkscape:snap-grids="false"
+ inkscape:snap-to-guides="false">
+ <inkscape:grid
+ type="xygrid"
+ id="grid3548"
+ empspacing="5"
+ visible="true"
+ enabled="true"
+ snapvisiblegridlinesonly="true" />
+ </sodipodi:namedview>
+ <defs
+ id="defs4">
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10292-9-2-6-6"
+ id="linearGradient10600-2"
+ gradientUnits="userSpaceOnUse"
+ x1="110.96875"
+ y1="976.29968"
+ x2="110.96875"
+ y2="992.375" />
+ <linearGradient
+ id="linearGradient10292-9-2-6-6"
+ inkscape:collect="always">
+ <stop
+ id="stop10294-9-9-7-8"
+ offset="0"
+ style="stop-color: rgb(102, 102, 102); stop-opacity: 1;" />
+ <stop
+ id="stop10296-6-6-7-4"
+ offset="1"
+ style="stop-color: rgb(51, 51, 51); stop-opacity: 1;" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10125-12"
+ id="linearGradient10596-0"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1,0,0,1.00011,0,-0.10551)"
+ x1="110.96875"
+ y1="976.29968"
+ x2="110.99982"
+ y2="991.87488" />
+ <linearGradient
+ id="linearGradient10125-12">
+ <stop
+ style="stop-color: rgb(201, 156, 0); stop-opacity: 1;"
+ offset="0"
+ id="stop10127-04" />
+ <stop
+ style="stop-color: rgb(135, 105, 0); stop-opacity: 1;"
+ offset="1"
+ id="stop10129-4" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10125-12"
+ id="linearGradient10598-1"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.00399,0,0,1.00959,-1.1658,-9.36636)"
+ x1="110.96875"
+ y1="976.29968"
+ x2="110.96875"
+ y2="992.375" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10286-3"
+ id="linearGradient10602-2"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.99213,0,0,3.00466,-933.712,-1623.08)"
+ x1="294.3429"
+ y1="256.58133"
+ x2="294.41818"
+ y2="243.13852" />
+ <linearGradient
+ id="linearGradient10286-3">
+ <stop
+ id="stop10288-8"
+ offset="0"
+ style="stop-color: rgb(179, 179, 179); stop-opacity: 1;" />
+ <stop
+ id="stop10290-7"
+ offset="1"
+ style="stop-color: rgb(230, 230, 230); stop-opacity: 1;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient15606-1">
+ <stop
+ style="stop-color: rgb(255, 255, 255); stop-opacity: 0.588235;"
+ offset="0"
+ id="stop15608-0" />
+ <stop
+ style="stop-color: rgb(255, 255, 255); stop-opacity: 0.862745;"
+ offset="1"
+ id="stop15610-5" />
+ </linearGradient>
+ <linearGradient
+ y2="-781.62268"
+ x2="209.0625"
+ y1="-765.46082"
+ x1="209.0625"
+ gradientTransform="matrix(1.5,0,0,1.5,-368,281.32)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient8673"
+ xlink:href="#linearGradient15606-1"
+ inkscape:collect="always" />
+ </defs>
+ <g
+ style="display:inline"
+ id="g10552"
+ transform="matrix(0.59915384,0,0,0.51444411,36.445696,461.74209)">
+ <g
+ transform="matrix(2.9999,0,0,2.99884,-391.989,-3823.61)"
+ id="g10554"
+ style="fill:url(#linearGradient10600-2);fill-opacity:1;display:inline">
+ <path
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccccccccccc"
+ id="path10556"
+ d="m 111.8125,976.3733 c -0.4584,0.0875 -0.82072,0.53346 -0.8125,1.00011 l 0,14.00148 c 5e-5,0.52364 0.47642,1.00006 1,1.00011 l 11.33333,0 c 0.52358,-5e-5 0.99995,-0.47647 1,-1.00011 l 0,-8.73078 c 0.006,-0.26409 -0.0975,-0.52903 -0.28125,-0.71883 l -5.27042,-5.2707 c -0.18978,-0.18372 -0.45469,-0.2874 -0.71875,-0.28128 l -6.06291,0 c -0.0623,-0.006 -0.12518,-0.006 -0.1875,0 z"
+ style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0pt;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;text-anchor:start;color:#000000;fill:url(#linearGradient10596-0);fill-opacity:1;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;font-family:Andale Mono" />
+ <path
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccccccc"
+ id="path10558"
+ d="m 120.66272,976.3765 c -0.33282,0.10664 -0.44483,0.61349 -0.18825,0.85184 l 3.01199,3.02879 c 0.27825,0.26531 0.83246,0.0383 0.84712,-0.34705 l 0,-3.02878 c -3e-5,-0.26431 -0.23916,-0.50477 -0.502,-0.5048 l -3.01199,0 c -0.0519,-0.008 -0.105,-0.008 -0.15687,0 z"
+ style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0pt;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;text-anchor:start;color:#000000;fill:url(#linearGradient10598-1);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;font-family:Andale Mono" />
+ </g>
+ <path
+ sodipodi:nodetypes="cccccccccccccccc"
+ id="path10560"
+ d="m -55.9999,-892.61507 0,0.75 0,40.50004 0,0.72723 0.74999,0 32.49943,0 0.74998,0 0,-0.72723 0,-25.50003 0,-0.3282 -0.23436,-0.2343 -14.90598,-15.00001 -0.18748,-0.1875 -0.32813,0 -17.59346,0 -0.74999,0 z"
+ style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0pt;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;text-anchor:start;color:#000000;fill:url(#linearGradient10602-2);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;font-family:Andale Mono"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:url(#linearGradient8673);fill-opacity:1;stroke:none;display:inline"
+ d="m -54,-890.6378 16,0 14,14 0,24 -30,0 0,-38 z"
+ id="path10562"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccccc"
+ inkscape:export-filename="/home/bedipp/DocumentFoundation/Writer_16.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90" />
+ </g>
+ <path
+ id="path303-2"
+ d="m 15.299838,22.117153 0,-3.724729 6.791318,0"
+ style="fill:none;stroke:#a33e03;stroke-width:0.58063614px;stroke-opacity:1"
+ inkscape:connector-curvature="0" />
+ <path
+ id="path305-2"
+ d="m 16.19962,19.500792 1.785093,0"
+ style="fill:none;stroke:#a33e03;stroke-width:0.73576558px;stroke-opacity:1"
+ inkscape:connector-curvature="0" />
+ <path
+ id="path307-0"
+ d="m 19.099716,19.5447 2.802461,0"
+ style="fill:none;stroke:#a33e03;stroke-width:0.74753916px;stroke-opacity:1"
+ inkscape:connector-curvature="0" />
+ <path
+ id="path309-5"
+ d="m 15.947174,21.474047 2.114702,0"
+ style="fill:none;stroke:#a33e03;stroke-width:0.86295766px;stroke-opacity:1"
+ inkscape:connector-curvature="0" />
+ <path
+ id="path311-5"
+ d="m 20.091621,21.496001 1.868901,0"
+ style="fill:none;stroke:#a33e03;stroke-width:1.01523209px;stroke-opacity:1"
+ inkscape:connector-curvature="0" />
+ <path
+ style="color:#000000;fill:none;stroke:#a33e03;stroke-width:0.9867416;stroke-linecap:square;stroke-opacity:1"
+ d="m 8.5368289,14.555249 0,3.93792"
+ id="path315-9"
+ inkscape:connector-curvature="0" />
+ <path
+ style="color:#000000;fill:none;stroke:#a33e03;stroke-width:0.94667083;stroke-linecap:round;stroke-opacity:1"
+ d="m 10.555193,14.642423 -1.9744558,1.81556"
+ id="path317-0"
+ inkscape:connector-curvature="0" />
+ <path
+ style="color:#000000;fill:none;stroke:#a33e03;stroke-width:0.94667083;stroke-linecap:round;stroke-opacity:1"
+ d="M 10.555193,18.62481 8.5807372,16.80925"
+ id="path319-2"
+ inkscape:connector-curvature="0" />
+ <path
+ style="color:#000000;fill:none;stroke:#a33e03;stroke-width:0.94667083;stroke-linecap:square;stroke-opacity:1"
+ d="m 8.5954516,16.501892 -3.7958892,0"
+ id="path321-8"
+ inkscape:connector-curvature="0" />
+ <path
+ style="color:#000000;fill:none;stroke:#a33e03;stroke-width:0.88937062;stroke-linecap:round;stroke-opacity:1"
+ d="m 10.555193,18.98439 0,2.135555"
+ id="path323-3"
+ inkscape:connector-curvature="0" />
+ <path
+ style="color:#000000;fill:none;stroke:#a33e03;stroke-width:0.94667083;stroke-linecap:square;stroke-opacity:1"
+ d="m 11.586328,21.454457 -1.9527374,0"
+ id="path325-8"
+ inkscape:connector-curvature="0" />
+ <path
+ style="color:#000000;fill:none;stroke:#a33e03;stroke-width:0.90969408;stroke-linecap:round;stroke-opacity:1"
+ d="m 10.496239,14.485005 0,-2.89048"
+ id="path327-0"
+ inkscape:connector-curvature="0" />
+ <path
+ style="color:#000000;fill:none;stroke:#a33e03;stroke-width:0.95496774;stroke-linecap:square;stroke-opacity:1"
+ d="m 11.530374,11.420821 -1.9661595,0 0,-3.7106413 1.9661595,0 0,3.7106413"
+ id="path329-4"
+ inkscape:connector-curvature="0" />
+ <path
+ style="color:#000000;fill:none;stroke:#a33e03;stroke-width:0.95042706;stroke-linecap:square;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ d="m 10.965511,13.559011 4.47239,0"
+ id="path331-0"
+ inkscape:connector-curvature="0" />
+ <path
+ style="color:#000000;fill:#a33e03;fill-opacity:1;stroke:#a33e03;stroke-width:0.94674802;stroke-linecap:square;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ d="m 10.539398,4.0749115 0,3.5088427"
+ id="path333-9"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:none;stroke:#a33e03;stroke-width:1.00222099px;stroke-opacity:1"
+ d="m 16.356524,12.501212 4.102533,-0.02269 1.085413,0.990712 -1.075141,0.937806 -4.112805,-0.03024 0,-1.875614 z"
+ id="path361-0"
+ inkscape:connector-curvature="0" />
+</svg>
diff --git a/resources/linux/mime/icons/hicolor/scalable/apps/pcbcalculator.svg b/resources/linux/mime/icons/hicolor/scalable/apps/pcbcalculator.svg
new file mode 100644
index 0000000..c71e211
--- /dev/null
+++ b/resources/linux/mime/icons/hicolor/scalable/apps/pcbcalculator.svg
@@ -0,0 +1,1100 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ height="48"
+ width="48"
+ version="1.1"
+ id="svg2"
+ inkscape:version="0.48.1 "
+ sodipodi:docname="icon_pcbcalculator.svg"
+ inkscape:export-filename="F:\kicad-launchpad\testing\bitmaps_png\sources\icon_pcbcalculator.png"
+ inkscape:export-xdpi="60"
+ inkscape:export-ydpi="60">
+ <metadata
+ id="metadata297">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1143"
+ inkscape:window-height="741"
+ id="namedview295"
+ showgrid="false"
+ inkscape:zoom="5.4791667"
+ inkscape:cx="24"
+ inkscape:cy="24"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="svg2" />
+ <defs
+ id="defs4">
+ <radialGradient
+ id="ay"
+ xlink:href="#ap"
+ gradientUnits="userSpaceOnUse"
+ cy="486.65"
+ cx="605.71"
+ gradientTransform="matrix(-2.7744,0,0,1.9697,112.76,-872.89)"
+ r="117.14" />
+ <linearGradient
+ id="ap">
+ <stop
+ offset="0"
+ id="stop8" />
+ <stop
+ stop-opacity="0"
+ offset="1"
+ id="stop10" />
+ </linearGradient>
+ <radialGradient
+ id="ax"
+ xlink:href="#ap"
+ gradientUnits="userSpaceOnUse"
+ cy="486.65"
+ cx="605.71"
+ gradientTransform="matrix(2.7744,0,0,1.9697,-1891.6,-872.89)"
+ r="117.14" />
+ <linearGradient
+ id="aq"
+ y2="609.51"
+ gradientUnits="userSpaceOnUse"
+ x2="302.86"
+ gradientTransform="matrix(2.7744,0,0,1.9697,-1892.2,-872.89)"
+ y1="366.65"
+ x1="302.86">
+ <stop
+ stop-opacity="0"
+ offset="0"
+ id="stop14" />
+ <stop
+ offset=".5"
+ id="stop16" />
+ <stop
+ stop-opacity="0"
+ offset="1"
+ id="stop18" />
+ </linearGradient>
+ <radialGradient
+ id="ar"
+ gradientUnits="userSpaceOnUse"
+ cy="3.7561"
+ cx="8.8244"
+ gradientTransform="matrix(.96827 0 0 1.0328 3.3536 .64645)"
+ r="37.752">
+ <stop
+ stop-color="#a3a3a3"
+ offset="0"
+ id="stop21" />
+ <stop
+ stop-color="#4c4c4c"
+ offset="1"
+ id="stop23" />
+ </radialGradient>
+ <radialGradient
+ id="as"
+ gradientUnits="userSpaceOnUse"
+ cy="35.737"
+ cx="33.967"
+ gradientTransform="scale(.96049 1.0411)"
+ r="86.708">
+ <stop
+ stop-color="#fafafa"
+ offset="0"
+ id="stop26" />
+ <stop
+ stop-color="#bbb"
+ offset="1"
+ id="stop28" />
+ </radialGradient>
+ <radialGradient
+ id="at"
+ gradientUnits="userSpaceOnUse"
+ cy="7.2679"
+ cx="8.1436"
+ gradientTransform="matrix(.96827 0 0 1.0328 3.3536 .64645)"
+ r="38.159">
+ <stop
+ stop-color="#fff"
+ offset="0"
+ id="stop31" />
+ <stop
+ stop-color="#f8f8f8"
+ offset="1"
+ id="stop33" />
+ </radialGradient>
+ <radialGradient
+ id="au"
+ gradientUnits="userSpaceOnUse"
+ cy="114.57"
+ cx="20.892"
+ gradientTransform="matrix(.2297 0 0 .2297 4.6135 3.9798)"
+ r="5.256">
+ <stop
+ stop-color="#f0f0f0"
+ offset="0"
+ id="stop36" />
+ <stop
+ stop-color="#9a9a9a"
+ offset="1"
+ id="stop38" />
+ </radialGradient>
+ <radialGradient
+ id="av"
+ gradientUnits="userSpaceOnUse"
+ cy="64.568"
+ cx="20.892"
+ gradientTransform="matrix(.2297 0 0 .2297 4.6135 3.9798)"
+ r="5.257">
+ <stop
+ stop-color="#f0f0f0"
+ offset="0"
+ id="stop41" />
+ <stop
+ stop-color="#9a9a9a"
+ offset="1"
+ id="stop43" />
+ </radialGradient>
+ <linearGradient
+ id="al"
+ y2="90.239"
+ xlink:href="#an"
+ gradientUnits="userSpaceOnUse"
+ x2="32.251"
+ gradientTransform="matrix(1.0238,0,0,1.0119,-1.1429,-98.071)"
+ y1="6.1317"
+ x1="32.251" />
+ <linearGradient
+ id="an"
+ y2="7.0165"
+ gradientUnits="userSpaceOnUse"
+ x2="45.448"
+ gradientTransform="matrix(1.0059 0 0 .99417 100 0)"
+ y1="92.54"
+ x1="45.448">
+ <stop
+ offset="0"
+ id="stop47" />
+ <stop
+ stop-opacity=".58824"
+ offset="1"
+ id="stop49" />
+ </linearGradient>
+ <linearGradient
+ id="bu"
+ y2="49"
+ xlink:href="#ao"
+ gradientUnits="userSpaceOnUse"
+ x2="37"
+ y1="16"
+ x1="37" />
+ <linearGradient
+ id="ao">
+ <stop
+ stop-color="#717249"
+ offset="0"
+ id="stop53" />
+ <stop
+ stop-color="#717249"
+ stop-opacity="0"
+ offset="1"
+ id="stop55" />
+ </linearGradient>
+ <linearGradient
+ id="bt"
+ y2="39"
+ xlink:href="#ao"
+ gradientUnits="userSpaceOnUse"
+ x2="34"
+ y1="14"
+ x1="34" />
+ <linearGradient
+ id="bb"
+ y2="5.9877"
+ gradientUnits="userSpaceOnUse"
+ x2="48"
+ y1="90"
+ x1="48">
+ <stop
+ stop-color="#bcb5ac"
+ offset="0"
+ id="stop59" />
+ <stop
+ stop-color="#e9e7e4"
+ offset="1"
+ id="stop61" />
+ </linearGradient>
+ <linearGradient
+ id="az"
+ y2="63.893"
+ gradientUnits="userSpaceOnUse"
+ x2="36.357"
+ y1="6"
+ x1="36.357">
+ <stop
+ stop-color="#fff"
+ offset="0"
+ id="stop64" />
+ <stop
+ stop-color="#fff"
+ stop-opacity="0"
+ offset="1"
+ id="stop66" />
+ </linearGradient>
+ <linearGradient
+ id="bs"
+ y2="35.771"
+ gradientUnits="userSpaceOnUse"
+ x2="64"
+ gradientTransform="matrix(1.025,0,0,1.0625,-9.6,-4)"
+ y1="15.5"
+ x1="64">
+ <stop
+ stop-color="#ccc7c1"
+ offset="0"
+ id="stop69" />
+ <stop
+ stop-color="#fefefd"
+ stop-opacity=".58015"
+ offset="1"
+ id="stop71" />
+ </linearGradient>
+ <radialGradient
+ id="aw"
+ gradientUnits="userSpaceOnUse"
+ cy="90.172"
+ cx="48"
+ gradientTransform="matrix(1.1573 0 0 .99591 -7.551 .19713)"
+ r="42">
+ <stop
+ stop-color="#fff"
+ offset="0"
+ id="stop74" />
+ <stop
+ stop-color="#fff"
+ stop-opacity="0"
+ offset="1"
+ id="stop76" />
+ </radialGradient>
+ <linearGradient
+ id="bk"
+ y2="112"
+ xlink:href="#a"
+ gradientUnits="userSpaceOnUse"
+ x2="30.247"
+ gradientTransform="matrix(.75 0 0 .75 -3 -12.75)"
+ y1="102"
+ x1="30.247" />
+ <linearGradient
+ id="a">
+ <stop
+ stop-color="#323232"
+ offset="0"
+ id="stop80" />
+ <stop
+ stop-color="#505050"
+ offset="1"
+ id="stop82" />
+ </linearGradient>
+ <linearGradient
+ id="bl"
+ y2="112"
+ xlink:href="#a"
+ gradientUnits="userSpaceOnUse"
+ x2="30.247"
+ gradientTransform="matrix(.75 0 0 .75 15 -12.75)"
+ y1="102"
+ x1="30.247" />
+ <linearGradient
+ id="bm"
+ y2="112"
+ xlink:href="#a"
+ gradientUnits="userSpaceOnUse"
+ x2="30.247"
+ gradientTransform="matrix(.75 0 0 .75 33 -12.75)"
+ y1="102"
+ x1="30.247" />
+ <linearGradient
+ id="bn"
+ y2="85.997"
+ xlink:href="#am"
+ gradientUnits="userSpaceOnUse"
+ x2="95.746"
+ gradientTransform="matrix(.75 0 0 .75 3 -.75002)"
+ y1="96"
+ x1="95.746" />
+ <linearGradient
+ id="am">
+ <stop
+ stop-color="#928075"
+ offset="0"
+ id="stop88" />
+ <stop
+ stop-color="#5b4f48"
+ offset="1"
+ id="stop90" />
+ </linearGradient>
+ <linearGradient
+ id="bg"
+ y2="112"
+ xlink:href="#a"
+ gradientUnits="userSpaceOnUse"
+ x2="30.247"
+ gradientTransform="matrix(.75 0 0 .75 -3 -24.75)"
+ y1="102"
+ x1="30.247" />
+ <linearGradient
+ id="bh"
+ y2="112"
+ xlink:href="#a"
+ gradientUnits="userSpaceOnUse"
+ x2="30.247"
+ gradientTransform="matrix(.75 0 0 .75 15 -24.75)"
+ y1="102"
+ x1="30.247" />
+ <linearGradient
+ id="bi"
+ y2="112"
+ xlink:href="#a"
+ gradientUnits="userSpaceOnUse"
+ x2="30.247"
+ gradientTransform="matrix(.75 0 0 .75 33 -24.75)"
+ y1="102"
+ x1="30.247" />
+ <linearGradient
+ id="bj"
+ y2="70"
+ xlink:href="#am"
+ gradientUnits="userSpaceOnUse"
+ x2="96"
+ gradientTransform="matrix(.75 0 0 .75 3 -.75001)"
+ y1="80"
+ x1="96" />
+ <linearGradient
+ id="bc"
+ y2="112"
+ xlink:href="#a"
+ gradientUnits="userSpaceOnUse"
+ x2="30.247"
+ gradientTransform="matrix(.75 0 0 .75 -3 -36.75)"
+ y1="102"
+ x1="30.247" />
+ <linearGradient
+ id="bd"
+ y2="112"
+ xlink:href="#a"
+ gradientUnits="userSpaceOnUse"
+ x2="30.247"
+ gradientTransform="matrix(.75 0 0 .75 15 -36.75)"
+ y1="102"
+ x1="30.247" />
+ <linearGradient
+ id="be"
+ y2="112"
+ xlink:href="#a"
+ gradientUnits="userSpaceOnUse"
+ x2="30.247"
+ gradientTransform="matrix(.75 0 0 .75 33 -36.75)"
+ y1="102"
+ x1="30.247" />
+ <linearGradient
+ id="bf"
+ y2="64"
+ gradientUnits="userSpaceOnUse"
+ x2="93.747"
+ gradientTransform="matrix(.75 0 0 .75 3 -.75001)"
+ y1="54"
+ x1="93.747">
+ <stop
+ stop-color="#802b27"
+ offset="0"
+ id="stop100" />
+ <stop
+ stop-color="#a93833"
+ offset="1"
+ id="stop102" />
+ </linearGradient>
+ <linearGradient
+ id="bo"
+ y2="112"
+ xlink:href="#a"
+ gradientUnits="userSpaceOnUse"
+ x2="30.247"
+ gradientTransform="matrix(.75 0 0 .75 -3 -.75002)"
+ y1="102"
+ x1="30.247" />
+ <linearGradient
+ id="bp"
+ y2="112"
+ xlink:href="#am"
+ gradientUnits="userSpaceOnUse"
+ x2="53.333"
+ gradientTransform="matrix(.75 0 0 .75 -.99999 -159.75)"
+ y1="102"
+ x1="53.333" />
+ <linearGradient
+ id="bq"
+ y2="102"
+ xlink:href="#am"
+ gradientUnits="userSpaceOnUse"
+ x2="74.667"
+ gradientTransform="matrix(.75 0 0 .75 1 -.75002)"
+ y1="112"
+ x1="74.667" />
+ <linearGradient
+ id="br"
+ y2="112.05"
+ gradientUnits="userSpaceOnUse"
+ x2="94.746"
+ gradientTransform="matrix(.75 0 0 .75 3 -.75002)"
+ y1="102"
+ x1="94.746">
+ <stop
+ stop-color="#c06517"
+ offset="0"
+ id="stop108" />
+ <stop
+ stop-color="#e88b3b"
+ offset="1"
+ id="stop110" />
+ </linearGradient>
+ <linearGradient
+ id="ba"
+ y2="90.239"
+ xlink:href="#an"
+ gradientUnits="userSpaceOnUse"
+ x2="32.251"
+ gradientTransform="translate(0,-97)"
+ y1="6.1317"
+ x1="32.251" />
+ </defs>
+ <g
+ transform="matrix(.95571 0 0 .92396 -1.3711 2.5805)"
+ id="g113">
+ <g
+ transform="matrix(.021055 0 0 .020868 42.852 41.154)"
+ id="g115">
+ <rect
+ opacity=".40206"
+ style="color:#000000"
+ height="478.36"
+ width="1339.6"
+ y="-150.7"
+ x="-1559.3"
+ fill="url(#aq)"
+ id="rect117" />
+ <path
+ opacity=".40206"
+ style="color:#000000"
+ fill="url(#ax)"
+ d="m-219.62-150.68v478.33c142.87 0.90045 345.4-107.17 345.4-239.2s-159.44-239.13-345.4-239.13z"
+ id="path119" />
+ <path
+ opacity=".40206"
+ style="color:#000000"
+ fill="url(#ay)"
+ d="m-1559.3-150.68v478.33c-142.87 0.90045-345.4-107.17-345.4-239.2s159.44-239.13 345.4-239.13z"
+ id="path121" />
+ </g>
+ </g>
+ <g
+ transform="matrix(.95571 0 0 .92396 -1.3711 2.5805)"
+ id="g123">
+ <rect
+ stroke-linejoin="round"
+ style="color:#000000"
+ display="block"
+ ry="1.149"
+ height="40.92"
+ width="34.875"
+ stroke="url(#ar)"
+ stroke-linecap="round"
+ y="3.6464"
+ x="6.6036"
+ fill="url(#as)"
+ id="rect125" />
+ <rect
+ stroke-linejoin="round"
+ style="color:#000000"
+ display="block"
+ rx=".14905"
+ ry=".14905"
+ height="38.946"
+ width="32.776"
+ stroke="url(#at)"
+ stroke-linecap="round"
+ y="4.5839"
+ x="7.6661"
+ fill="none"
+ id="rect127" />
+ <g
+ transform="translate(.64645 -.037989)"
+ id="g129">
+ <g
+ fill="#fff"
+ transform="matrix(.2297 0 0 .2297 4.9671 4.245)"
+ stroke="#000"
+ id="g131">
+ <path
+ d="m23.428 113.07c0 1.973-1.6 3.572-3.573 3.572-1.974 0-3.573-1.6-3.573-3.572 0-1.974 1.6-3.573 3.573-3.573s3.573 1.6 3.573 3.573z"
+ id="path133" />
+ <path
+ d="m23.428 63.07c0 1.973-1.6 3.573-3.573 3.573-1.974 0-3.573-1.6-3.573-3.573 0-1.974 1.6-3.573 3.573-3.573s3.573 1.6 3.573 3.573z"
+ id="path135" />
+ </g>
+ <path
+ fill="url(#au)"
+ d="m9.995 29.952c0 0.4532-0.36752 0.8205-0.82073 0.8205-0.45343 0-0.82073-0.36752-0.82073-0.8205 0-0.45343 0.36752-0.82073 0.82073-0.82073 0.4532 0 0.82073 0.36752 0.82073 0.82073z"
+ id="path137" />
+ <path
+ fill="url(#av)"
+ d="m9.995 18.467c0 0.4532-0.36752 0.82073-0.82073 0.82073-0.45343 0-0.82073-0.36752-0.82073-0.82073 0-0.45343 0.36752-0.82073 0.82073-0.82073 0.4532 0 0.82073 0.36752 0.82073 0.82073z"
+ id="path139" />
+ </g>
+ <path
+ d="m11.506 5.4943v37.907"
+ stroke-opacity=".017544"
+ stroke="#000"
+ stroke-width=".98855"
+ fill="none"
+ id="path141" />
+ <path
+ d="m12.5 5.0205v38.018"
+ stroke-opacity=".20468"
+ stroke="#fff"
+ fill="none"
+ id="path143" />
+ </g>
+ <path
+ stroke-linejoin="round"
+ style="color:#000000"
+ d="m12.044 29.784v9.2396"
+ stroke-opacity=".74510"
+ stroke="#9b9b9b"
+ stroke-linecap="round"
+ stroke-width=".93970"
+ fill="none"
+ id="path145" />
+ <path
+ stroke-linejoin="round"
+ style="color:#000000"
+ d="m16.822 29.784-4.7786 2.7719"
+ stroke-opacity=".74510"
+ stroke="#9b9b9b"
+ stroke-linecap="round"
+ stroke-width=".93970"
+ fill="none"
+ id="path147" />
+ <path
+ stroke-linejoin="round"
+ style="color:#000000"
+ d="m16.822 39.023-4.778-2.771"
+ stroke-opacity=".74510"
+ stroke="#9b9b9b"
+ stroke-linecap="round"
+ stroke-width=".93970"
+ fill="none"
+ id="path149" />
+ <path
+ stroke-linejoin="round"
+ style="color:#000000"
+ d="m11.531 34.365h-3.3791"
+ stroke-opacity=".74510"
+ stroke="#9b9b9b"
+ stroke-linecap="round"
+ stroke-width=".89703"
+ fill="none"
+ id="path151" />
+ <path
+ stroke-linejoin="round"
+ style="color:#000000"
+ d="m16.822 39.023v2.7719"
+ stroke-opacity=".74510"
+ stroke="#9b9b9b"
+ stroke-linecap="round"
+ stroke-width=".93970"
+ fill="none"
+ id="path153" />
+ <path
+ stroke-linejoin="round"
+ style="color:#000000"
+ d="m18.734 41.795h-3.823"
+ stroke-opacity=".74510"
+ stroke="#9b9b9b"
+ stroke-linecap="round"
+ stroke-width=".93970"
+ fill="none"
+ id="path155" />
+ <path
+ stroke-linejoin="round"
+ style="color:#000000"
+ d="m16.822 29.784v-5.544"
+ stroke-opacity=".74510"
+ stroke="#9b9b9b"
+ stroke-linecap="round"
+ stroke-width=".93970"
+ fill="none"
+ id="path157" />
+ <path
+ stroke-linejoin="round"
+ style="color:#000000"
+ d="m17.778 24.24h-1.911v-6.468h1.911v6.468"
+ stroke-opacity=".74510"
+ stroke="#9b9b9b"
+ stroke-linecap="round"
+ stroke-width=".93970"
+ fill="none"
+ id="path159" />
+ <path
+ stroke-linejoin="round"
+ style="color:#000000"
+ d="m16.822 27.012h7.6457"
+ stroke-opacity=".74510"
+ stroke="#9b9b9b"
+ stroke-linecap="round"
+ stroke-width=".93970"
+ fill="none"
+ id="path161" />
+ <path
+ stroke-linejoin="round"
+ style="color:#000000"
+ d="m16.822 15v2.7719"
+ stroke-opacity=".74510"
+ stroke="#9b9b9b"
+ stroke-linecap="round"
+ stroke-width=".93970"
+ fill="none"
+ id="path163" />
+ <path
+ stroke-linejoin="round"
+ style="color:#000000"
+ d="m29.758 36.663h4.7786"
+ stroke-opacity=".74510"
+ stroke="#9b9b9b"
+ stroke-linecap="round"
+ stroke-width=".93970"
+ fill="none"
+ id="path165" />
+ <path
+ stroke-linejoin="round"
+ style="color:#000000"
+ d="m29.758 38.511h4.7786"
+ stroke-opacity=".74510"
+ stroke="#9b9b9b"
+ stroke-linecap="round"
+ stroke-width=".93970"
+ fill="none"
+ id="path167" />
+ <path
+ stroke-linejoin="round"
+ style="color:#000000"
+ d="m22.646 36.665h4.7786"
+ stroke-opacity=".74510"
+ stroke="#9b9b9b"
+ stroke-linecap="round"
+ stroke-width=".93970"
+ fill="none"
+ id="path169" />
+ <path
+ stroke-linejoin="round"
+ style="color:#000000"
+ d="m22.646 38.513h4.7786"
+ stroke-opacity=".74510"
+ stroke="#9b9b9b"
+ stroke-linecap="round"
+ stroke-width=".93970"
+ fill="none"
+ id="path171" />
+ <path
+ style="color:#000000"
+ d="m16.822 39.023-2.0906-2.5986-1.0752 2.2522 3.1658 0.34649z"
+ fill-opacity=".74510"
+ fill-rule="evenodd"
+ fill="#9b9b9b"
+ id="path173" />
+ <path
+ stroke-linejoin="round"
+ style="color:#000000"
+ d="m27.376 38.551 0.0283 2.4666"
+ stroke-opacity=".74510"
+ stroke="#9b9b9b"
+ stroke-linecap="round"
+ stroke-width=".92714"
+ fill="none"
+ id="path177" />
+ <g
+ transform="matrix(.84783 0 0 .86922 -52.414 -25.974)"
+ id="g179">
+ <g
+ transform="matrix(.34827 0 0 .37406 84.233 30.234)"
+ id="g181">
+ <path
+ opacity="0.08"
+ d="m12-95.031c-5.5111 0-10.031 4.5202-10.031 10.031v71c0 5.5111 4.5202 10.031 10.031 10.031h72c5.5111 0 10.031-4.5202 10.031-10.031v-71c0-5.5111-4.5202-10.031-10.031-10.031h-72z"
+ transform="scale(1,-1)"
+ fill="url(#al)"
+ id="path183" />
+ <path
+ opacity=".1"
+ d="m12-94.031c-4.9716 0-9.0312 4.0596-9.0312 9.0312v71c0 4.9716 4.0596 9.0312 9.0312 9.0312h72c4.9716 0 9.0312-4.0596 9.0312-9.0312v-71c0-4.9716-4.0596-9.0312-9.0312-9.0312h-72z"
+ transform="scale(1,-1)"
+ fill="url(#al)"
+ id="path185" />
+ <path
+ opacity=".2"
+ d="m12-93c-4.4091 0-8 3.5909-8 8v71c0 4.4091 3.5909 8 8 8h72c4.4091 0 8-3.5909 8-8v-71c0-4.4091-3.5909-8-8-8h-72z"
+ transform="scale(1,-1)"
+ fill="url(#al)"
+ id="path187" />
+ <rect
+ opacity=".3"
+ transform="scale(1,-1)"
+ rx="7"
+ ry="7"
+ height="85"
+ width="86"
+ y="-92"
+ x="5"
+ fill="url(#al)"
+ id="rect189" />
+ <rect
+ opacity=".45"
+ transform="scale(1,-1)"
+ rx="6"
+ ry="6"
+ height="84"
+ width="84"
+ y="-91"
+ x="6"
+ fill="url(#ba)"
+ id="rect191" />
+ </g>
+ <g
+ transform="matrix(.34827 0 0 .37406 84.233 30.234)"
+ id="g193">
+ <rect
+ rx="2.5647"
+ ry="2.4475"
+ height="26.107"
+ width="68.393"
+ y="10.714"
+ x="13.429"
+ fill="#bdc490"
+ id="rect195" />
+ <g
+ transform="matrix(.77679 0 0 .77679 18.259 5.3571)"
+ id="g197">
+ <path
+ opacity=".5"
+ d="m68.217 22.238-1.896 1.496-1.921-1.496v-3.0174l1.921-1.496 1.896 1.496v3.0174m-2.2204-4.7417-1.896 1.5214h-3.817l-1.896-1.5214 1.896-1.496h3.817l1.896 1.496m2.2204 11.208-1.896 1.496-1.921-1.496v-3.0174l1.921-1.496 1.896 1.496v3.0174m-8.183-6.4659-1.896 1.496-1.921-1.496v-3.017l1.921-1.496 1.896 1.496v3.0174m5.963 8.266-1.921 1.496h-3.7921l-1.921-1.496 1.921-1.5214h3.7921l1.921 1.5214m-5.9626-1.8003-1.896 1.496-1.921-1.496v-3.0174l1.921-1.496 1.896 1.496v3.0174"
+ id="path199" />
+ <path
+ opacity=".5"
+ d="m74.139 30.473-2.044 1.527l-1.8893-1.5269 1.9669-1.5528 1.9669 1.5528"
+ id="path201" />
+ </g>
+ <path
+ opacity=".3"
+ style="color:#000000"
+ fill="url(#bu)"
+ d="m17 14c-0.554 0-1 0.446-1 1v18c0 0.554 0.446 1 1 1h62c0.554 0 1-0.446 1-1v-18c0-0.554-0.446-1-1-1zm1.0312 2.0312h59.938v15.938h-59.938z"
+ id="path203" />
+ <path
+ opacity=".6"
+ style="color:#000000"
+ fill="url(#bt)"
+ d="m17 14c-0.554 0-1 0.446-1 1v18c0 0.554 0.446 1 1 1h62c0.554 0 1-0.446 1-1v-18c0-0.554-0.446-1-1-1zm0.03125 1.0312h61.938v17.938h-61.938z"
+ id="path205" />
+ </g>
+ <g
+ transform="matrix(.34827 0 0 .37406 84.233 30.234)"
+ id="g207">
+ <path
+ fill="url(#bb)"
+ d="m12 6c-3.324 0-6 2.676-6 6v72c0 3.324 2.676 6 6 6h72c3.324 0 6-2.676 6-6v-72c0-3.324-2.676-6-6-6h-72zm6 7h60c1.108 0 2 0.892 2 2v18c0 1.108-0.892 2-2 2h-60c-1.108 0-2-0.892-2-2v-18c0-1.108 0.892-2 2-2z"
+ id="path209" />
+ <path
+ opacity=".5"
+ fill="url(#az)"
+ d="m12 6c-3.324 0-6 2.676-6 6v72c0 0.33472 0.04135 0.6507 0.09375 0.96875 0.0487 0.2956 0.09704 0.59692 0.1875 0.875 0.00988 0.03038 0.020892 0.0636 0.03125 0.09375 0.098865 0.28777 0.23488 0.54745 0.375 0.8125 0.14459 0.27351 0.31562 0.53562 0.5 0.78125 0.18438 0.24564 0.37378 0.47347 0.59375 0.6875 0.43995 0.42806 0.94291 0.81453 1.5 1.0938 0.27854 0.13961 0.57347 0.24695 0.875 0.34375-0.2562-0.10022-0.48671-0.23627-0.71875-0.375-0.00741-0.0044-0.023866 0.0045-0.03125 0-0.0319-0.019-0.0622-0.042-0.0937-0.062-0.1204-0.077-0.231-0.164-0.3437-0.25-0.1062-0.081-0.2133-0.161-0.3126-0.25-0.1778-0.162-0.3473-0.346-0.4999-0.531-0.1075-0.131-0.2183-0.266-0.3124-0.407-0.0251-0.038-0.0385-0.086-0.0626-0.125-0.0647-0.103-0.1302-0.204-0.1874-0.312-0.1011-0.195-0.2057-0.416-0.2813-0.625-0.008-0.022-0.0236-0.041-0.0313-0.063-0.0318-0.092-0.0358-0.187-0.0624-0.281-0.0304-0.107-0.0704-0.203-0.0938-0.313-0.0729-0.341-0.125-0.698-0.125-1.062v-72c0-2.782 2.2182-5 5-5h72c2.7818 0 5 2.2182 5 5v72c0 0.3643-0.05212 0.72099-0.125 1.0625-0.04415 0.20689-0.08838 0.39766-0.15625 0.59375-0.0077 0.02195-0.0233 0.04069-0.03125 0.0625-0.06274 0.17374-0.13838 0.36745-0.21875 0.53125-0.04158 0.0828-0.07904 0.16995-0.125 0.25-0.0546 0.09721-0.12677 0.18835-0.1875 0.28125-0.09411 0.14096-0.20492 0.276-0.3125 0.40625-0.14317 0.17445-0.30314 0.347-0.46875 0.5-0.01117 0.0102-0.01998 0.02115-0.03125 0.03125-0.13839 0.12556-0.28509 0.23444-0.4375 0.34375-0.10257 0.07315-0.20432 0.15336-0.3125 0.21875-0.0074 0.0045-0.02384-0.0044-0.03125 0-0.23204 0.13873-0.46255 0.27478-0.71875 0.375 0.30153-0.0968 0.59646-0.20414 0.875-0.34375 0.55709-0.27922 1.0601-0.66569 1.5-1.0938 0.21997-0.21403 0.40937-0.44186 0.59375-0.6875s0.35541-0.50774 0.5-0.78125c0.14012-0.26505 0.27614-0.52473 0.375-0.8125 0.01041-0.03078 0.02133-0.06274 0.03125-0.09375 0.09046-0.27808 0.1388-0.5794 0.1875-0.875 0.053-0.318 0.094-0.634 0.094-0.969v-72c0-3.324-2.676-6-6-6h-72z"
+ id="path211" />
+ <rect
+ stroke-linejoin="round"
+ rx="2"
+ ry="2"
+ height="22"
+ width="66"
+ stroke="url(#bs)"
+ stroke-linecap="square"
+ y="13"
+ x="15"
+ stroke-width="2"
+ fill="none"
+ id="rect213" />
+ <path
+ opacity=".5"
+ fill="url(#aw)"
+ d="m12 90c-3.324 0-6-2.676-6-6v-72c0-0.33472 0.04135-0.6507 0.09375-0.96875 0.0487-0.2956 0.09704-0.59692 0.1875-0.875 0.01-0.03 0.0209-0.063 0.0313-0.094 0.0989-0.2873 0.2349-0.547 0.375-0.812 0.1446-0.2735 0.3156-0.5356 0.5-0.7812 0.1844-0.2457 0.3738-0.4735 0.5937-0.6876 0.44-0.428 0.943-0.8145 1.5-1.0937 0.2786-0.1396 0.5735-0.2469 0.8748-0.3437-0.256 0.1002-0.4865 0.2362-0.7185 0.375-0.0074 0.0044-0.02387-0.0045-0.03125 0-0.03193 0.0193-0.06229 0.04251-0.09375 0.0625-0.1204 0.0767-0.23102 0.16351-0.34375 0.25-0.10617 0.0808-0.21328 0.16111-0.3125 0.25-0.1779 0.1614-0.3474 0.3453-0.5 0.5312-0.1075 0.1303-0.2183 0.2653-0.3124 0.4063-0.0251 0.0383-0.0385 0.0858-0.0626 0.125-0.0647 0.103-0.1302 0.2045-0.1874 0.3124-0.1011 0.1948-0.2057 0.4158-0.2813 0.625-0.008 0.0219-0.0236 0.0406-0.0313 0.0626-0.0318 0.0919-0.0358 0.1868-0.0624 0.2812-0.0304 0.1066-0.0704 0.203-0.0938 0.3125-0.0729 0.3415-0.125 0.6985-0.125 1.0625v72c0 2.7818 2.2182 5 5 5h72c2.7818 0 5-2.2182 5-5v-72c0-0.364-0.052-0.721-0.125-1.0625-0.044-0.2069-0.088-0.3977-0.156-0.5937-0.008-0.022-0.024-0.0407-0.031-0.0626-0.063-0.1737-0.139-0.3674-0.219-0.5312-0.042-0.0828-0.079-0.17-0.125-0.25-0.055-0.0972-0.127-0.1884-0.188-0.2812-0.094-0.141-0.205-0.276-0.312-0.4063-0.143-0.1745-0.303-0.347-0.469-0.5-0.011-0.0102-0.02-0.0211-0.031-0.0313-0.139-0.1255-0.285-0.2344-0.438-0.3437-0.102-0.0731-0.204-0.1534-0.312-0.2187-0.0074-0.0045-0.02384 0.0044-0.03125 0-0.23204-0.13873-0.46255-0.27478-0.71875-0.375 0.30153 0.0968 0.59646 0.20414 0.875 0.34375 0.55709 0.27922 1.0601 0.66569 1.5 1.0938 0.21997 0.21403 0.40937 0.44186 0.59375 0.6875s0.35541 0.50774 0.5 0.78125c0.14012 0.26505 0.27614 0.52473 0.375 0.8125 0.01041 0.03078 0.02133 0.06274 0.03125 0.09375 0.09046 0.27808 0.1388 0.5794 0.1875 0.875 0.053 0.318 0.094 0.634 0.094 0.969v72c0 3.324-2.676 6-6 6h-72z"
+ id="path215" />
+ </g>
+ <g
+ transform="matrix(.34827 0 0 .37406 84.233 30.234)"
+ id="g217">
+ <path
+ opacity=".2"
+ d="m16.125 39.844c-0.97087 0-1.7812 0.81038-1.7812 1.7812v5.25c0 0.97087 0.81038 1.7813 1.7812 1.7812h9.75c0.97087 0 1.7812-0.81038 1.7812-1.7812v-5.25c0-0.97087-0.81038-1.7812-1.7812-1.7812h-9.75zm18 0c-0.97087 0-1.7812 0.81038-1.7812 1.7812v5.25c0 0.97087 0.81038 1.7813 1.7812 1.7812h9.75c0.97087 0 1.7812-0.81038 1.7812-1.7812v-5.25c0-0.97087-0.81038-1.7812-1.7812-1.7812h-9.75zm18 0c-0.97087 0-1.7812 0.81038-1.7812 1.7812v5.25c0 0.97087 0.81038 1.7813 1.7812 1.7812h9.75c0.97087 0 1.7812-0.81038 1.7812-1.7812v-5.25c0-0.97087-0.81038-1.7812-1.7812-1.7812h-9.75zm18 0c-0.97087 0-1.7812 0.81038-1.7812 1.7812v5.25c0 0.97087 0.81038 1.7813 1.7812 1.7812h9.75c0.97087 0 1.7812-0.81038 1.7812-1.7812v-5.25c0-0.97087-0.81038-1.7812-1.7812-1.7812h-9.75zm-54 12c-0.97087 0-1.7812 0.81038-1.7812 1.7812v5.25c0 0.97087 0.81038 1.7813 1.7812 1.7812h9.75c0.97087 0 1.7812-0.81038 1.7812-1.7812v-5.25c0-0.97087-0.81038-1.7812-1.7812-1.7812h-9.75zm18 0c-0.97087 0-1.7812 0.81038-1.7812 1.7812v5.25c0 0.97087 0.81038 1.7813 1.7812 1.7812h9.75c0.97087 0 1.7812-0.81038 1.7812-1.7812v-5.25c0-0.97087-0.81038-1.7812-1.7812-1.7812h-9.75zm18 0c-0.97087 0-1.7812 0.81038-1.7812 1.7812v5.25c0 0.97087 0.81038 1.7813 1.7812 1.7812h9.75c0.97087 0 1.7812-0.81038 1.7812-1.7812v-5.25c0-0.97087-0.81038-1.7812-1.7812-1.7812h-9.75zm18 0c-0.97087 0-1.7812 0.81038-1.7812 1.7812v5.25c0 0.97087 0.81038 1.7813 1.7812 1.7812h9.75c0.97087 0 1.7812-0.81038 1.7812-1.7812v-5.25c0-0.97087-0.81038-1.7812-1.7812-1.7812h-9.75zm-54 12c-0.97087 0-1.7812 0.81038-1.7812 1.7812v5.25c0 0.97087 0.81038 1.7813 1.7812 1.7812h9.75c0.97087 0 1.7812-0.81038 1.7812-1.7812v-5.25c0-0.97087-0.81038-1.7812-1.7812-1.7812h-9.75zm18 0c-0.97087 0-1.7812 0.81038-1.7812 1.7812v5.25c0 0.97087 0.81038 1.7813 1.7812 1.7812h9.75c0.97087 0 1.7812-0.81038 1.7812-1.7812v-5.25c0-0.97087-0.81038-1.7812-1.7812-1.7812h-9.75zm18 0c-0.97087 0-1.7812 0.81038-1.7812 1.7812v5.25c0 0.97087 0.81038 1.7813 1.7812 1.7812h9.75c0.97087 0 1.7812-0.81038 1.7812-1.7812v-5.25c0-0.97087-0.81038-1.7812-1.7812-1.7812h-9.75zm18 0c-0.97087 0-1.7812 0.81038-1.7812 1.7812v5.25c0 0.97087 0.81038 1.7813 1.7812 1.7812h9.75c0.97087 0 1.7812-0.81038 1.7812-1.7812v-5.25c0-0.97087-0.81038-1.7812-1.7812-1.7812h-9.75zm-54 12c-0.97087 0-1.7812 0.81038-1.7812 1.7812v5.25c0 0.97087 0.81038 1.7813 1.7812 1.7812h9.75c0.97087 0 1.7812-0.81038 1.7812-1.7812v-5.25c0-0.97087-0.81038-1.7812-1.7812-1.7812h-9.75zm18 0c-0.97088-0.000002-1.7812 0.81038-1.7812 1.7812v5.25c0 0.97087 0.81038 1.7812 1.7812 1.7812h9.75c0.97087 0 1.7812-0.81038 1.7812-1.7812v-5.25c-0.000002-0.97087-0.81038-1.7812-1.7812-1.7812h-9.75zm18 0c-0.97087 0-1.7812 0.81038-1.7812 1.7812v5.25c0 0.97087 0.81038 1.7813 1.7812 1.7812h9.75c0.97087 0 1.7812-0.81038 1.7812-1.7812v-5.25c0-0.97087-0.81038-1.7812-1.7812-1.7812h-9.75zm18 0c-0.97087 0-1.7812 0.81038-1.7812 1.7812v5.25c0 0.97087 0.81038 1.7813 1.7812 1.7812h9.75c0.97087 0 1.7812-0.81038 1.7812-1.7812v-5.25c0-0.97087-0.81038-1.7812-1.7812-1.7812h-9.75z"
+ id="path219" />
+ <path
+ opacity=".6"
+ d="m16.125 40.5c-0.623 0-1.125 0.502-1.125 1.125v5.25c0 0.623 0.502 1.125 1.125 1.125h9.75c0.623 0 1.125-0.502 1.125-1.125v-5.25c0-0.623-0.502-1.125-1.125-1.125h-9.75zm18 0c-0.623 0-1.125 0.502-1.125 1.125v5.25c0 0.623 0.502 1.125 1.125 1.125h9.75c0.623 0 1.125-0.502 1.125-1.125v-5.25c0-0.623-0.502-1.125-1.125-1.125h-9.75zm18 0c-0.623 0-1.125 0.502-1.125 1.125v5.25c0 0.623 0.502 1.125 1.125 1.125h9.75c0.623 0 1.125-0.502 1.125-1.125v-5.25c0-0.623-0.502-1.125-1.125-1.125h-9.75zm18 0c-0.623 0-1.125 0.502-1.125 1.125v5.25c0 0.623 0.502 1.125 1.125 1.125h9.75c0.623 0 1.125-0.502 1.125-1.125v-5.25c0-0.623-0.502-1.125-1.125-1.125h-9.75zm-54 12c-0.623 0-1.125 0.502-1.125 1.125v5.25c0 0.623 0.502 1.125 1.125 1.125h9.75c0.623 0 1.125-0.502 1.125-1.125v-5.25c0-0.623-0.502-1.125-1.125-1.125h-9.75zm18 0c-0.623 0-1.125 0.502-1.125 1.125v5.25c0 0.623 0.502 1.125 1.125 1.125h9.75c0.623 0 1.125-0.502 1.125-1.125v-5.25c0-0.623-0.502-1.125-1.125-1.125h-9.75zm18 0c-0.623 0-1.125 0.502-1.125 1.125v5.25c0 0.623 0.502 1.125 1.125 1.125h9.75c0.623 0 1.125-0.502 1.125-1.125v-5.25c0-0.623-0.502-1.125-1.125-1.125h-9.75zm18 0c-0.623 0-1.125 0.502-1.125 1.125v5.25c0 0.623 0.502 1.125 1.125 1.125h9.75c0.623 0 1.125-0.502 1.125-1.125v-5.25c0-0.623-0.502-1.125-1.125-1.125h-9.75zm-54 12c-0.623 0-1.125 0.502-1.125 1.125v5.25c0 0.623 0.502 1.125 1.125 1.125h9.75c0.623 0 1.125-0.502 1.125-1.125v-5.25c0-0.623-0.502-1.125-1.125-1.125h-9.75zm18 0c-0.623 0-1.125 0.502-1.125 1.125v5.25c0 0.623 0.502 1.125 1.125 1.125h9.75c0.623 0 1.125-0.502 1.125-1.125v-5.25c0-0.623-0.502-1.125-1.125-1.125h-9.75zm18 0c-0.623 0-1.125 0.502-1.125 1.125v5.25c0 0.623 0.502 1.125 1.125 1.125h9.75c0.623 0 1.125-0.502 1.125-1.125v-5.25c0-0.623-0.502-1.125-1.125-1.125h-9.75zm18 0c-0.623 0-1.125 0.502-1.125 1.125v5.25c0 0.623 0.502 1.125 1.125 1.125h9.75c0.623 0 1.125-0.502 1.125-1.125v-5.25c0-0.623-0.502-1.125-1.125-1.125h-9.75zm-54 12c-0.623 0-1.125 0.502-1.125 1.125v5.25c0 0.623 0.502 1.125 1.125 1.125h9.75c0.623 0 1.125-0.502 1.125-1.125v-5.25c0-0.623-0.502-1.125-1.125-1.125h-9.75zm18 0c-0.623 0-1.125 0.502-1.125 1.125v5.25c0 0.623 0.502 1.125 1.125 1.125h9.75c0.623 0 1.125-0.502 1.125-1.125v-5.25c0-0.623-0.502-1.125-1.125-1.125h-9.75zm18 0c-0.623 0-1.125 0.502-1.125 1.125v5.25c0 0.623 0.502 1.125 1.125 1.125h9.75c0.623 0 1.125-0.502 1.125-1.125v-5.25c0-0.623-0.502-1.125-1.125-1.125h-9.75zm18 0c-0.623 0-1.125 0.502-1.125 1.125v5.25c0 0.623 0.502 1.125 1.125 1.125h9.75c0.623 0 1.125-0.502 1.125-1.125v-5.25c0-0.623-0.502-1.125-1.125-1.125h-9.75z"
+ id="path221" />
+ <g
+ transform="translate(0 .25)"
+ id="g223">
+ <rect
+ rx="1.125"
+ ry="1.125"
+ height="7.5"
+ width="12"
+ y="63.75"
+ x="15"
+ fill="url(#bk)"
+ id="rect225" />
+ <path
+ fill="#fff"
+ d="m19.43 69.822v-0.68884h1.1503v-3.1674l-1.114 0.69543v-0.728l1.1635-0.75476h0.87671v3.9551h1.0646v0.68884h-3.141"
+ id="path227" />
+ <rect
+ rx="1.125"
+ ry="1.125"
+ height="7.5"
+ width="12"
+ y="63.75"
+ x="33"
+ fill="url(#bl)"
+ id="rect229" />
+ <path
+ fill="#fff"
+ d="m37.375 69.857v-0.6427c0.10547-0.23071 0.23401-0.43835 0.38562-0.62292 0.15161-0.18677 0.30981-0.35705 0.47461-0.51086 0.16699-0.15601 0.33179-0.29993 0.49438-0.43176 0.16479-0.13403 0.31201-0.26257 0.44165-0.38562 0.13183-0.12524 0.2384-0.25049 0.3197-0.37573 0.08129-0.12524 0.12194-0.25818 0.12195-0.3988-0.000007-0.20654-0.05164-0.35925-0.15491-0.45813-0.10327-0.09887-0.25488-0.14831-0.45483-0.14832-0.19776 0.000005-0.34937 0.05384-0.45483 0.1615-0.10327 0.10547-0.17029 0.26477-0.20105 0.4779l-0.93274-0.05273c0.02197-0.18237 0.06702-0.35376 0.13513-0.51416s0.16479-0.30102 0.29004-0.42188c0.12744-0.12084 0.28564-0.21533 0.47461-0.28345 0.19116-0.07031 0.41858-0.10546 0.68225-0.10547 0.25048 0.000004 0.47241 0.02967 0.66577 0.08899 0.19335 0.05713 0.35595 0.14173 0.48779 0.25378 0.13183 0.11206 0.2318 0.24939 0.29992 0.41199 0.06811 0.1604 0.10216 0.34388 0.10217 0.55042-0.000007 0.21753-0.04285 0.41419-0.12854 0.58997-0.0857 0.17578-0.19556 0.33838-0.32959 0.48779-0.13404 0.14722-0.28345 0.28565-0.44824 0.41528-0.1626 0.12964-0.3208 0.25598-0.47461 0.37903s-0.29444 0.24719-0.42188 0.37244c-0.12524 0.12525-0.21863 0.25928-0.28015 0.4021h2.1555v0.76135h-3.2498"
+ id="path231" />
+ <rect
+ rx="1.125"
+ ry="1.125"
+ height="7.5"
+ width="12"
+ y="63.75"
+ x="51"
+ fill="url(#bm)"
+ id="rect233" />
+ <path
+ fill="#fff"
+ d="m58.678 68.53c0 0.21094-0.03516 0.401-0.10547 0.57019-0.06813 0.16699-0.17139 0.30981-0.30982 0.42847-0.13842 0.11865-0.31092 0.20984-0.51746 0.27356-0.20434 0.06152-0.44274 0.09229-0.71521 0.09229-0.30322 0-0.55921-0.03845-0.76794-0.11536-0.20654-0.0769-0.37683-0.17798-0.51087-0.30322-0.13183-0.12744-0.23181-0.27136-0.29992-0.43176-0.06592-0.1604-0.10877-0.323-0.12854-0.48779l0.94263-0.0824c0.01318 0.09888 0.03735 0.19006 0.07251 0.27356 0.03735 0.0813 0.08679 0.15161 0.14831 0.21094s0.13623 0.10547 0.22412 0.13843c0.09008 0.03076 0.19556 0.04614 0.3164 0.04614 0.22192 0.000001 0.39442-0.05493 0.51746-0.1648 0.12304-0.10986 0.18457-0.27795 0.18458-0.50427-0.000008-0.13843-0.02967-0.24939-0.08899-0.33289-0.05713-0.08349-0.13073-0.14722-0.22082-0.19116-0.08789-0.04614-0.18348-0.0769-0.28674-0.09228-0.10328-0.01538-0.20105-0.02307-0.29334-0.02307h-0.324v-0.74817h0.30323c0.09228 0.000004 0.18566-0.0088 0.28015-0.02637 0.09667-0.01758 0.18347-0.04944 0.26038-0.09558 0.0769-0.04614 0.13952-0.10986 0.18787-0.19116 0.04833-0.08349 0.07251-0.18896 0.07251-0.31641 0-0.19116-0.05383-0.34057-0.1615-0.44824-0.10547-0.10986-0.26038-0.16479-0.46472-0.1648-0.19117 0.000003-0.34608 0.05274-0.46472 0.1582-0.11646 0.10547-0.18348 0.25489-0.20105 0.44824l-0.926-0.067c0.02637-0.21752 0.0857-0.40649 0.17798-0.5669 0.09449-0.1604 0.21204-0.29333 0.35267-0.3988 0.14282-0.10546 0.30652-0.18347 0.49108-0.23401 0.18458-0.05273 0.38013-0.0791 0.58668-0.0791 0.27246 0.000005 0.50647 0.03296 0.70203 0.09888 0.19554 0.06592 0.35595 0.15491 0.4812 0.26697 0.12523 0.11206 0.21752 0.2428 0.27685 0.39221 0.05932 0.14942 0.08899 0.30872 0.08899 0.4779 0 0.13623-0.01978 0.26368-0.05933 0.38232-0.03956 0.11646-0.09998 0.22193-0.18128 0.31641-0.07911 0.09229-0.18128 0.17249-0.30652 0.2406-0.12525 0.06592-0.27357 0.11536-0.44495 0.14832v0.01318c0.19116 0.02197 0.35706 0.06592 0.49768 0.13184 0.14062 0.06372 0.25598 0.14392 0.34607 0.2406 0.09008 0.09668 0.1571 0.20654 0.20105 0.32959 0.04394 0.12085 0.06592 0.24939 0.06592 0.38562"
+ id="path235" />
+ <rect
+ rx="1.125"
+ ry="1.125"
+ height="7.5"
+ width="12"
+ y="63.75"
+ x="69"
+ fill="url(#bn)"
+ id="rect237" />
+ <path
+ fill="#fff"
+ d="m75.408 67.91v1.4941h-0.828v-1.494h-1.461v-0.82h1.461v-1.494h0.828v1.4941h1.4722v0.82h-1.472"
+ id="path239" />
+ </g>
+ <g
+ transform="translate(0 .25)"
+ id="g241">
+ <rect
+ rx="1.125"
+ ry="1.125"
+ height="7.5"
+ width="12"
+ y="51.75"
+ x="15"
+ fill="url(#bg)"
+ id="rect243" />
+ <path
+ fill="#fff"
+ d="m22.188 56.876v0.94592h-0.883v-0.946h-2.113v-0.69543l1.9611-3.0026h1.0349v3.0092h0.61963v0.68884h-0.62m-0.8833-2.2083c-0.000003-0.03735-0.000003-0.08569 0-0.14502 0.0022-0.06152 0.0044-0.12414 0.0066-0.18787 0.0044-0.06372 0.0077-0.12414 0.0099-0.18127 0.0044-0.05932 0.0088-0.10546 0.01318-0.13843-0.01099 0.02417-0.02857 0.05933-0.05273 0.10547-0.02417 0.04395-0.05164 0.09229-0.0824 0.14502-0.02857 0.05054-0.05933 0.10108-0.09229 0.15161-0.03076 0.05054-0.05823 0.09449-0.0824 0.13184l-1.0778 1.6381h1.3579v-1.5194"
+ id="path245" />
+ <rect
+ rx="1.125"
+ ry="1.125"
+ height="7.5"
+ width="12"
+ y="51.75"
+ x="33"
+ fill="url(#bh)"
+ id="rect247" />
+ <path
+ fill="#fff"
+ d="m40.679 56.243c-0.000008 0.23291-0.03736 0.44824-0.11206 0.646-0.07251 0.19556-0.18128 0.36585-0.32629 0.51087-0.14503 0.14282-0.3252 0.25488-0.54053 0.33618-0.21314 0.0791-0.46033 0.11865-0.74158 0.11865-0.25928-0.000001-0.4856-0.03076-0.67895-0.09228-0.19336-0.06372-0.35706-0.15051-0.49109-0.26038-0.13184-0.11206-0.23621-0.2428-0.31311-0.39221-0.07471-0.14941-0.12634-0.30981-0.15491-0.4812l0.92615-0.07581c0.01758 0.07251 0.04175 0.14282 0.07251 0.21094 0.03296 0.06812 0.078 0.12854 0.13513 0.18127 0.05713 0.05274 0.12744 0.09558 0.21094 0.12854 0.0835 0.03076 0.18457 0.04614 0.30322 0.04614 0.23071 0.000001 0.41418-0.07361 0.55041-0.22082 0.13843-0.14722 0.20764-0.35925 0.20764-0.63611 0-0.12304-0.01758-0.23401-0.05273-0.33289-0.03296-0.09887-0.08131-0.18347-0.14502-0.25378-0.06153-0.07031-0.13844-0.12414-0.23072-0.1615-0.09009-0.03735-0.19336-0.05603-0.30981-0.05603-0.15381 0.000003-0.28015 0.02967-0.37903 0.08899-0.09888 0.05713-0.18237 0.12744-0.25049 0.21094h-0.902l0.1615-2.6136h2.7916v0.68884h-1.9512l-0.07581 1.1733c0.09228-0.0813 0.20874-0.15051 0.34936-0.20764 0.14062-0.05932 0.30432-0.08899 0.49109-0.08899 0.22851 0.000003 0.43286 0.03736 0.61304 0.11206 0.18017 0.07471 0.33288 0.17908 0.45813 0.31311 0.12524 0.13404 0.22082 0.29553 0.28674 0.4845 0.06592 0.18677 0.09887 0.39441 0.09888 0.62292"
+ id="path249" />
+ <rect
+ rx="1.125"
+ ry="1.125"
+ height="7.5"
+ width="12"
+ y="51.75"
+ x="51"
+ fill="url(#bi)"
+ id="rect251" />
+ <path
+ fill="#fff"
+ d="m58.631 56.304c-0.000008 0.23291-0.03297 0.44714-0.09888 0.6427-0.06592 0.19336-0.16479 0.36035-0.29662 0.50098-0.12964 0.13843-0.29224 0.24719-0.48779 0.3263-0.19556 0.0769-0.42188 0.11536-0.67896 0.11536-0.54712 0-0.9679-0.19116-1.2623-0.57349-0.29223-0.38452-0.43836-0.95361-0.43836-1.7073 0-0.82837 0.14832-1.4513 0.44495-1.8688 0.29882-0.41967 0.72509-0.62951 1.2788-0.62952 0.18458 0.000004 0.35267 0.01978 0.50428 0.05933 0.1516 0.03736 0.28674 0.09888 0.40539 0.18457 0.11865 0.0857 0.21972 0.19666 0.30322 0.33289 0.08348 0.13404 0.15051 0.29774 0.20105 0.49109l-0.87342 0.12195c-0.04395-0.16259-0.11536-0.27905-0.21423-0.34937-0.09888-0.07251-0.21424-0.10876-0.34607-0.10876-0.24829 0.000003-0.44385 0.12415-0.58668 0.37244-0.14062 0.2483-0.21093 0.62513-0.21093 1.1305 0.09887-0.16479 0.2362-0.29114 0.41199-0.37903 0.17578-0.08789 0.37463-0.13183 0.59656-0.13184 0.20433 0.000003 0.38891 0.03406 0.55371 0.10217 0.16698 0.06592 0.30871 0.1626 0.42517 0.29004 0.11865 0.12525 0.20983 0.27906 0.27356 0.46142 0.06372 0.18238 0.09558 0.38782 0.09558 0.61633m-0.92944 0.02637c0-0.12524-0.01428-0.2373-0.04285-0.33618-0.02857-0.10107-0.07251-0.18677-0.13183-0.25708-0.05713-0.07031-0.12854-0.12414-0.21423-0.1615-0.08569-0.03955-0.18458-0.05932-0.29663-0.05933-0.0813 0.000001-0.1626 0.01319-0.2439 0.03955-0.0813 0.02637-0.15381 0.06921-0.21753 0.12854-0.06372 0.05713-0.11536 0.13074-0.15491 0.22082-0.03955 0.09009-0.05933 0.19885-0.05933 0.32629 0 0.13184 0.01647 0.25488 0.04944 0.36914 0.03295 0.11426 0.0791 0.21424 0.13842 0.29993 0.06152 0.0835 0.13512 0.14942 0.22082 0.19775 0.08569 0.04614 0.18237 0.06921 0.29004 0.06921 0.20873 0.000001 0.37133-0.07361 0.48779-0.22082 0.11645-0.14941 0.17468-0.35486 0.17468-0.61633"
+ id="path253" />
+ <rect
+ rx="1.125"
+ ry="1.125"
+ height="7.5"
+ width="12"
+ y="51.75"
+ x="69"
+ fill="url(#bj)"
+ id="rect255" />
+ <path
+ fill="#fff"
+ d="m73.385 55.947v-0.89356h3.2294v0.894h-3.229"
+ id="path257" />
+ </g>
+ <g
+ transform="translate(0 .25)"
+ id="g259">
+ <rect
+ rx="1.125"
+ ry="1.125"
+ height="7.5"
+ width="12"
+ y="39.75"
+ x="15"
+ fill="url(#bc)"
+ id="rect261" />
+ <path
+ fill="#fff"
+ d="m22.584 41.913c-0.20874 0.32959-0.4065 0.6493-0.59326 0.95911-0.18457 0.30982-0.34607 0.62183-0.4845 0.93604-0.13843 0.31201-0.24829 0.63391-0.32959 0.9657-0.0791 0.32959-0.11865 0.67896-0.11865 1.0481h-0.9657c-0.000001-0.36694 0.04285-0.71521 0.12854-1.0448 0.08789-0.33179 0.20654-0.65478 0.35596-0.96899 0.14941-0.31421 0.323-0.62402 0.52075-0.92944 0.19775-0.30761 0.40649-0.62072 0.62622-0.93933h-2.3071v-0.76135h3.1674v0.73498"
+ id="path263" />
+ <rect
+ rx="1.125"
+ ry="1.125"
+ height="7.5"
+ width="12"
+ y="39.75"
+ x="33"
+ fill="url(#bd)"
+ id="rect265" />
+ <path
+ fill="#fff"
+ d="m40.666 44.515c0 0.19995-0.03296 0.38452-0.09888 0.55371-0.06592 0.16699-0.167 0.31201-0.30322 0.43506-0.13624 0.12085-0.30872 0.21533-0.51746 0.28345-0.20875 0.06811-0.45704 0.10217-0.74488 0.10217-0.28565 0-0.53394-0.03406-0.74487-0.10217-0.20874-0.06812-0.38232-0.1626-0.52075-0.28345-0.13623-0.12305-0.2373-0.26807-0.30322-0.43506s-0.09888-0.34936-0.09888-0.54712c-0.000001-0.16919 0.02637-0.3186 0.0791-0.44824 0.05273-0.13183 0.12305-0.24499 0.21094-0.33948 0.08789-0.09448 0.18896-0.16919 0.30322-0.22412 0.11426-0.05713 0.23181-0.09558 0.35266-0.11536v-0.01318c-0.12964-0.02856-0.2461-0.0758-0.34936-0.14172-0.10327-0.06591-0.19116-0.14502-0.26367-0.2373s-0.12854-0.19446-0.16809-0.30652c-0.03735-0.11425-0.05603-0.23291-0.05603-0.35596 0-0.18457 0.03296-0.35266 0.09888-0.50427 0.06811-0.15161 0.16699-0.28124 0.29663-0.38892 0.12964-0.10766 0.29004-0.19006 0.4812-0.24719 0.19336-0.05932 0.41638-0.08898 0.66907-0.08899 0.27026 0.000004 0.50317 0.03077 0.69873 0.09229 0.19775 0.05933 0.36035 0.14392 0.48779 0.25378 0.12744 0.10767 0.22192 0.23731 0.28345 0.38892 0.06152 0.15162 0.09229 0.31861 0.09229 0.50098 0 0.12085-0.01978 0.23841-0.05933 0.35266-0.03736 0.11206-0.09229 0.21424-0.1648 0.30652-0.07251 0.09229-0.1615 0.17029-0.26696 0.23401-0.10328 0.06372-0.22083 0.10877-0.35266 0.13513v0.01318c0.13623 0.02197 0.26258 0.06262 0.37903 0.12195 0.11865 0.05713 0.22082 0.13294 0.30652 0.22742 0.08569 0.09229 0.15271 0.20435 0.20105 0.33618 0.04834 0.12964 0.07251 0.27686 0.07251 0.44165m-1.068-2.118c0-0.09228-0.0099-0.17688-0.02966-0.25378-0.01978-0.0791-0.05273-0.14611-0.09887-0.20105-0.04615-0.05713-0.10877-0.10107-0.18787-0.13184-0.07691-0.03295-0.17469-0.04943-0.29334-0.04944-0.11426 0.000004-0.20984 0.01648-0.28674 0.04944-0.07691 0.03077-0.13843 0.07471-0.18457 0.13184-0.04614 0.05494-0.0791 0.12195-0.09888 0.20105-0.01978 0.07691-0.02966 0.1615-0.02966 0.25378-0.000002 0.0813 0.0077 0.1615 0.02307 0.2406 0.01758 0.0791 0.04834 0.15052 0.09229 0.21423 0.04394 0.06153 0.10547 0.11206 0.18457 0.15161s0.18127 0.05933 0.30652 0.05933c0.13183 0.000002 0.2373-0.01977 0.31641-0.05933 0.08129-0.03955 0.14282-0.09118 0.18457-0.15491 0.04175-0.06372 0.06921-0.13513 0.0824-0.21423s0.01978-0.1582 0.01978-0.2373m0.10876 2.0435c0-0.09448-0.01099-0.18567-0.03296-0.27356-0.01978-0.08789-0.05713-0.16479-0.11206-0.23071-0.05273-0.06811-0.12634-0.12195-0.22082-0.1615-0.09448-0.04175-0.21424-0.06262-0.35925-0.06262-0.13184 0.000003-0.2428 0.02088-0.33289 0.06262-0.08789 0.03955-0.1593 0.09339-0.21423 0.1615-0.05274 0.06812-0.09119 0.14722-0.11536 0.2373-0.02197 0.08789-0.03296 0.18128-0.03296 0.28015-0.000001 0.11646 0.01098 0.22302 0.03296 0.3197 0.02417 0.09668 0.06372 0.18018 0.11865 0.25049 0.05713 0.06812 0.13184 0.12085 0.22412 0.1582s0.20764 0.05603 0.34607 0.05603c0.13843 0.000001 0.25268-0.01868 0.34277-0.05603 0.09009-0.03955 0.1615-0.09448 0.21424-0.1648 0.05273-0.07031 0.08899-0.15381 0.10876-0.25049 0.02197-0.09887 0.03296-0.20764 0.03296-0.32629"
+ id="path267" />
+ <rect
+ rx="1.125"
+ ry="1.125"
+ height="7.5"
+ width="12"
+ y="39.75"
+ x="51"
+ fill="url(#be)"
+ id="rect269" />
+ <path
+ fill="#fff"
+ d="m58.635 43.427c0 0.82398-0.15051 1.4403-0.45153 1.849-0.30103 0.40869-0.7284 0.61304-1.2821 0.61304-0.19995 0-0.38012-0.02087-0.54052-0.06262-0.1582-0.04175-0.29663-0.10657-0.41528-0.19446-0.11866-0.08789-0.21972-0.20105-0.30322-0.33948-0.0813-0.13843-0.14611-0.30322-0.19446-0.49438l0.87012-0.12195c0.04394 0.1648 0.11535 0.28674 0.21422 0.36584 0.10108 0.0791 0.22742 0.11865 0.37903 0.11865 0.12525 0.000001 0.23731-0.02966 0.33619-0.08899 0.09887-0.05932 0.18347-0.14941 0.25378-0.27026s0.12414-0.27356 0.1615-0.45813c0.03955-0.18677 0.06042-0.40649 0.06262-0.65918-0.04394 0.0857-0.10218 0.1615-0.17468 0.22742-0.07251 0.06592-0.15491 0.12085-0.24719 0.1648-0.09009 0.04395-0.18677 0.078-0.29004 0.10217-0.10327 0.02197-0.20763 0.03296-0.31311 0.03296-0.20215 0.000002-0.38562-0.03515-0.55041-0.10547-0.1648-0.07251-0.30542-0.17578-0.42188-0.30982-0.11646-0.13403-0.20654-0.29553-0.27027-0.4845-0.06152-0.19116-0.09228-0.40649-0.09228-0.646 0-0.24609 0.03625-0.46582 0.10877-0.65918 0.0747-0.19336 0.18128-0.35595 0.3197-0.48779 0.13842-0.13403 0.30761-0.2351 0.50757-0.30322 0.20215-0.07031 0.43066-0.10546 0.68555-0.10547 0.26806 0.000004 0.50427 0.04615 0.70861 0.13843 0.20655 0.09009 0.37903 0.23072 0.51746 0.42188 0.13842 0.18897 0.2428 0.42957 0.31311 0.7218 0.07251 0.29004 0.10877 0.63501 0.10877 1.0349m-0.979-0.649c-0.000008-0.13183-0.01538-0.25488-0.04615-0.36914-0.02856-0.11645-0.07251-0.21643-0.13183-0.29993-0.05933-0.08569-0.13183-0.15271-0.21753-0.20105-0.08569-0.04834-0.18457-0.0725-0.29662-0.07251-0.10328 0.000005-0.19556 0.01978-0.27685 0.05933-0.0813 0.03736-0.15052 0.09229-0.20765 0.1648s-0.10108 0.1604-0.13183 0.26367c-0.02857 0.10328-0.04285 0.21973-0.04285 0.34937 0 0.12085 0.01428 0.23401 0.04285 0.33948 0.02856 0.10327 0.07032 0.19336 0.12524 0.27026 0.05493 0.07471 0.12415 0.13404 0.20765 0.17798 0.08348 0.04175 0.17907 0.06262 0.28673 0.06262 0.0857 0.000002 0.16919-0.01538 0.25049-0.04614 0.08349-0.03076 0.15711-0.0769 0.22082-0.13843 0.06592-0.06152 0.11865-0.13842 0.15821-0.23071 0.03954-0.09448 0.05932-0.20434 0.05932-0.32959"
+ id="path271" />
+ <rect
+ rx="1.125"
+ ry="1.125"
+ height="7.5"
+ width="12"
+ y="39.75"
+ x="69"
+ fill="url(#bf)"
+ id="rect273" />
+ <path
+ fill="#fff"
+ d="m73.154 44.944c0.16211 0.000001 0.30176-0.02246 0.41895-0.06738 0.11913-0.04687 0.22168-0.10742 0.30761-0.18164 0.08789-0.07422 0.16016-0.1582 0.21681-0.25195 0.05858-0.09375 0.10643-0.18848 0.14355-0.28418l0.75292 0.28418c-0.06054 0.14844-0.13965 0.29395-0.2373 0.43652-0.09766 0.14258-0.21973 0.26855-0.36622 0.37793-0.14648 0.10938-0.32129 0.19824-0.52441 0.2666-0.20118 0.06641-0.43848 0.09961-0.71192 0.09961-0.35742 0-0.66699-0.05273-0.92871-0.1582-0.26172-0.10742-0.47852-0.25586-0.65039-0.44531-0.16992-0.19141-0.29688-0.41797-0.38086-0.67969-0.08203-0.26172-0.12305-0.54785-0.12305-0.8584 0-0.32226 0.04102-0.61328 0.12305-0.87305 0.08398-0.25976 0.20996-0.48046 0.37793-0.66211 0.16797-0.18359 0.37988-0.32422 0.63574-0.42188 0.25781-0.09961 0.56054-0.14941 0.9082-0.14941 0.26953 0.000004 0.50488 0.02832 0.70605 0.08496 0.20312 0.05665 0.37792 0.13672 0.52442 0.24023 0.14843 0.10157 0.27051 0.22266 0.36621 0.36328 0.09765 0.13868 0.17383 0.292 0.22852 0.45996l-0.76172 0.20801c-0.02735-0.08789-0.06934-0.17187-0.12598-0.25195-0.05664-0.08203-0.12892-0.15429-0.2168-0.2168-0.08594-0.0625-0.1875-0.1123-0.30468-0.14941-0.11719-0.03906-0.25001-0.05859-0.39844-0.05859-0.20898 0.000002-0.38867 0.03418-0.53906 0.10254-0.15039 0.06641-0.27442 0.16211-0.37207 0.28711-0.09571 0.125-0.167 0.27539-0.21388 0.45117-0.04492 0.17383-0.06737 0.36914-0.06737 0.58594 0 0.21484 0.02245 0.41211 0.06737 0.5918 0.04687 0.17774 0.11914 0.33106 0.2168 0.45996 0.09766 0.12891 0.22266 0.22949 0.375 0.30176 0.15429 0.07227 0.33886 0.1084 0.55371 0.1084m2.4082 0.62109v-4.1279h3.2461v0.66797h-2.3818v1.0371h2.2031v0.66797h-2.2031v1.0869h2.502v0.66797h-3.3662"
+ id="path275" />
+ </g>
+ <g
+ transform="translate(0 .25)"
+ id="g277">
+ <rect
+ rx="1.125"
+ ry="1.125"
+ height="7.5"
+ width="12"
+ y="75.75"
+ x="15"
+ fill="url(#bo)"
+ id="rect279" />
+ <path
+ fill="#fff"
+ d="m22.605 79.5c-0.000003 0.44385-0.04065 0.81848-0.12195 1.1239-0.0813 0.30322-0.19446 0.54932-0.33948 0.73828-0.14282 0.18677-0.31311 0.3219-0.51086 0.4054-0.19556 0.0813-0.40979 0.12195-0.6427 0.12195s-0.44714-0.04065-0.6427-0.12195-0.36365-0.21533-0.50427-0.4021c-0.141-0.189-0.25-0.436-0.33-0.739-0.079-0.305-0.118-0.681-0.118-1.127 0-0.46802 0.03845-0.85473 0.11536-1.1602 0.0791-0.30761 0.19006-0.55261 0.33289-0.73498 0.14282-0.18237 0.31311-0.30981 0.51086-0.38233 0.19995-0.0747 0.42077-0.11206 0.66248-0.11206 0.22632 0 0.43616 0.03736 0.62952 0.11206 0.19555 0.07252 0.36364 0.19996 0.50427 0.38233 0.14282 0.18238 0.25378 0.42737 0.33289 0.73498 0.08129 0.30542 0.12194 0.69214 0.12195 1.1602m-0.92944 0c-0.000002-0.32739-0.01209-0.59765-0.03626-0.81079-0.02417-0.21533-0.06262-0.38562-0.11536-0.51086-0.05274-0.12744-0.12195-0.21643-0.20764-0.26697-0.0835-0.05053-0.18567-0.0758-0.30652-0.07581-0.12964 0.000008-0.23841 0.02638-0.32629 0.0791-0.08789 0.05054-0.1593 0.13953-0.21423 0.26697-0.05274 0.12745-0.09119 0.29883-0.11536 0.51416-0.02197 0.21314-0.03296 0.4812-0.03296 0.8042-0.000001 0.31861 0.01208 0.58448 0.03625 0.79761 0.02417 0.21314 0.06262 0.38452 0.11536 0.51416 0.05273 0.12744 0.12195 0.21753 0.20764 0.27026s0.19116 0.0791 0.31641 0.0791c0.12085 0 0.22412-0.02637 0.30982-0.0791 0.08569-0.05273 0.156-0.14282 0.21094-0.27026 0.05493-0.12964 0.09448-0.30103 0.11865-0.51416 0.02636-0.21314 0.03955-0.479 0.03955-0.79761"
+ id="path281" />
+ <rect
+ transform="scale(1,-1)"
+ rx="1.125"
+ ry="1.125"
+ height="7.5"
+ width="12"
+ y="-83.25"
+ x="33"
+ fill="url(#bp)"
+ id="rect283" />
+ <path
+ fill="#fff"
+ d="m38.471 81.558v-1.1169h1.0583v1.117h-1.058"
+ id="path285" />
+ <rect
+ rx="1.125"
+ ry="1.125"
+ height="7.5"
+ width="12"
+ y="75.75"
+ x="51"
+ fill="url(#bq)"
+ id="rect287" />
+ <path
+ fill="#fff"
+ d="m59.828 80.398c0 0.27466-0.03077 0.50647-0.09228 0.69544-0.06153 0.18896-0.14502 0.34167-0.25049 0.45813-0.10548 0.11426-0.22962 0.19665-0.37243 0.24719-0.14063 0.05054-0.29114 0.0758-0.45154 0.0758-0.16479 0-0.3186-0.02527-0.46142-0.0758-0.14063-0.05054-0.26368-0.13294-0.36914-0.24719-0.10328-0.11646-0.18568-0.26917-0.24719-0.45813-0.05933-0.18897-0.089-0.42078-0.08899-0.69544-0.000008-0.29004 0.02966-0.52954 0.08899-0.71851 0.06152-0.19116 0.14501-0.34277 0.25048-0.45483 0.10547-0.11426 0.22961-0.19336 0.37244-0.23731 0.14501-0.04614 0.30102-0.06921 0.46802-0.06921 0.15819 0 0.30761 0.02307 0.44823 0.06921 0.14062 0.04395 0.26258 0.12305 0.36585 0.23731 0.10547 0.11206 0.18786 0.26367 0.24719 0.45483 0.06152 0.18897 0.09228 0.42847 0.09228 0.71851m-4.0045 1.4238h-0.67895l3.0322-4.6439h0.68885l-3.0421 4.6439m-0.47461-4.6967c0.1582 0.000007 0.30762 0.02308 0.44824 0.06922 0.14062 0.04395 0.26367 0.12195 0.36914 0.23401s0.18897 0.26368 0.25048 0.45483c0.06153 0.18897 0.09229 0.42848 0.09229 0.71851 0 0.27467-0.03077 0.50648-0.09229 0.69544s-0.14612 0.34168-0.25378 0.45813c-0.10548 0.11645-0.22962 0.19995-0.37244 0.25048-0.14282 0.05054-0.29553 0.07581-0.45813 0.0758-0.16039 0.000007-0.31201-0.02526-0.45482-0.0758-0.14062-0.05054-0.26368-0.13293-0.36914-0.24719-0.10328-0.11645-0.18568-0.26916-0.2472-0.45813-0.05933-0.18896-0.08898-0.42188-0.08898-0.69873 0-0.29003 0.02966-0.52954 0.08898-0.71851 0.05932-0.19115 0.14173-0.34276 0.2472-0.45483 0.10547-0.11206 0.22961-0.19006 0.37243-0.23401 0.14502-0.04614 0.30102-0.06921 0.46802-0.06922m3.7705 3.2728c-0.000008-0.19116-0.0099-0.34936-0.02966-0.47461-0.01758-0.12524-0.04506-0.22412-0.0824-0.29663-0.03737-0.07471-0.0835-0.12634-0.13842-0.1549-0.05494-0.03077-0.11976-0.04614-0.19446-0.04615-0.08131 0.000007-0.15162 0.01538-0.21094 0.04615-0.05713 0.02857-0.10438 0.0802-0.14172 0.1549-0.03736 0.07471-0.06592 0.17468-0.08569 0.29993-0.01758 0.12525-0.02637 0.28235-0.02637 0.47132-0.000008 0.18458 0.0088 0.33838 0.02637 0.46142 0.01758 0.12086 0.04504 0.21863 0.0824 0.29334 0.03954 0.07471 0.08788 0.12744 0.14502 0.1582 0.05712 0.03077 0.12523 0.04615 0.20435 0.04615 0.0725 0 0.13622-0.01538 0.19116-0.04615 0.05712-0.03076 0.10437-0.0824 0.14172-0.1549 0.03954-0.07471 0.06921-0.17358 0.08899-0.29663 0.01977-0.12304 0.02965-0.27685 0.02966-0.46142m-3.326-1.796c0-0.18896-0.0099-0.34497-0.02967-0.46802-0.01758-0.12524-0.04504-0.22412-0.0824-0.29663-0.03735-0.0747-0.0835-0.12634-0.13842-0.1549-0.05493-0.03076-0.11976-0.04614-0.19446-0.04615-0.0813 0.000008-0.15161 0.01539-0.21093 0.04615-0.05933 0.03076-0.10767 0.0835-0.14502 0.1582-0.03736 0.07252-0.06593 0.17139-0.0857 0.29663-0.01758 0.12305-0.02637 0.27796-0.02637 0.46472 0 0.18458 0.0088 0.33839 0.02637 0.46142 0.01977 0.12305 0.04834 0.22193 0.0857 0.29663 0.03954 0.07471 0.08788 0.12854 0.14502 0.1615 0.05712 0.03077 0.12524 0.04615 0.20434 0.04615 0.0747 0 0.13952-0.01538 0.19446-0.04615 0.05712-0.03076 0.10437-0.08349 0.14172-0.1582 0.03955-0.07471 0.06812-0.17358 0.08569-0.29663 0.01978-0.12524 0.02967-0.28015 0.02967-0.46472"
+ id="path289" />
+ <rect
+ rx="1.125"
+ ry="1.125"
+ height="7.5"
+ width="12"
+ y="75.75"
+ x="69"
+ fill="url(#br)"
+ id="rect291" />
+ <path
+ fill="#fff"
+ d="m73.12 78.899v-0.81665h3.761v0.817h-3.761m0 2.0178v-0.80932h3.761v0.809h-3.761"
+ id="path293" />
+ </g>
+ </g>
+ </g>
+</svg>
diff --git a/resources/linux/mime/icons/hicolor/scalable/apps/pcbnew.svg b/resources/linux/mime/icons/hicolor/scalable/apps/pcbnew.svg
new file mode 100644
index 0000000..8001830
--- /dev/null
+++ b/resources/linux/mime/icons/hicolor/scalable/apps/pcbnew.svg
@@ -0,0 +1,1554 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ height="26"
+ width="26"
+ version="1.1"
+ id="svg2"
+ inkscape:version="0.47 r22583"
+ sodipodi:docname="icon_pcbnew.svg">
+ <metadata
+ id="metadata373">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1280"
+ inkscape:window-height="949"
+ id="namedview371"
+ showgrid="true"
+ inkscape:snap-to-guides="false"
+ inkscape:snap-grids="false"
+ inkscape:zoom="9.4280905"
+ inkscape:cx="12.473042"
+ inkscape:cy="20.702255"
+ inkscape:window-x="0"
+ inkscape:window-y="25"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg2">
+ <inkscape:grid
+ type="xygrid"
+ id="grid3350"
+ empspacing="5"
+ visible="true"
+ enabled="true"
+ snapvisiblegridlinesonly="true" />
+ </sodipodi:namedview>
+ <defs
+ id="defs4">
+ <filter
+ id="bb"
+ color-interpolation-filters="sRGB">
+ <feGaussianBlur
+ stdDeviation="2.0786429"
+ id="feGaussianBlur7" />
+ </filter>
+ <clipPath
+ id="ba">
+ <path
+ d="M 72,88 40,120 H 32 V 80 h 40 v 8 z"
+ id="path10"
+ inkscape:connector-curvature="0"
+ style="fill-rule:evenodd" />
+ </clipPath>
+ <filter
+ id="bc"
+ height="1.3839999"
+ width="1.3839999"
+ color-interpolation-filters="sRGB"
+ y="-0.192"
+ x="-0.192">
+ <feGaussianBlur
+ stdDeviation="1.9447689"
+ id="feGaussianBlur13" />
+ </filter>
+ <linearGradient
+ id="bn"
+ y2="172.44"
+ gradientUnits="userSpaceOnUse"
+ x2="153"
+ gradientTransform="translate(-143,-64)"
+ y1="175.56"
+ x1="153">
+ <stop
+ stop-color="#fff"
+ offset="0"
+ id="stop16" />
+ <stop
+ stop-color="#ddd"
+ offset="1"
+ id="stop18" />
+ </linearGradient>
+ <linearGradient
+ id="bo"
+ y2="172.89999"
+ gradientUnits="userSpaceOnUse"
+ x2="151.89999"
+ gradientTransform="translate(-143,-64)"
+ y1="175.10001"
+ x1="154.10001">
+ <stop
+ stop-color="#bbb"
+ offset="0"
+ id="stop21" />
+ <stop
+ stop-color="#616161"
+ offset="1"
+ id="stop23" />
+ </linearGradient>
+ <linearGradient
+ id="bp"
+ y2="164.44"
+ gradientUnits="userSpaceOnUse"
+ x2="153"
+ gradientTransform="translate(-143,-64)"
+ y1="167.56"
+ x1="153">
+ <stop
+ stop-color="#fff"
+ offset="0"
+ id="stop26" />
+ <stop
+ stop-color="#ddd"
+ offset="1"
+ id="stop28" />
+ </linearGradient>
+ <linearGradient
+ id="bq"
+ y2="164.89999"
+ gradientUnits="userSpaceOnUse"
+ x2="151.89999"
+ gradientTransform="translate(-143,-64)"
+ y1="167.10001"
+ x1="154.10001">
+ <stop
+ stop-color="#bbb"
+ offset="0"
+ id="stop31" />
+ <stop
+ stop-color="#616161"
+ offset="1"
+ id="stop33" />
+ </linearGradient>
+ <linearGradient
+ id="br"
+ y2="156.44"
+ gradientUnits="userSpaceOnUse"
+ x2="153"
+ gradientTransform="translate(-143,-64)"
+ y1="159.56"
+ x1="153">
+ <stop
+ stop-color="#fff"
+ offset="0"
+ id="stop36" />
+ <stop
+ stop-color="#ddd"
+ offset="1"
+ id="stop38" />
+ </linearGradient>
+ <linearGradient
+ id="bs"
+ y2="156.89999"
+ gradientUnits="userSpaceOnUse"
+ x2="151.89999"
+ gradientTransform="translate(-143,-64)"
+ y1="159.10001"
+ x1="154.10001">
+ <stop
+ stop-color="#bbb"
+ offset="0"
+ id="stop41" />
+ <stop
+ stop-color="#616161"
+ offset="1"
+ id="stop43" />
+ </linearGradient>
+ <linearGradient
+ id="bt"
+ y2="148.44"
+ gradientUnits="userSpaceOnUse"
+ x2="153"
+ gradientTransform="translate(-143,-64)"
+ y1="151.56"
+ x1="153">
+ <stop
+ stop-color="#fff"
+ offset="0"
+ id="stop46" />
+ <stop
+ stop-color="#ddd"
+ offset="1"
+ id="stop48" />
+ </linearGradient>
+ <linearGradient
+ id="bu"
+ y2="148.89999"
+ gradientUnits="userSpaceOnUse"
+ x2="151.89999"
+ gradientTransform="translate(-143,-64)"
+ y1="151.10001"
+ x1="154.10001">
+ <stop
+ stop-color="#bbb"
+ offset="0"
+ id="stop51" />
+ <stop
+ stop-color="#616161"
+ offset="1"
+ id="stop53" />
+ </linearGradient>
+ <linearGradient
+ id="bv"
+ y2="140.44"
+ gradientUnits="userSpaceOnUse"
+ x2="153"
+ gradientTransform="translate(-143,-64)"
+ y1="143.56"
+ x1="153">
+ <stop
+ stop-color="#fff"
+ offset="0"
+ id="stop56" />
+ <stop
+ stop-color="#ddd"
+ offset="1"
+ id="stop58" />
+ </linearGradient>
+ <linearGradient
+ id="bw"
+ y2="140.89999"
+ gradientUnits="userSpaceOnUse"
+ x2="151.89999"
+ gradientTransform="translate(-143,-64)"
+ y1="143.10001"
+ x1="154.10001">
+ <stop
+ stop-color="#bbb"
+ offset="0"
+ id="stop61" />
+ <stop
+ stop-color="#616161"
+ offset="1"
+ id="stop63" />
+ </linearGradient>
+ <linearGradient
+ id="bx"
+ y2="132.44"
+ gradientUnits="userSpaceOnUse"
+ x2="153"
+ gradientTransform="translate(-143,-64)"
+ y1="135.56"
+ x1="153">
+ <stop
+ stop-color="#fff"
+ offset="0"
+ id="stop66" />
+ <stop
+ stop-color="#ddd"
+ offset="1"
+ id="stop68" />
+ </linearGradient>
+ <linearGradient
+ id="by"
+ y2="132.89999"
+ gradientUnits="userSpaceOnUse"
+ x2="151.89999"
+ gradientTransform="translate(-143,-64)"
+ y1="135.10001"
+ x1="154.10001">
+ <stop
+ stop-color="#bbb"
+ offset="0"
+ id="stop71" />
+ <stop
+ stop-color="#616161"
+ offset="1"
+ id="stop73" />
+ </linearGradient>
+ <linearGradient
+ id="bz"
+ y2="124.44"
+ gradientUnits="userSpaceOnUse"
+ x2="153"
+ gradientTransform="translate(-143,-64)"
+ y1="127.56"
+ x1="153">
+ <stop
+ stop-color="#fff"
+ offset="0"
+ id="stop76" />
+ <stop
+ stop-color="#ddd"
+ offset="1"
+ id="stop78" />
+ </linearGradient>
+ <linearGradient
+ id="ca"
+ y2="124.9"
+ gradientUnits="userSpaceOnUse"
+ x2="151.89999"
+ gradientTransform="translate(-143,-64)"
+ y1="127.1"
+ x1="154.10001">
+ <stop
+ stop-color="#bbb"
+ offset="0"
+ id="stop81" />
+ <stop
+ stop-color="#616161"
+ offset="1"
+ id="stop83" />
+ </linearGradient>
+ <linearGradient
+ id="cb"
+ y2="116.44"
+ gradientUnits="userSpaceOnUse"
+ x2="153"
+ gradientTransform="translate(-143,-64)"
+ y1="119.56"
+ x1="153">
+ <stop
+ stop-color="#fff"
+ offset="0"
+ id="stop86" />
+ <stop
+ stop-color="#ddd"
+ offset="1"
+ id="stop88" />
+ </linearGradient>
+ <linearGradient
+ id="cc"
+ y2="116.9"
+ gradientUnits="userSpaceOnUse"
+ x2="151.89999"
+ gradientTransform="translate(-143,-64)"
+ y1="119.1"
+ x1="154.10001">
+ <stop
+ stop-color="#bbb"
+ offset="0"
+ id="stop91" />
+ <stop
+ stop-color="#616161"
+ offset="1"
+ id="stop93" />
+ </linearGradient>
+ <linearGradient
+ id="cd"
+ y2="108.44"
+ gradientUnits="userSpaceOnUse"
+ x2="153"
+ gradientTransform="translate(-143,-64)"
+ y1="111.56"
+ x1="153">
+ <stop
+ stop-color="#fff"
+ offset="0"
+ id="stop96" />
+ <stop
+ stop-color="#ddd"
+ offset="1"
+ id="stop98" />
+ </linearGradient>
+ <linearGradient
+ id="ce"
+ y2="108.9"
+ gradientUnits="userSpaceOnUse"
+ x2="151.89999"
+ gradientTransform="translate(-143,-64)"
+ y1="111.1"
+ x1="154.10001">
+ <stop
+ stop-color="#bbb"
+ offset="0"
+ id="stop101" />
+ <stop
+ stop-color="#616161"
+ offset="1"
+ id="stop103" />
+ </linearGradient>
+ <linearGradient
+ id="cf"
+ y2="100.44"
+ gradientUnits="userSpaceOnUse"
+ x2="153"
+ gradientTransform="translate(-143,-64)"
+ y1="103.56"
+ x1="153">
+ <stop
+ stop-color="#fff"
+ offset="0"
+ id="stop106" />
+ <stop
+ stop-color="#ddd"
+ offset="1"
+ id="stop108" />
+ </linearGradient>
+ <linearGradient
+ id="cg"
+ y2="100.9"
+ gradientUnits="userSpaceOnUse"
+ x2="151.89999"
+ gradientTransform="translate(-143,-64)"
+ y1="103.1"
+ x1="154.10001">
+ <stop
+ stop-color="#bbb"
+ offset="0"
+ id="stop111" />
+ <stop
+ stop-color="#616161"
+ offset="1"
+ id="stop113" />
+ </linearGradient>
+ <linearGradient
+ id="ch"
+ y2="92.444"
+ gradientUnits="userSpaceOnUse"
+ x2="153"
+ gradientTransform="translate(-143,-64)"
+ y1="95.556999"
+ x1="153">
+ <stop
+ stop-color="#fff"
+ offset="0"
+ id="stop116" />
+ <stop
+ stop-color="#ddd"
+ offset="1"
+ id="stop118" />
+ </linearGradient>
+ <linearGradient
+ id="at"
+ y2="92.900002"
+ gradientUnits="userSpaceOnUse"
+ x2="151.89999"
+ gradientTransform="translate(-143,-64)"
+ y1="95.100998"
+ x1="154.10001">
+ <stop
+ stop-color="#bbb"
+ offset="0"
+ id="stop121" />
+ <stop
+ stop-color="#616161"
+ offset="1"
+ id="stop123" />
+ </linearGradient>
+ <linearGradient
+ id="au"
+ y2="84.444"
+ gradientUnits="userSpaceOnUse"
+ x2="153"
+ gradientTransform="translate(-143,-64)"
+ y1="87.556999"
+ x1="153">
+ <stop
+ stop-color="#fff"
+ offset="0"
+ id="stop126" />
+ <stop
+ stop-color="#ddd"
+ offset="1"
+ id="stop128" />
+ </linearGradient>
+ <linearGradient
+ id="av"
+ y2="84.900002"
+ gradientUnits="userSpaceOnUse"
+ x2="151.89999"
+ gradientTransform="translate(-143,-64)"
+ y1="87.100998"
+ x1="154.10001">
+ <stop
+ stop-color="#bbb"
+ offset="0"
+ id="stop131" />
+ <stop
+ stop-color="#616161"
+ offset="1"
+ id="stop133" />
+ </linearGradient>
+ <linearGradient
+ id="aw"
+ y2="76.444"
+ gradientUnits="userSpaceOnUse"
+ x2="153"
+ gradientTransform="translate(-143,-64)"
+ y1="79.556999"
+ x1="153">
+ <stop
+ stop-color="#fff"
+ offset="0"
+ id="stop136" />
+ <stop
+ stop-color="#ddd"
+ offset="1"
+ id="stop138" />
+ </linearGradient>
+ <linearGradient
+ id="ax"
+ y2="76.900002"
+ gradientUnits="userSpaceOnUse"
+ x2="151.89999"
+ gradientTransform="translate(-143,-64)"
+ y1="79.100998"
+ x1="154.10001">
+ <stop
+ stop-color="#bbb"
+ offset="0"
+ id="stop141" />
+ <stop
+ stop-color="#616161"
+ offset="1"
+ id="stop143" />
+ </linearGradient>
+ <linearGradient
+ id="bd"
+ y2="99.254997"
+ gradientUnits="userSpaceOnUse"
+ x2="91.228996"
+ gradientTransform="matrix(0.21025443,0,0,0.20691162,-0.54460205,-0.4483919)"
+ y1="106.41"
+ x1="98.616997">
+ <stop
+ stop-color="#a2a2a2"
+ offset="0"
+ id="stop193" />
+ <stop
+ stop-color="#fff"
+ offset="1"
+ id="stop195" />
+ </linearGradient>
+ <radialGradient
+ id="az"
+ gradientUnits="userSpaceOnUse"
+ cy="112.3"
+ cx="102"
+ gradientTransform="matrix(0.21025443,0,0,0.20683793,-0.54460205,-0.44781288)"
+ r="139.56">
+ <stop
+ stop-color="#b7b8b9"
+ offset="0"
+ id="stop203" />
+ <stop
+ stop-color="#ececec"
+ offset=".18851"
+ id="stop205" />
+ <stop
+ stop-color="#fafafa"
+ offset=".25718"
+ id="stop207" />
+ <stop
+ stop-color="#fff"
+ offset=".30111"
+ id="stop209" />
+ <stop
+ stop-color="#fafafa"
+ offset=".53130"
+ id="stop211" />
+ <stop
+ stop-color="#ebecec"
+ offset=".84490"
+ id="stop213" />
+ <stop
+ stop-color="#e1e2e3"
+ offset="1"
+ id="stop215" />
+ </radialGradient>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#az"
+ id="radialGradient3418"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.21025443,0,0,0.20683793,-0.54460205,-0.44781288)"
+ cx="102"
+ cy="112.3"
+ r="139.56" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#bn"
+ id="linearGradient3420"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-143,-64)"
+ x1="153"
+ y1="175.56"
+ x2="153"
+ y2="172.44" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#bo"
+ id="linearGradient3422"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-143,-64)"
+ x1="154.10001"
+ y1="175.10001"
+ x2="151.89999"
+ y2="172.89999" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#bp"
+ id="linearGradient3424"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-143,-64)"
+ x1="153"
+ y1="167.56"
+ x2="153"
+ y2="164.44" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#bq"
+ id="linearGradient3426"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-143,-64)"
+ x1="154.10001"
+ y1="167.10001"
+ x2="151.89999"
+ y2="164.89999" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#br"
+ id="linearGradient3428"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-143,-64)"
+ x1="153"
+ y1="159.56"
+ x2="153"
+ y2="156.44" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#bs"
+ id="linearGradient3430"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-143,-64)"
+ x1="154.10001"
+ y1="159.10001"
+ x2="151.89999"
+ y2="156.89999" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#bt"
+ id="linearGradient3432"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-143,-64)"
+ x1="153"
+ y1="151.56"
+ x2="153"
+ y2="148.44" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#bu"
+ id="linearGradient3434"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-143,-64)"
+ x1="154.10001"
+ y1="151.10001"
+ x2="151.89999"
+ y2="148.89999" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#bv"
+ id="linearGradient3436"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-143,-64)"
+ x1="153"
+ y1="143.56"
+ x2="153"
+ y2="140.44" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#bw"
+ id="linearGradient3438"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-143,-64)"
+ x1="154.10001"
+ y1="143.10001"
+ x2="151.89999"
+ y2="140.89999" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#bx"
+ id="linearGradient3440"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-143,-64)"
+ x1="153"
+ y1="135.56"
+ x2="153"
+ y2="132.44" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#by"
+ id="linearGradient3442"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-143,-64)"
+ x1="154.10001"
+ y1="135.10001"
+ x2="151.89999"
+ y2="132.89999" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#bz"
+ id="linearGradient3444"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-143,-64)"
+ x1="153"
+ y1="127.56"
+ x2="153"
+ y2="124.44" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#ca"
+ id="linearGradient3446"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-143,-64)"
+ x1="154.10001"
+ y1="127.1"
+ x2="151.89999"
+ y2="124.9" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#cb"
+ id="linearGradient3448"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-143,-64)"
+ x1="153"
+ y1="119.56"
+ x2="153"
+ y2="116.44" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#cc"
+ id="linearGradient3450"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-143,-64)"
+ x1="154.10001"
+ y1="119.1"
+ x2="151.89999"
+ y2="116.9" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#cd"
+ id="linearGradient3452"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-143,-64)"
+ x1="153"
+ y1="111.56"
+ x2="153"
+ y2="108.44" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#ce"
+ id="linearGradient3454"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-143,-64)"
+ x1="154.10001"
+ y1="111.1"
+ x2="151.89999"
+ y2="108.9" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#cf"
+ id="linearGradient3456"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-143,-64)"
+ x1="153"
+ y1="103.56"
+ x2="153"
+ y2="100.44" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#cg"
+ id="linearGradient3458"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-143,-64)"
+ x1="154.10001"
+ y1="103.1"
+ x2="151.89999"
+ y2="100.9" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#ch"
+ id="linearGradient3460"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-143,-64)"
+ x1="153"
+ y1="95.556999"
+ x2="153"
+ y2="92.444" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#at"
+ id="linearGradient3462"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-143,-64)"
+ x1="154.10001"
+ y1="95.100998"
+ x2="151.89999"
+ y2="92.900002" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#au"
+ id="linearGradient3464"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-143,-64)"
+ x1="153"
+ y1="87.556999"
+ x2="153"
+ y2="84.444" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#av"
+ id="linearGradient3466"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-143,-64)"
+ x1="154.10001"
+ y1="87.100998"
+ x2="151.89999"
+ y2="84.900002" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#aw"
+ id="linearGradient3468"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-143,-64)"
+ x1="153"
+ y1="79.556999"
+ x2="153"
+ y2="76.444" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#ax"
+ id="linearGradient3470"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-143,-64)"
+ x1="154.10001"
+ y1="79.100998"
+ x2="151.89999"
+ y2="76.900002" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#bd"
+ id="linearGradient3472"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.21025443,0,0,0.20691162,-0.54460205,-0.4483919)"
+ x1="98.616997"
+ y1="106.41"
+ x2="91.228996"
+ y2="99.254997" />
+ <linearGradient
+ id="be-2-3"
+ y2="31.211"
+ gradientUnits="userSpaceOnUse"
+ x2="23.576"
+ gradientTransform="matrix(0.64407,-0.64045,0.66092,0.65072,61.423,86.661)"
+ y1="25.357"
+ x1="23.576">
+ <stop
+ offset="0"
+ id="stop180-9" />
+ <stop
+ stop-color="#c3c3c3"
+ offset=".13483"
+ id="stop182-1" />
+ <stop
+ stop-color="#8c8c8c"
+ offset=".20224"
+ id="stop184-9" />
+ <stop
+ stop-color="#fff"
+ offset=".26966"
+ id="stop186-6" />
+ <stop
+ stop-color="#757575"
+ offset=".4465"
+ id="stop188-5-9" />
+ <stop
+ stop-color="#7d7d7d"
+ offset=".57114"
+ id="stop190-8-3" />
+ <stop
+ stop-color="#b6b6b6"
+ offset=".72038"
+ id="stop192-3" />
+ <stop
+ offset="1"
+ id="stop194-8" />
+ </linearGradient>
+ <linearGradient
+ id="bf-6-0"
+ y2="30"
+ xlink:href="#ar-2-5"
+ gradientUnits="userSpaceOnUse"
+ x2="30.038"
+ gradientTransform="matrix(0.62586,-0.62234,0.77349,0.76155,59.298,83.616)"
+ y1="24.99"
+ x1="30.038" />
+ <linearGradient
+ id="ar-2-5">
+ <stop
+ stop-color="#3e3e3e"
+ offset="0"
+ id="stop166-8-6" />
+ <stop
+ stop-color="#828282"
+ offset=".5"
+ id="stop168-4-6" />
+ <stop
+ stop-color="#3c3c3c"
+ offset="1"
+ id="stop170-4" />
+ </linearGradient>
+ <linearGradient
+ id="bg-7-0"
+ y2="30"
+ xlink:href="#aq-0"
+ gradientUnits="userSpaceOnUse"
+ x2="30.038"
+ gradientTransform="matrix(0.62586,-0.62234,0.77349,0.76155,59.119,83.794)"
+ y1="24.99"
+ x1="30.038" />
+ <linearGradient
+ id="aq-0">
+ <stop
+ stop-color="#999"
+ offset="0"
+ id="stop173-4" />
+ <stop
+ stop-color="#fff"
+ offset=".5"
+ id="stop175-6" />
+ <stop
+ stop-color="#777"
+ offset="1"
+ id="stop177-2" />
+ </linearGradient>
+ <linearGradient
+ id="bh-2-6"
+ y2="30"
+ xlink:href="#ar-2-5"
+ gradientUnits="userSpaceOnUse"
+ x2="30.038"
+ gradientTransform="matrix(0.62586,-0.62234,0.77349,0.76155,59.921,82.996)"
+ y1="24.99"
+ x1="30.038" />
+ <linearGradient
+ id="bi-4-7"
+ y2="30"
+ xlink:href="#aq-0"
+ gradientUnits="userSpaceOnUse"
+ x2="30.038"
+ gradientTransform="matrix(0.62586,-0.62234,0.77349,0.76155,59.742,83.175)"
+ y1="24.99"
+ x1="30.038" />
+ <linearGradient
+ id="bj-0-7"
+ y2="30"
+ xlink:href="#ar-2-5"
+ gradientUnits="userSpaceOnUse"
+ x2="30.038"
+ gradientTransform="matrix(0.62586,-0.62234,0.77349,0.76155,60.547,82.374)"
+ y1="24.99"
+ x1="30.038" />
+ <linearGradient
+ id="bk-6-9"
+ y2="30"
+ xlink:href="#aq-0"
+ gradientUnits="userSpaceOnUse"
+ x2="30.038"
+ gradientTransform="matrix(0.62586,-0.62234,0.77349,0.76155,60.367,82.552)"
+ y1="24.99"
+ x1="30.038" />
+ <linearGradient
+ id="bl-2-6"
+ y2="26.030001"
+ gradientUnits="userSpaceOnUse"
+ x2="9"
+ gradientTransform="matrix(0.62586,-0.62234,0.77349,0.76155,59.06,83.852)"
+ y1="29.056999"
+ x1="9">
+ <stop
+ stop-color="#e4db7b"
+ offset="0"
+ id="stop203-9-1" />
+ <stop
+ stop-color="#f4f0c8"
+ offset="1"
+ id="stop205-9-3" />
+ </linearGradient>
+ <linearGradient
+ id="bm-0-2"
+ y2="41.391998"
+ gradientUnits="userSpaceOnUse"
+ x2="9.5221004"
+ gradientTransform="matrix(0.52586,0,0,0.51993,81.027,79.545)"
+ y1="37.372002"
+ x1="5.5179">
+ <stop
+ stop-color="#cbbd27"
+ offset="0"
+ id="stop208-1" />
+ <stop
+ stop-color="#9b901d"
+ offset="1"
+ id="stop210-5" />
+ </linearGradient>
+ </defs>
+ <g
+ id="g3352"
+ transform="matrix(1.0840142,0,0,1.0274941,-1.0097516,-0.11085364)">
+ <path
+ style="opacity:0.5;filter:url(#bb)"
+ inkscape:connector-curvature="0"
+ id="path217"
+ transform="matrix(0.21901212,0,0,0.21245446,-1.1052539,-0.69963571)"
+ d="m 16,8 v 112 h 63.187 c 3e-6,0 11.906,-9.9062 17.406,-15.406 C 102.09,99.106 112,87.2 112,87.2 V 8.012 H 16 z" />
+ <path
+ style="fill:#ffffff"
+ inkscape:connector-curvature="0"
+ id="path219"
+ d="M 2.8194588,1.20688 V 24.381003 H 16.096062 L 23.003525,17.58326 V 1.20688 H 2.8194588 z" />
+ <path
+ style="fill:url(#radialGradient3418)"
+ inkscape:connector-curvature="0"
+ id="path221"
+ d="m 3.2399477,1.4137496 c -0.1158487,0 -0.2102544,0.09287 -0.2102544,0.2068379 V 23.959337 c 0,0.114173 0.094406,0.206838 0.2102544,0.206838 H 15.681156 c 0.0553,0 0.580839,0.02444 0.619967,-0.01423 l 6.43045,-6.372949 c 0.03932,-0.03868 0.0616,-0.555495 0.0616,-0.609923 V 1.6206928 c 0,-0.1139678 -0.09419,-0.206838 -0.210255,-0.206838 H 3.2397334 z" />
+ <path
+ style="opacity:0.4;filter:url(#bc)"
+ inkscape:connector-curvature="0"
+ id="path223"
+ transform="matrix(0.21025443,0,0,0.20691162,7.8654255,-0.4483919)"
+ clip-path="url(#ba)"
+ d="M 41.88,115.98 66.19,91.67 c 0,0 -9.3531,2.9131 -19.603,2.9131 0,10.25 -4.7065,21.396 -4.7065,21.396 z" />
+ <g
+ id="g225"
+ transform="matrix(0.21025443,0,0,0.20691162,-22.411172,7.0004898)">
+ <g
+ id="g227"
+ transform="translate(116,-32)">
+ <circle
+ style="fill:url(#linearGradient3420)"
+ sodipodi:ry="2"
+ sodipodi:rx="2"
+ sodipodi:cy="110"
+ sodipodi:cx="10"
+ d="m 12,110 c 0,1.10457 -0.895431,2 -2,2 -1.1045695,0 -2,-0.89543 -2,-2 0,-1.10457 0.8954305,-2 2,-2 1.104569,0 2,0.89543 2,2 z"
+ id="circle229"
+ r="2"
+ cx="10"
+ cy="110" />
+ <circle
+ style="fill:url(#linearGradient3422)"
+ sodipodi:ry="1.556"
+ sodipodi:rx="1.556"
+ sodipodi:cy="110"
+ sodipodi:cx="10"
+ d="m 11.556,110 c 0,0.85936 -0.696645,1.556 -1.556,1.556 -0.8593551,0 -1.556,-0.69664 -1.556,-1.556 0,-0.85936 0.6966449,-1.556 1.556,-1.556 0.859355,0 1.556,0.69664 1.556,1.556 z"
+ id="circle231"
+ r="1.556"
+ cx="10"
+ cy="110" />
+ </g>
+ <g
+ id="g233"
+ transform="translate(116,-32)">
+ <circle
+ style="fill:url(#linearGradient3424)"
+ sodipodi:ry="2"
+ sodipodi:rx="2"
+ sodipodi:cy="102"
+ sodipodi:cx="10"
+ d="m 12,102 c 0,1.10457 -0.895431,2 -2,2 -1.1045695,0 -2,-0.89543 -2,-2 0,-1.10457 0.8954305,-2 2,-2 1.104569,0 2,0.89543 2,2 z"
+ id="circle235"
+ r="2"
+ cx="10"
+ cy="102" />
+ <circle
+ style="fill:url(#linearGradient3426)"
+ sodipodi:ry="1.556"
+ sodipodi:rx="1.556"
+ sodipodi:cy="102"
+ sodipodi:cx="10"
+ d="m 11.556,102 c 0,0.85936 -0.696645,1.556 -1.556,1.556 -0.8593551,0 -1.556,-0.69664 -1.556,-1.556 0,-0.85936 0.6966449,-1.556 1.556,-1.556 0.859355,0 1.556,0.69664 1.556,1.556 z"
+ id="circle237"
+ r="1.556"
+ cx="10"
+ cy="102" />
+ </g>
+ <g
+ id="g239"
+ transform="translate(116,-32)">
+ <circle
+ style="fill:url(#linearGradient3428)"
+ sodipodi:ry="2"
+ sodipodi:rx="2"
+ sodipodi:cy="94"
+ sodipodi:cx="10"
+ d="m 12,94 c 0,1.104569 -0.895431,2 -2,2 -1.1045695,0 -2,-0.895431 -2,-2 0,-1.104569 0.8954305,-2 2,-2 1.104569,0 2,0.895431 2,2 z"
+ id="circle241"
+ r="2"
+ cx="10"
+ cy="94" />
+ <circle
+ style="fill:url(#linearGradient3430)"
+ sodipodi:ry="1.556"
+ sodipodi:rx="1.556"
+ sodipodi:cy="94"
+ sodipodi:cx="10"
+ d="m 11.556,94 c 0,0.859355 -0.696645,1.556 -1.556,1.556 -0.8593551,0 -1.556,-0.696645 -1.556,-1.556 0,-0.859355 0.6966449,-1.556 1.556,-1.556 0.859355,0 1.556,0.696645 1.556,1.556 z"
+ id="circle243"
+ r="1.556"
+ cx="10"
+ cy="94" />
+ </g>
+ <g
+ id="g245"
+ transform="translate(116,-32)">
+ <circle
+ style="fill:url(#linearGradient3432)"
+ sodipodi:ry="2"
+ sodipodi:rx="2"
+ sodipodi:cy="86"
+ sodipodi:cx="10"
+ d="m 12,86 c 0,1.104569 -0.895431,2 -2,2 -1.1045695,0 -2,-0.895431 -2,-2 0,-1.104569 0.8954305,-2 2,-2 1.104569,0 2,0.895431 2,2 z"
+ id="circle247"
+ r="2"
+ cx="10"
+ cy="86" />
+ <circle
+ style="fill:url(#linearGradient3434)"
+ sodipodi:ry="1.556"
+ sodipodi:rx="1.556"
+ sodipodi:cy="86"
+ sodipodi:cx="10"
+ d="m 11.556,86 c 0,0.859355 -0.696645,1.556 -1.556,1.556 -0.8593551,0 -1.556,-0.696645 -1.556,-1.556 0,-0.859355 0.6966449,-1.556 1.556,-1.556 0.859355,0 1.556,0.696645 1.556,1.556 z"
+ id="circle249"
+ r="1.556"
+ cx="10"
+ cy="86" />
+ </g>
+ <g
+ id="g251"
+ transform="translate(116,-32)">
+ <circle
+ style="fill:url(#linearGradient3436)"
+ sodipodi:ry="2"
+ sodipodi:rx="2"
+ sodipodi:cy="78"
+ sodipodi:cx="10"
+ d="m 12,78 c 0,1.104569 -0.895431,2 -2,2 -1.1045695,0 -2,-0.895431 -2,-2 0,-1.104569 0.8954305,-2 2,-2 1.104569,0 2,0.895431 2,2 z"
+ id="circle253"
+ r="2"
+ cx="10"
+ cy="78" />
+ <circle
+ style="fill:url(#linearGradient3438)"
+ sodipodi:ry="1.556"
+ sodipodi:rx="1.556"
+ sodipodi:cy="78"
+ sodipodi:cx="10"
+ d="m 11.556,78 c 0,0.859355 -0.696645,1.556 -1.556,1.556 -0.8593551,0 -1.556,-0.696645 -1.556,-1.556 0,-0.859355 0.6966449,-1.556 1.556,-1.556 0.859355,0 1.556,0.696645 1.556,1.556 z"
+ id="circle255"
+ r="1.556"
+ cx="10"
+ cy="78" />
+ </g>
+ <g
+ id="g257"
+ transform="translate(116,-32)">
+ <circle
+ style="fill:url(#linearGradient3440)"
+ sodipodi:ry="2"
+ sodipodi:rx="2"
+ sodipodi:cy="70"
+ sodipodi:cx="10"
+ d="m 12,70 c 0,1.104569 -0.895431,2 -2,2 -1.1045695,0 -2,-0.895431 -2,-2 0,-1.104569 0.8954305,-2 2,-2 1.104569,0 2,0.895431 2,2 z"
+ id="circle259"
+ r="2"
+ cx="10"
+ cy="70" />
+ <circle
+ style="fill:url(#linearGradient3442)"
+ sodipodi:ry="1.556"
+ sodipodi:rx="1.556"
+ sodipodi:cy="70"
+ sodipodi:cx="10"
+ d="m 11.556,70 c 0,0.859355 -0.696645,1.556 -1.556,1.556 -0.8593551,0 -1.556,-0.696645 -1.556,-1.556 0,-0.859355 0.6966449,-1.556 1.556,-1.556 0.859355,0 1.556,0.696645 1.556,1.556 z"
+ id="circle261"
+ r="1.556"
+ cx="10"
+ cy="70" />
+ </g>
+ <g
+ id="g263"
+ transform="translate(116,-32)">
+ <circle
+ style="fill:url(#linearGradient3444)"
+ sodipodi:ry="2"
+ sodipodi:rx="2"
+ sodipodi:cy="62"
+ sodipodi:cx="10"
+ d="m 12,62 c 0,1.104569 -0.895431,2 -2,2 -1.1045695,0 -2,-0.895431 -2,-2 0,-1.104569 0.8954305,-2 2,-2 1.104569,0 2,0.895431 2,2 z"
+ id="circle265"
+ r="2"
+ cx="10"
+ cy="62" />
+ <circle
+ style="fill:url(#linearGradient3446)"
+ sodipodi:ry="1.556"
+ sodipodi:rx="1.556"
+ sodipodi:cy="62"
+ sodipodi:cx="10"
+ d="m 11.556,62 c 0,0.859355 -0.696645,1.556 -1.556,1.556 -0.8593551,0 -1.556,-0.696645 -1.556,-1.556 0,-0.859355 0.6966449,-1.556 1.556,-1.556 0.859355,0 1.556,0.696645 1.556,1.556 z"
+ id="circle267"
+ r="1.556"
+ cx="10"
+ cy="62" />
+ </g>
+ <g
+ id="g269"
+ transform="translate(116,-32)">
+ <circle
+ style="fill:url(#linearGradient3448)"
+ sodipodi:ry="2"
+ sodipodi:rx="2"
+ sodipodi:cy="54"
+ sodipodi:cx="10"
+ d="m 12,54 c 0,1.104569 -0.895431,2 -2,2 -1.1045695,0 -2,-0.895431 -2,-2 0,-1.104569 0.8954305,-2 2,-2 1.104569,0 2,0.895431 2,2 z"
+ id="circle271"
+ r="2"
+ cx="10"
+ cy="54" />
+ <circle
+ style="fill:url(#linearGradient3450)"
+ sodipodi:ry="1.556"
+ sodipodi:rx="1.556"
+ sodipodi:cy="54"
+ sodipodi:cx="10"
+ d="m 11.556,54 c 0,0.859355 -0.696645,1.556 -1.556,1.556 -0.8593551,0 -1.556,-0.696645 -1.556,-1.556 0,-0.859355 0.6966449,-1.556 1.556,-1.556 0.859355,0 1.556,0.696645 1.556,1.556 z"
+ id="circle273"
+ r="1.556"
+ cx="10"
+ cy="54" />
+ </g>
+ <g
+ id="g275"
+ transform="translate(116,-32)">
+ <circle
+ style="fill:url(#linearGradient3452)"
+ sodipodi:ry="2"
+ sodipodi:rx="2"
+ sodipodi:cy="46"
+ sodipodi:cx="10"
+ d="m 12,46 c 0,1.104569 -0.895431,2 -2,2 -1.1045695,0 -2,-0.895431 -2,-2 0,-1.104569 0.8954305,-2 2,-2 1.104569,0 2,0.895431 2,2 z"
+ id="circle277"
+ r="2"
+ cx="10"
+ cy="46" />
+ <circle
+ style="fill:url(#linearGradient3454)"
+ sodipodi:ry="1.556"
+ sodipodi:rx="1.556"
+ sodipodi:cy="46"
+ sodipodi:cx="10"
+ d="m 11.556,46 c 0,0.859355 -0.696645,1.556 -1.556,1.556 -0.8593551,0 -1.556,-0.696645 -1.556,-1.556 0,-0.859355 0.6966449,-1.556 1.556,-1.556 0.859355,0 1.556,0.696645 1.556,1.556 z"
+ id="circle279"
+ r="1.556"
+ cx="10"
+ cy="46" />
+ </g>
+ <g
+ id="g281"
+ transform="translate(116,-32)">
+ <circle
+ style="fill:url(#linearGradient3456)"
+ sodipodi:ry="2"
+ sodipodi:rx="2"
+ sodipodi:cy="38"
+ sodipodi:cx="10"
+ d="m 12,38 c 0,1.104569 -0.895431,2 -2,2 -1.1045695,0 -2,-0.895431 -2,-2 0,-1.104569 0.8954305,-2 2,-2 1.104569,0 2,0.895431 2,2 z"
+ id="circle283"
+ r="2"
+ cx="10"
+ cy="38" />
+ <circle
+ style="fill:url(#linearGradient3458)"
+ sodipodi:ry="1.556"
+ sodipodi:rx="1.556"
+ sodipodi:cy="38"
+ sodipodi:cx="10"
+ d="m 11.556,38 c 0,0.859355 -0.696645,1.556 -1.556,1.556 -0.8593551,0 -1.556,-0.696645 -1.556,-1.556 0,-0.859355 0.6966449,-1.556 1.556,-1.556 0.859355,0 1.556,0.696645 1.556,1.556 z"
+ id="circle285"
+ r="1.556"
+ cx="10"
+ cy="38" />
+ </g>
+ <g
+ id="g287"
+ transform="translate(116,-32)">
+ <circle
+ style="fill:url(#linearGradient3460)"
+ sodipodi:ry="2"
+ sodipodi:rx="2"
+ sodipodi:cy="30"
+ sodipodi:cx="10"
+ d="m 12,30 c 0,1.104569 -0.895431,2 -2,2 -1.1045695,0 -2,-0.895431 -2,-2 0,-1.104569 0.8954305,-2 2,-2 1.104569,0 2,0.895431 2,2 z"
+ id="circle289"
+ r="2"
+ cx="10"
+ cy="30" />
+ <circle
+ style="fill:url(#linearGradient3462)"
+ sodipodi:ry="1.556"
+ sodipodi:rx="1.556"
+ sodipodi:cy="30"
+ sodipodi:cx="10"
+ d="m 11.556,30 c 0,0.859355 -0.696645,1.556 -1.556,1.556 -0.8593551,0 -1.556,-0.696645 -1.556,-1.556 0,-0.859355 0.6966449,-1.556 1.556,-1.556 0.859355,0 1.556,0.696645 1.556,1.556 z"
+ id="circle291"
+ r="1.556"
+ cx="10"
+ cy="30" />
+ </g>
+ <g
+ id="g293"
+ transform="translate(116,-32)">
+ <circle
+ style="fill:url(#linearGradient3464)"
+ sodipodi:ry="2"
+ sodipodi:rx="2"
+ sodipodi:cy="22"
+ sodipodi:cx="10"
+ d="m 12,22 c 0,1.104569 -0.895431,2 -2,2 -1.1045695,0 -2,-0.895431 -2,-2 0,-1.104569 0.8954305,-2 2,-2 1.104569,0 2,0.895431 2,2 z"
+ id="circle295"
+ r="2"
+ cx="10"
+ cy="22" />
+ <circle
+ style="fill:url(#linearGradient3466)"
+ sodipodi:ry="1.556"
+ sodipodi:rx="1.556"
+ sodipodi:cy="22"
+ sodipodi:cx="10"
+ d="m 11.556,22 c 0,0.859355 -0.696645,1.556 -1.556,1.556 -0.8593551,0 -1.556,-0.696645 -1.556,-1.556 0,-0.859355 0.6966449,-1.556 1.556,-1.556 0.859355,0 1.556,0.696645 1.556,1.556 z"
+ id="circle297"
+ r="1.556"
+ cx="10"
+ cy="22" />
+ </g>
+ <g
+ id="g299"
+ transform="translate(116,-32)">
+ <circle
+ style="fill:url(#linearGradient3468)"
+ sodipodi:ry="2"
+ sodipodi:rx="2"
+ sodipodi:cy="14"
+ sodipodi:cx="10"
+ d="m 12,14 c 0,1.104569 -0.895431,2 -2,2 -1.1045695,0 -2,-0.895431 -2,-2 0,-1.104569 0.8954305,-2 2,-2 1.104569,0 2,0.895431 2,2 z"
+ id="circle301"
+ r="2"
+ cx="10"
+ cy="14" />
+ <circle
+ style="fill:url(#linearGradient3470)"
+ sodipodi:ry="1.556"
+ sodipodi:rx="1.556"
+ sodipodi:cy="14"
+ sodipodi:cx="10"
+ d="m 11.556,14 c 0,0.859355 -0.696645,1.556 -1.556,1.556 -0.8593551,0 -1.556,-0.696645 -1.556,-1.556 0,-0.859355 0.6966449,-1.556 1.556,-1.556 0.859355,0 1.556,0.696645 1.556,1.556 z"
+ id="circle303"
+ r="1.556"
+ cx="10"
+ cy="14" />
+ </g>
+ </g>
+ <g
+ style="opacity:0.38462004;stroke:#000000;stroke-linecap:round;stroke-linejoin:round"
+ id="g305"
+ transform="matrix(-0.59030942,0,0,-0.63192583,27.618336,27.775348)">
+ <path
+ inkscape:connector-curvature="0"
+ id="path307"
+ d="M 16.728,8.2961 20.5,12.5 v 10"
+ display="block"
+ style="color:#000000;fill:none;display:block" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path309"
+ d="m 24.5,22.5 v -12 l -3,-3"
+ display="block"
+ style="color:#000000;fill:none;display:block" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path311"
+ d="m 28.5,22.5 v -14 l -1,-1"
+ display="block"
+ style="color:#000000;fill:none;display:block" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path313"
+ d="M 32.5,22.5 V 7.5"
+ display="block"
+ style="color:#000000;fill:none;display:block" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path315"
+ display="block"
+ d="m 20.5,22.5 v 2"
+ style="color:#000000;fill:none;stroke-width:2;display:block" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path317"
+ display="block"
+ d="m 24.5,22.5 v 2"
+ style="color:#000000;fill:none;stroke-width:2;display:block" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path319"
+ display="block"
+ d="m 28.5,22.5 v 2"
+ style="color:#000000;fill:none;stroke-width:2;display:block" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path321"
+ display="block"
+ d="m 32.5,22.5 v 2"
+ style="color:#000000;fill:none;stroke-width:2;display:block" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path323"
+ display="block"
+ d="m 32.5,31.5 v 2"
+ style="color:#000000;fill:none;stroke-width:2;display:block" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path325"
+ display="block"
+ d="m 28.5,31.5 v 2"
+ style="color:#000000;fill:none;stroke-width:2;display:block" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path327"
+ display="block"
+ d="m 24.5,31.5 v 2"
+ style="color:#000000;fill:none;stroke-width:2;display:block" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path329"
+ display="block"
+ d="m 20.5,31.5 v 2"
+ style="color:#000000;fill:none;stroke-width:2;display:block" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path331"
+ d="m 20.5,33.5 v 4 l -3,3 h -7"
+ display="block"
+ style="color:#000000;fill:none;display:block" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path333"
+ d="m 24.5,33.5 v 7"
+ display="block"
+ style="color:#000000;fill:none;display:block" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path335"
+ d="m 28.5,33.5 v 4 l 3,3 h 6"
+ display="block"
+ style="color:#000000;fill:none;display:block" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path337"
+ d="m 32.5,33.5 2,2 h 3"
+ display="block"
+ style="color:#000000;fill:none;display:block" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path339"
+ d="m 10.888,13.663 c 1.6249,-1.4406 3.4028,-3.3721 3.4028,-3.3721 0.01885,0.01796 1.7133,1.7863 3.2087,3.2089 v 22 l -2,2 h -5 z"
+ display="block"
+ style="color:#000000;display:block" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path341"
+ d="m 37.5,32.5 h -1 v -12 l -1,-1 v -12 h 2 v 25 z"
+ display="block"
+ style="color:#000000;display:block" />
+ </g>
+ <path
+ style="fill:url(#linearGradient3472)"
+ inkscape:connector-curvature="0"
+ id="path345"
+ d="m 16.101745,24.380687 c 0,0 2.504939,-2.051324 3.661334,-3.189317 1.156394,-1.137993 3.240845,-3.603109 3.240845,-3.603109 0,0 -2.890986,1.82661 -5.046266,1.82661 0,2.120808 -1.856113,4.965816 -1.856113,4.965816 z" />
+ </g>
+ <g
+ transform="matrix(0.63117684,-0.08174752,0.08600797,0.59988584,-47.68986,-33.098749)"
+ id="g616-9">
+ <path
+ style="fill:url(#be-2-3);stroke:#000000;stroke-width:0.55532998;stroke-linejoin:round"
+ d="m 85.365,96.011 c 0.43282,-0.32935 1.5837,0.20459 2.6183,1.2232 1.0321,1.0162 1.5508,2.1286 1.225,2.5608 -0.0012,0.0017 0.02544,0.02217 0.02417,0.0238 l 15.708,-15.618 c 0.39874,-0.3965 -0.14267,-1.5725 -1.2101,-2.6235 -1.0674,-1.0509 -2.2586,-1.5808 -2.6574,-1.1843 l -15.705,15.618 z"
+ id="path618-9" />
+ <path
+ style="opacity:0.8;fill:#ff9de8;stroke:#dd78c5;stroke-width:0.55532998;stroke-linejoin:round"
+ d="m 99.153,82.3 c 0.43282,-0.32935 1.5837,0.20459 2.6183,1.2232 1.0321,1.0162 1.5508,2.1285 1.225,2.5608 -0.001,0.0017 0.0254,0.02217 0.0242,0.0238 l 1.9167,-1.9059 0.0391,-0.0389 c 0.001,-0.0017 -0.0254,-0.02215 -0.0242,-0.0238 0.32575,-0.43223 -0.19292,-1.5446 -1.225,-2.5608 -1.0346,-1.0186 -2.1854,-1.5525 -2.6183,-1.2232 l -0.04,0.039 -1.917,1.906 z"
+ id="path620-1" />
+ <path
+ style="opacity:0.6"
+ d="m 85.365,96.011 c 0.43282,-0.32935 1.5837,0.20459 2.6183,1.2232 1.0321,1.0162 1.5508,2.1286 1.225,2.5608 -0.0012,0.0017 0.02544,0.02217 0.02417,0.0238 l 10.816,-10.755 0.0391,-0.0389 c 0.001,-0.0017 -0.0254,-0.02216 -0.0242,-0.0238 0.32575,-0.43223 -0.19293,-1.5446 -1.225,-2.5608 -1.0346,-1.0186 -2.1854,-1.5525 -2.6183,-1.2232 l -0.03911,0.0389 -10.816,10.755 z"
+ id="path622-4" />
+ <path
+ style="fill:url(#bf-6-0)"
+ d="m 97.005,84.436 c 0.43282,-0.32935 1.5837,0.20459 2.6183,1.2232 1.0321,1.0162 1.5508,2.1286 1.225,2.5608 -10e-4,0.0017 0.0254,0.02217 0.0242,0.0238 l 0.19558,-0.19448 c 0.001,-0.0017 -0.0254,-0.02215 -0.0242,-0.0238 0.32575,-0.43222 -0.19292,-1.5446 -1.225,-2.5608 -1.0346,-1.0186 -2.1854,-1.5525 -2.6183,-1.2232 l -0.197,0.194 z"
+ id="path624-9" />
+ <path
+ style="fill:url(#bg-7-0)"
+ d="m 96.826,84.614 c 0.43282,-0.32935 1.5837,0.20459 2.6183,1.2232 1.0321,1.0162 1.5508,2.1285 1.225,2.5608 -0.001,0.0017 0.0254,0.02217 0.0242,0.0238 l 0.19558,-0.19448 c 10e-4,-0.0017 -0.0254,-0.02215 -0.0242,-0.0238 0.32575,-0.43222 -0.19292,-1.5446 -1.225,-2.5608 -1.0346,-1.0186 -2.1854,-1.5525 -2.6183,-1.2232 l -0.196,0.194 z"
+ id="path626-1" />
+ <path
+ style="fill:url(#bh-2-6)"
+ d="m 97.628,83.817 c 0.43282,-0.32935 1.5837,0.20459 2.6183,1.2232 1.0321,1.0162 1.5508,2.1286 1.225,2.5608 -0.001,0.0017 0.0254,0.02217 0.0242,0.0238 l 0.19558,-0.19448 c 0.001,-0.0017 -0.0254,-0.02215 -0.0242,-0.0238 0.32575,-0.43222 -0.19292,-1.5446 -1.225,-2.5608 -1.0346,-1.0186 -2.1854,-1.5525 -2.6183,-1.2232 l -0.204,0.194 z"
+ id="path628-0" />
+ <path
+ style="fill:url(#bi-4-7)"
+ d="m 97.448,83.995 c 0.43282,-0.32935 1.5837,0.20459 2.6183,1.2232 1.0321,1.0162 1.5508,2.1286 1.225,2.5608 -0.001,0.0017 0.0254,0.02217 0.0242,0.0238 l 0.19558,-0.19448 c 10e-4,-0.0017 -0.0254,-0.02215 -0.0242,-0.0238 0.32575,-0.43222 -0.19292,-1.5446 -1.225,-2.5608 -1.0346,-1.0186 -2.1854,-1.5525 -2.6183,-1.2232 l -0.204,0.194 z"
+ id="path630-7" />
+ <path
+ style="fill:url(#bj-0-7)"
+ d="m 98.254,83.194 c 0.43282,-0.32935 1.5837,0.20459 2.6183,1.2232 1.0321,1.0162 1.5508,2.1286 1.225,2.5608 -0.001,0.0017 0.0254,0.02217 0.0242,0.0238 l 0.19558,-0.19448 c 10e-4,-0.0017 -0.0254,-0.02216 -0.0242,-0.0238 0.32575,-0.43223 -0.19292,-1.5446 -1.225,-2.5608 -1.0346,-1.0186 -2.1854,-1.5525 -2.6183,-1.2232 l -0.198,0.194 z"
+ id="path632-5" />
+ <path
+ style="fill:url(#bk-6-9)"
+ d="m 98.074,83.372 c 0.43282,-0.32935 1.5837,0.20459 2.6183,1.2232 1.0321,1.0162 1.5508,2.1286 1.225,2.5608 -10e-4,0.0017 0.0254,0.02217 0.0242,0.0238 l 0.19558,-0.19448 c 0.001,-0.0017 -0.0254,-0.02216 -0.0242,-0.0238 0.32575,-0.43223 -0.19292,-1.5446 -1.225,-2.5608 -1.0346,-1.0186 -2.1854,-1.5525 -2.6183,-1.2232 l -0.198,0.194 z"
+ id="path634-8" />
+ <path
+ style="fill:url(#bl-2-6);fill-rule:evenodd;stroke:url(#bm-0-2);stroke-width:0.55532998"
+ d="m 82.185,102.91 7.0294,-3.0074 c 0.32575,-0.43222 -0.20033,-1.6548 -1.2324,-2.671 -1.0346,-1.0186 -2.2398,-1.5494 -2.6726,-1.22 l -3.124,6.898 z"
+ id="path636-7" />
+ <path
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.55532998"
+ d="m 83.046,101.04 -0.86146,1.8612 1.9008,-0.83782 c -0.14196,-0.17111 -0.2723,-0.3455 -0.44432,-0.51486 -0.19804,-0.19498 -0.39436,-0.35167 -0.59506,-0.50847 z"
+ id="path638-0" />
+ </g>
+</svg>
diff --git a/resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-3d-project.svg b/resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-3d-project.svg
new file mode 100644
index 0000000..1d4f04c
--- /dev/null
+++ b/resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-3d-project.svg
@@ -0,0 +1,32 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs>
+ <linearGradient id="a">
+ <stop stop-color="#fbffff" stop-opacity="0" offset="0"/>
+ <stop stop-color="#fff" offset="1"/>
+ </linearGradient>
+ <linearGradient id="f" y2="16.549" xlink:href="#a" gradientUnits="userSpaceOnUse" x2="110.17" gradientTransform="matrix(.18304 0 0 .35849 2.8651 3.4851)" y1="79.338" x1="157.21"/>
+ <linearGradient id="g" y2="11.503" xlink:href="#a" gradientUnits="userSpaceOnUse" x2="22.394" gradientTransform="matrix(.21564 0 0 .30429 2.8651 3.4851)" y1="74.459" x1="78.703"/>
+ <filter id="e" color-interpolation-filters="sRGB">
+ <feGaussianBlur stdDeviation="2.6423077"/>
+ </filter>
+ </defs>
+ <path opacity=".29297" d="m84.5 33 69.5-9 12-13.5 3.5-57.5-149 56z" transform="matrix(.31556 0 0 .33013 -2.7527 33.927)" filter="url(#e)"/>
+ <path stroke-linejoin="round" d="m23.485 5.6161-18.67 4.9429 0.0392 25.808h0.0078v0.04909l0.070485-0.01638 18.505 7.1761 17.385-7.5033v-25.513l-17.338-4.9424z" stroke-opacity=".5" stroke="#000" stroke-width="3.202" fill="none"/>
+ <path stroke-linejoin="round" d="m40.8 10.563v24.014l-16.778-3.856-0.562-25.097 17.34 4.939z" fill-rule="evenodd" stroke="#000" stroke-linecap="round" stroke-width="1.9265" fill="#515151"/>
+ <path stroke-linejoin="round" d="m4.7875 10.563 18.674-4.938 1.195 25.952-19.82 4.84l-0.05-25.854z" fill-rule="evenodd" stroke="#000" stroke-linecap="round" stroke-width="1.9265" fill="#4a494d"/>
+ <path stroke-linejoin="round" d="m23.415 16.567 17.385-6.004-17.339-4.938-18.674 4.938 18.628 6.004z" fill-rule="evenodd" stroke="#000" stroke-linecap="round" stroke-width="1.9265" fill="#9db8d2"/>
+ <path stroke-linejoin="round" d="m40.8 10.563v25.515l-17.386 7.504v-27.017l17.386-6.003z" fill-rule="evenodd" stroke="#000" stroke-linecap="round" stroke-width="1.9265" fill="#4b6983"/>
+ <path stroke-linejoin="round" d="m23.415 16.567v27.016l-18.589-7.214-0.0384-25.806 18.627 6.004z" fill-rule="evenodd" stroke="#000" stroke-linecap="round" stroke-width="1.9265" fill="#7590ae"/>
+ <path fill-rule="evenodd" fill="url(#g)" d="m23.392 6.6501-17.585 4.6919v24.002c18.303 7.503 21.351 12.426 16.673-19.471 16.577-6.3815 0.912-9.2226 0.912-9.2226z"/>
+ <path d="m24.39 17.326v24.541c0.06887 0 7.3698-2.8787 10.745-6.4769 3.4437-2.9506 4.6147-8.9239 4.6147-8.9239v-14.682l-15.36 5.5416z" fill-opacity=".75" fill-rule="evenodd" fill="url(#f)"/>
+ <path stroke-linejoin="round" d="m23.415 16.567v27.016l-18.589-7.214-0.0384-25.806 18.627 6.004z" stroke="#000" stroke-linecap="round" stroke-width="1.9265" fill="none"/>
+ <g transform="matrix(.27133 .094049 .0019799 .4195 36.941 31.118)">
+ <rect fill-rule="evenodd" rx="2.825" ry="7.866" height="46.063" width="38.976" stroke="#000" y="-13.622" x="-102.76" stroke-width="2.2011" fill="#fff"/>
+ <text style="word-spacing:0px;letter-spacing:0px" xml:space="preserve" font-size="40px" line-height="125%" y="23.416523" x="-94.562927" font-family="Bitstream Vera Sans" fill="#000000"><tspan y="23.416523" x="-94.562927" font-weight="bold" fill="#f80d0d">3</tspan></text>
+ </g>
+ <g transform="matrix(.36014 .11661 .0026279 .52013 13.318 26.471)">
+ <rect transform="matrix(.89114 -.45373 -.028839 .99958 0 0)" fill-rule="evenodd" rx="2.6473" ry="6.2048" height="36.335" width="36.525" stroke="#000" y="-2.7531" x="41.151" stroke-width="1.8925" fill="#fff"/>
+ <text style="word-spacing:0px;letter-spacing:0px" xml:space="preserve" transform="matrix(1.0067 -.60333 -.017083 1.0035 0 0)" line-height="125%" font-size="31.689px" y="31.395872" x="39.189945" font-family="Bitstream Vera Sans" fill="#000000"><tspan y="31.395872" x="39.189945" font-weight="bold" fill="#f80d0d">D</tspan></text>
+ </g>
+ <text style="word-spacing:0px;letter-spacing:0px" xml:space="preserve" font-size="40px" line-height="125%" y="34.097565" x="-6.9512191" font-family="Bitstream Vera Sans" fill="#000000"><tspan/></text>
+</svg>
diff --git a/resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-bitmap2component-project.svg b/resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-bitmap2component-project.svg
new file mode 100644
index 0000000..fe6cb4d
--- /dev/null
+++ b/resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-bitmap2component-project.svg
@@ -0,0 +1,1125 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ height="48"
+ width="48"
+ version="1.1"
+ id="svg2"
+ inkscape:version="0.47 r22583"
+ sodipodi:docname="icon_bitmap2component.svg">
+ <metadata
+ id="metadata347">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1280"
+ inkscape:window-height="949"
+ id="namedview345"
+ showgrid="false"
+ inkscape:zoom="4.9166667"
+ inkscape:cx="24"
+ inkscape:cy="23.59322"
+ inkscape:window-x="0"
+ inkscape:window-y="25"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg2" />
+ <defs
+ id="defs4">
+ <inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 24 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="48 : 24 : 1"
+ inkscape:persp3d-origin="24 : 16 : 1"
+ id="perspective349" />
+ <linearGradient
+ id="aw">
+ <stop
+ offset="0"
+ id="stop7" />
+ <stop
+ stop-opacity="0"
+ offset="1"
+ id="stop9" />
+ </linearGradient>
+ <linearGradient
+ id="au">
+ <stop
+ stop-color="#555753"
+ stop-opacity="0"
+ offset="0"
+ id="stop12" />
+ <stop
+ stop-color="#eeeeec"
+ offset="1"
+ id="stop14" />
+ </linearGradient>
+ <linearGradient
+ id="ay">
+ <stop
+ stop-color="#2e3436"
+ offset="0"
+ id="stop17" />
+ <stop
+ stop-color="#555753"
+ stop-opacity="0"
+ offset="1"
+ id="stop19" />
+ </linearGradient>
+ <linearGradient
+ id="az">
+ <stop
+ stop-color="#eeeeec"
+ offset="0"
+ id="stop22" />
+ <stop
+ stop-color="#fff"
+ stop-opacity="0"
+ offset="1"
+ id="stop24" />
+ </linearGradient>
+ <linearGradient
+ id="ax">
+ <stop
+ stop-color="#888a85"
+ offset="0"
+ id="stop27" />
+ <stop
+ stop-color="#fff"
+ offset="1"
+ id="stop29" />
+ </linearGradient>
+ <filter
+ id="ck"
+ height="1.6824"
+ width="1.6824"
+ color-interpolation-filters="sRGB"
+ y="-.34118"
+ x="-.34118">
+ <feGaussianBlur
+ stdDeviation="0.28648224"
+ id="feGaussianBlur32" />
+ </filter>
+ <linearGradient
+ id="at">
+ <stop
+ stop-color="#dee3e0"
+ offset="0"
+ id="stop35" />
+ <stop
+ stop-color="#dee3e0"
+ stop-opacity="0"
+ offset="1"
+ id="stop37" />
+ </linearGradient>
+ <radialGradient
+ id="ba"
+ xlink:href="#aw"
+ gradientUnits="userSpaceOnUse"
+ cy="486.65"
+ cx="605.71"
+ gradientTransform="matrix(-.062854 0 0 .020588 46.705 34.451)"
+ r="117.14" />
+ <linearGradient
+ id="av"
+ y2="609.51"
+ gradientUnits="userSpaceOnUse"
+ x2="302.86"
+ gradientTransform="matrix(.062854 0 0 .020588 1.2826 34.451)"
+ y1="366.65"
+ x1="302.86">
+ <stop
+ stop-opacity="0"
+ offset="0"
+ id="stop41" />
+ <stop
+ offset=".5"
+ id="stop43" />
+ <stop
+ stop-opacity="0"
+ offset="1"
+ id="stop45" />
+ </linearGradient>
+ <radialGradient
+ id="bb"
+ xlink:href="#aw"
+ gradientUnits="userSpaceOnUse"
+ cy="486.65"
+ cx="605.71"
+ gradientTransform="matrix(.062854 0 0 .020588 1.295 34.451)"
+ r="117.14" />
+ <radialGradient
+ id="bl"
+ gradientUnits="userSpaceOnUse"
+ cy="188.5"
+ cx="171.25"
+ gradientTransform="matrix(.23274 0 0 .23274 -13.152 -9.0643)"
+ r="19">
+ <stop
+ stop-color="#b100cb"
+ offset="0"
+ id="stop49" />
+ <stop
+ stop-color="#204a87"
+ stop-opacity="0"
+ offset="1"
+ id="stop51" />
+ </radialGradient>
+ <linearGradient
+ id="cl"
+ y2="53.914"
+ gradientUnits="userSpaceOnUse"
+ x2="11.692"
+ gradientTransform="matrix(.97498 0 0 .9583 -.89967 3.1462)"
+ y1="20.521"
+ x1="10.666">
+ <stop
+ stop-color="#fff"
+ offset="0"
+ id="stop54" />
+ <stop
+ stop-color="#fff"
+ stop-opacity="0"
+ offset="1"
+ id="stop56" />
+ </linearGradient>
+ <radialGradient
+ id="bm"
+ xlink:href="#at"
+ gradientUnits="userSpaceOnUse"
+ cy="12.007"
+ cx="14.739"
+ gradientTransform="matrix(-3.3976e-6,-2.2552,3.3832,-5.402e-6,-27.32,54.059)"
+ r=".54688" />
+ <radialGradient
+ id="bn"
+ gradientUnits="userSpaceOnUse"
+ cy="17.109"
+ cx="9.2366"
+ gradientTransform="matrix(1.2675 -4.6716e-7 1.8899e-7 .44533 1.7922 12.443)"
+ r="2.961">
+ <stop
+ stop-color="#e9e9e9"
+ offset="0"
+ id="stop60" />
+ <stop
+ stop-color="#a7a7a7"
+ offset="0"
+ id="stop62" />
+ <stop
+ stop-color="#bebebe"
+ offset="0.529"
+ id="stop64" />
+ <stop
+ stop-color="#e7e7e7"
+ offset="1"
+ id="stop66" />
+ </radialGradient>
+ <radialGradient
+ id="bo"
+ xlink:href="#at"
+ gradientUnits="userSpaceOnUse"
+ cy="20.823"
+ cx="9.119"
+ gradientTransform="matrix(0 -.60512 2.7541 0 -44.001 27.997)"
+ r="3.177" />
+ <linearGradient
+ id="bc"
+ y2="30.191"
+ gradientUnits="userSpaceOnUse"
+ x2="6.5596"
+ gradientTransform="matrix(1.3054 0 0 .96884 27.658 -4.2992)"
+ y1="28.781"
+ x1="6.5596">
+ <stop
+ stop-color="#fff"
+ offset="0"
+ id="stop70" />
+ <stop
+ stop-color="#d7dbc7"
+ offset="1"
+ id="stop72" />
+ </linearGradient>
+ <linearGradient
+ id="bd"
+ y2="22.713"
+ gradientUnits="userSpaceOnUse"
+ x2="8.5602"
+ gradientTransform="matrix(1.1646 0 0 .37791 27.782 13.975)"
+ y1="29.18"
+ x1="5.3348">
+ <stop
+ stop-color="#eee"
+ offset="0"
+ id="stop75" />
+ <stop
+ stop-color="#a2a2a2"
+ offset="1"
+ id="stop77" />
+ </linearGradient>
+ <linearGradient
+ id="be"
+ y2="24.938"
+ xlink:href="#ax"
+ gradientUnits="userSpaceOnUse"
+ x2="24"
+ gradientTransform="matrix(.67368 0 0 .67368 6.6323 9.8582)"
+ y1="22.125"
+ x1="22.062" />
+ <linearGradient
+ id="bf"
+ y2="26.868"
+ xlink:href="#ax"
+ gradientUnits="userSpaceOnUse"
+ x2="24.082"
+ gradientTransform="matrix(1.0732 0 0 1.0757 2.6528 -.50307)"
+ y1="21.016"
+ x1="21.568" />
+ <radialGradient
+ id="bp"
+ gradientUnits="userSpaceOnUse"
+ cy="62.526"
+ cx="442.29"
+ gradientTransform="matrix(-1.3017e-5,-1.3896,0.25862,0,12.335,648.41)"
+ r="77.923">
+ <stop
+ stop-color="#777"
+ stop-opacity="0"
+ offset="0"
+ id="stop82" />
+ <stop
+ stop-color="#2b2b2b"
+ offset="1"
+ id="stop84" />
+ </radialGradient>
+ <radialGradient
+ id="bq"
+ xlink:href="#az"
+ gradientUnits="userSpaceOnUse"
+ cy="183.64"
+ cx="258.76"
+ gradientTransform="matrix(.17369 -.0023476 .0017845 .13208 -14.213 13.883)"
+ r="18.578" />
+ <radialGradient
+ id="br"
+ gradientUnits="userSpaceOnUse"
+ cy="171.79"
+ cx="251.69"
+ gradientTransform="matrix(.040643 -.16438 .25253 .063961 -21.612 69.314)"
+ r="21.531">
+ <stop
+ stop-color="#eeeeec"
+ offset="0"
+ id="stop88" />
+ <stop
+ stop-color="#090908"
+ stop-opacity=".96622"
+ offset="1"
+ id="stop90" />
+ </radialGradient>
+ <linearGradient
+ id="bg"
+ y2="179.04"
+ xlink:href="#az"
+ gradientUnits="userSpaceOnUse"
+ x2="222.73"
+ gradientTransform="matrix(.13883 0 0 .13367 -4.9888 13.067)"
+ y1="171.62"
+ x1="236.75" />
+ <radialGradient
+ id="bs"
+ gradientUnits="userSpaceOnUse"
+ cy="111.3"
+ cx="439.05"
+ gradientTransform="matrix(.071034 -.056703 .11549 .15363 -18.07 41.978)"
+ r="75.752">
+ <stop
+ stop-color="#eeeeec"
+ offset="0"
+ id="stop94" />
+ <stop
+ stop-color="#babdb6"
+ offset="1"
+ id="stop96" />
+ </radialGradient>
+ <linearGradient
+ id="bh"
+ y2="114.23"
+ gradientUnits="userSpaceOnUse"
+ x2="457.2"
+ gradientTransform="matrix(.072459 0 0 .10109 -4.6279 19.41)"
+ y1="289.78"
+ x1="457.2">
+ <stop
+ stop-color="#2e3436"
+ offset="0"
+ id="stop99" />
+ <stop
+ stop-color="#fff"
+ offset="1"
+ id="stop101" />
+ </linearGradient>
+ <radialGradient
+ id="bt"
+ xlink:href="#ay"
+ gradientUnits="userSpaceOnUse"
+ cy="98.975"
+ cx="434.13"
+ gradientTransform="matrix(-.15022 .15417 -.1987 -.20393 113.38 -16.437)"
+ r="74.908" />
+ <radialGradient
+ id="bu"
+ gradientUnits="userSpaceOnUse"
+ cy="130.89"
+ cx="441.36"
+ gradientTransform="matrix(-.012867 -.088952 .29168 -.044437 -3.9993 74.887)"
+ r="75.756">
+ <stop
+ stop-color="#3a3a3a"
+ offset="0"
+ id="stop105" />
+ <stop
+ stop-color="#3a3a3a"
+ stop-opacity="0"
+ offset="1"
+ id="stop107" />
+ </radialGradient>
+ <radialGradient
+ id="bv"
+ xlink:href="#ay"
+ gradientUnits="userSpaceOnUse"
+ cy="131.83"
+ cx="469.91"
+ gradientTransform="matrix(-.14051 -.13746 .12306 -.13043 81.917 108.28)"
+ r="74.908" />
+ <radialGradient
+ id="bw"
+ xlink:href="#au"
+ gradientUnits="userSpaceOnUse"
+ cy="218.66"
+ cx="344.26"
+ gradientTransform="matrix(-.013724 -.24747 .093642 -.0055498 12.749 122.21)"
+ r="74.908" />
+ <linearGradient
+ id="bi"
+ y2="225.83"
+ xlink:href="#au"
+ gradientUnits="userSpaceOnUse"
+ x2="280.02"
+ gradientTransform="matrix(.093448 0 0 .13038 -14.224 11.368)"
+ y1="126.84"
+ x1="469.36" />
+ <radialGradient
+ id="bx"
+ gradientUnits="userSpaceOnUse"
+ cy="95.382"
+ cx="529.4"
+ gradientTransform="matrix(0 .10037 -.10096 0 38.13 -18.815)"
+ r="74.908">
+ <stop
+ stop-color="#2e3436"
+ stop-opacity="0"
+ offset="0"
+ id="stop113" />
+ <stop
+ stop-color="#2e3436"
+ stop-opacity="0"
+ offset=".47336"
+ id="stop115" />
+ <stop
+ stop-color="#0f1112"
+ offset="1"
+ id="stop117" />
+ </radialGradient>
+ <radialGradient
+ id="by"
+ gradientUnits="userSpaceOnUse"
+ cy="165.52"
+ cx="449.88"
+ gradientTransform="matrix(0 -.096503 .10561 0 11.021 76.299)"
+ r="74.908">
+ <stop
+ stop-color="#eeeeec"
+ stop-opacity="0"
+ offset="0"
+ id="stop120" />
+ <stop
+ stop-color="#eeeeec"
+ stop-opacity=".49804"
+ offset=".86670"
+ id="stop122" />
+ <stop
+ stop-color="#eeeeec"
+ stop-opacity="0"
+ offset="1"
+ id="stop124" />
+ </radialGradient>
+ <radialGradient
+ id="bz"
+ gradientUnits="userSpaceOnUse"
+ cy="170.41"
+ cx="459.45"
+ gradientTransform="matrix(.15094 -2.3254e-8 1.3013e-8 .12469 -40.912 13.001)"
+ r="74.908">
+ <stop
+ stop-color="#eeeeec"
+ stop-opacity=".95270"
+ offset="0"
+ id="stop127" />
+ <stop
+ stop-color="#eeeeec"
+ stop-opacity="0"
+ offset=".47989"
+ id="stop129" />
+ <stop
+ stop-color="#eeeeec"
+ stop-opacity=".99324"
+ offset=".52296"
+ id="stop131" />
+ <stop
+ stop-color="#eeeeec"
+ stop-opacity="0"
+ offset=".63154"
+ id="stop133" />
+ <stop
+ stop-color="#eeeeec"
+ stop-opacity=".23529"
+ offset=".73835"
+ id="stop135" />
+ <stop
+ stop-color="#fff"
+ stop-opacity=".71622"
+ offset=".83401"
+ id="stop137" />
+ <stop
+ stop-color="#f6f6f5"
+ stop-opacity=".27027"
+ offset=".90514"
+ id="stop139" />
+ <stop
+ stop-color="#f2f2f0"
+ stop-opacity=".27703"
+ offset=".90514"
+ id="stop141" />
+ <stop
+ stop-color="#eeeeec"
+ stop-opacity="0"
+ offset="1"
+ id="stop143" />
+ </radialGradient>
+ <radialGradient
+ id="ca"
+ gradientUnits="userSpaceOnUse"
+ cy="217.46"
+ cx="502.53"
+ gradientTransform="matrix(.10492 -.072831 .035871 .057009 -25.672 64.354)"
+ r="74.908">
+ <stop
+ stop-color="#eeeeec"
+ offset="0"
+ id="stop146" />
+ <stop
+ stop-color="#eeeeec"
+ stop-opacity="0"
+ offset="1"
+ id="stop148" />
+ </radialGradient>
+ <radialGradient
+ id="cb"
+ gradientUnits="userSpaceOnUse"
+ cy="33.605"
+ cx="427.8"
+ gradientTransform="matrix(-.44827 .10225 -.081708 -.35819 230.04 -5.144)"
+ r="74.908">
+ <stop
+ stop-color="#eeeeec"
+ offset="0"
+ id="stop151" />
+ <stop
+ stop-color="#eeeeec"
+ stop-opacity="0"
+ offset="1"
+ id="stop153" />
+ </radialGradient>
+ <radialGradient
+ id="cc"
+ gradientUnits="userSpaceOnUse"
+ cy="127.25"
+ cx="399.88"
+ gradientTransform="matrix(.942 -.093118 .12194 .43761 -363.99 7.6542)"
+ r="2.0222">
+ <stop
+ stop-color="#fff"
+ offset="0"
+ id="stop156" />
+ <stop
+ stop-color="#fff"
+ stop-opacity="0"
+ offset="1"
+ id="stop158" />
+ </radialGradient>
+ <radialGradient
+ id="cd"
+ gradientUnits="userSpaceOnUse"
+ cy="127.65"
+ cx="400.3"
+ gradientTransform="matrix(1.0867,3.1905e-5,-1.4474e-5,0.51097,-406.97,-39.597)"
+ r="2.0222">
+ <stop
+ stop-color="#fff"
+ offset="0"
+ id="stop161" />
+ <stop
+ stop-color="#eeeeec"
+ stop-opacity="0"
+ offset="1"
+ id="stop163" />
+ </radialGradient>
+ <linearGradient
+ id="bj"
+ y2="496.34"
+ gradientUnits="userSpaceOnUse"
+ x2="700.41"
+ gradientTransform="matrix(.15392 0 0 .1533 -88.254 -28.438)"
+ y1="327.78"
+ x1="697.91">
+ <stop
+ stop-color="#f0f0f4"
+ offset="0"
+ id="stop166" />
+ <stop
+ stop-color="#eeeeec"
+ offset=".037441"
+ id="stop168" />
+ <stop
+ stop-color="#eeeeec"
+ stop-opacity=".69595"
+ offset=".39254"
+ id="stop170" />
+ <stop
+ stop-color="#a1a29f"
+ offset="0.908"
+ id="stop172" />
+ <stop
+ stop-color="#555753"
+ offset="1"
+ id="stop174" />
+ </linearGradient>
+ <radialGradient
+ id="ce"
+ gradientUnits="userSpaceOnUse"
+ cy="14.782"
+ cx="76.166"
+ gradientTransform="matrix(4.2066 1.9379e-8 0 .1402 -297.74 19.928)"
+ r="21">
+ <stop
+ stop-color="#7a7c7c"
+ offset="0"
+ id="stop177" />
+ <stop
+ stop-color="#33393a"
+ offset="1"
+ id="stop179" />
+ </radialGradient>
+ <linearGradient
+ id="bk"
+ y2="45.818"
+ gradientUnits="userSpaceOnUse"
+ x2="8.5625"
+ gradientTransform="translate(-53.5,9.8117)"
+ y1="4.6468"
+ x1="8.5625">
+ <stop
+ stop-color="#2f3537"
+ offset="0"
+ id="stop182" />
+ <stop
+ stop-color="#8a8e8e"
+ offset="0.3"
+ id="stop184" />
+ <stop
+ stop-color="#2f3537"
+ offset="1"
+ id="stop186" />
+ </linearGradient>
+ <radialGradient
+ id="cf"
+ gradientUnits="userSpaceOnUse"
+ cy="114.57"
+ cx="20.892"
+ gradientTransform="matrix(0.2297,0,0,0.2297,4.6135,3.9798)"
+ r="5.256">
+ <stop
+ stop-color="#f0f0f0"
+ offset="0"
+ id="stop189" />
+ <stop
+ stop-color="#9a9a9a"
+ offset="1"
+ id="stop191" />
+ </radialGradient>
+ <radialGradient
+ id="cg"
+ gradientUnits="userSpaceOnUse"
+ cy="64.568"
+ cx="20.892"
+ gradientTransform="matrix(0.2297,0,0,0.2297,4.6135,3.9798)"
+ r="5.257">
+ <stop
+ stop-color="#f0f0f0"
+ offset="0"
+ id="stop194" />
+ <stop
+ stop-color="#9a9a9a"
+ offset="1"
+ id="stop196" />
+ </radialGradient>
+ <radialGradient
+ id="ch"
+ gradientUnits="userSpaceOnUse"
+ cy="7.2679"
+ cx="8.1436"
+ gradientTransform="matrix(0.83037,0,0,0.95552,-34.846,-1.8031)"
+ r="38.159">
+ <stop
+ stop-color="#fff"
+ offset="0"
+ id="stop199" />
+ <stop
+ stop-color="#f8f8f8"
+ offset="1"
+ id="stop201" />
+ </radialGradient>
+ <radialGradient
+ id="ci"
+ gradientUnits="userSpaceOnUse"
+ cy="35.737"
+ cx="33.967"
+ gradientTransform="matrix(0.8327,0,0,0.97109,-37.62,-2.5748)"
+ r="86.708">
+ <stop
+ stop-color="#fafafa"
+ offset="0"
+ id="stop204" />
+ <stop
+ stop-color="#bbb"
+ offset="1"
+ id="stop206" />
+ </radialGradient>
+ <radialGradient
+ id="cj"
+ gradientUnits="userSpaceOnUse"
+ cy="3.7561"
+ cx="8.8244"
+ gradientTransform="matrix(0.83945,0,0,0.96329,-34.713,-1.9718)"
+ r="37.752">
+ <stop
+ stop-color="#a3a3a3"
+ offset="0"
+ id="stop209" />
+ <stop
+ stop-color="#4c4c4c"
+ offset="1"
+ id="stop211" />
+ </radialGradient>
+ </defs>
+ <g
+ fill="none"
+ id="g213">
+ <path
+ d="m11.506 5.4943v37.907"
+ stroke-opacity=".017544"
+ stroke="#000"
+ stroke-width=".98855"
+ id="path215" />
+ <path
+ stroke-opacity=".20468"
+ d="m12.5 5.0205v38.018"
+ stroke="#fff"
+ id="path217" />
+ </g>
+ <g
+ opacity=".3"
+ transform="matrix(.9138 0 0 1 4.4269 -9.3837)"
+ id="g219">
+ <path
+ fill="url(#ba)"
+ d="m8.8251 42v4.9997c-3.2368 0.009-7.8251-1.12-7.8251-2.5s3.6121-2.4995 7.8251-2.4995z"
+ id="path221" />
+ <rect
+ height="5"
+ width="30.35"
+ y="42"
+ x="8.8251"
+ fill="url(#av)"
+ id="rect223" />
+ <path
+ fill="url(#bb)"
+ d="m39.175 42v4.9997c3.2369 0.0094 7.8251-1.1202 7.8251-2.5002s-3.612-2.5-7.825-2.5z"
+ id="path225" />
+ </g>
+ <g
+ transform="translate(46,33.47034)"
+ id="g227">
+ <rect
+ style="color:#000000;fill:url(#ci);stroke:url(#cj);stroke-width:0.89924002;stroke-linecap:round;stroke-linejoin:round;display:block"
+ display="block"
+ transform="matrix(0,1,-1,0,0,0)"
+ ry="1.0718"
+ width="30.235001"
+ y="0.82638001"
+ x="-31.895"
+ height="38.167999"
+ id="rect229" />
+ <rect
+ style="color:#000000;fill:none;stroke:url(#ch);stroke-width:0.89074999;stroke-linecap:round;stroke-linejoin:round;display:block"
+ transform="matrix(0,1,-1,0,0,0)"
+ display="block"
+ rx="0.12782"
+ ry="0.13789999"
+ height="36.033001"
+ width="28.108"
+ y="1.8398"
+ x="-31.148001"
+ id="rect231" />
+ <g
+ transform="matrix(0,1.3362,-1.0851,0,9.545,-39.685)"
+ id="g233">
+ <g
+ transform="matrix(0.2297,0,0,0.2297,4.9671,4.245)"
+ id="g235"
+ style="fill:#ffffff;stroke:#000000">
+ <path
+ d="m 23.428,113.07 c 0,1.973 -1.6,3.572 -3.573,3.572 -1.974,0 -3.573,-1.6 -3.573,-3.572 0,-1.974 1.6,-3.573 3.573,-3.573 1.973,0 3.573,1.6 3.573,3.573 z"
+ id="path237" />
+ <path
+ d="m 23.428,63.07 c 0,1.973 -1.6,3.573 -3.573,3.573 -1.974,0 -3.573,-1.6 -3.573,-3.573 0,-1.974 1.6,-3.573 3.573,-3.573 1.973,0 3.573,1.6 3.573,3.573 z"
+ id="path239" />
+ </g>
+ <path
+ d="m 9.995,29.952 c 0,0.4532 -0.36752,0.8205 -0.82073,0.8205 -0.45343,0 -0.82073,-0.36752 -0.82073,-0.8205 0,-0.45343 0.36752,-0.82073 0.82073,-0.82073 0.4532,0 0.82073,0.36752 0.82073,0.82073 z"
+ id="path241"
+ style="fill:url(#cf)" />
+ <path
+ d="m 9.995,18.467 c 0,0.4532 -0.36752,0.82073 -0.82073,0.82073 -0.45343,0 -0.82073,-0.36752 -0.82073,-0.82073 0,-0.45343 0.36752,-0.82073 0.82073,-0.82073 0.4532,0 0.82073,0.36752 0.82073,0.82073 z"
+ id="path243"
+ style="fill:url(#cg)" />
+ </g>
+ <path
+ style="color:#000000;fill:none;stroke:#9b9b9b;stroke-width:1.20410001;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.74510001"
+ d="M -22.444,-23.794 H -33.295"
+ id="path245" />
+ <path
+ style="color:#000000;fill:none;stroke:#9b9b9b;stroke-width:1.20410001;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.74510001"
+ d="m -22.444,-17.113 -3.2554,-6.6808"
+ id="path247" />
+ <path
+ style="color:#000000;fill:none;stroke:#9b9b9b;stroke-width:1.20410001;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.74510001"
+ d="m -33.295,-17.113 3.2554,-6.6808"
+ id="path249" />
+ <path
+ style="color:#000000;fill:none;stroke:#9b9b9b;stroke-width:1.1494;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.74510001"
+ d="m -27.825,-24.512 v -4.7243"
+ id="path251" />
+ <path
+ style="color:#000000;fill:none;stroke:#9b9b9b;stroke-width:1.20410001;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.74510001"
+ d="m -33.295,-17.113 h -3.2554"
+ id="path253" />
+ <path
+ style="color:#000000;fill:none;stroke:#9b9b9b;stroke-width:1.20410001;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.74510001"
+ d="m -36.639,-17.148 v -5.3447"
+ id="path255" />
+ <path
+ style="color:#000000;fill:none;stroke:#9b9b9b;stroke-width:1.20410001;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.74510001"
+ d="m -22.444,-17.113 h 6.5107"
+ id="path257" />
+ <path
+ style="color:#000000;fill:none;stroke:#9b9b9b;stroke-width:1.20410001;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.74510001"
+ d="m -15.934,-15.777 v -2.6723 h 7.5958 v 2.6723 h -7.5958"
+ id="path259" />
+ <path
+ style="color:#000000;fill:none;stroke:#9b9b9b;stroke-width:0.99326003;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.74510001"
+ d="m -19.189,-17.306 v 7.2734"
+ id="path261" />
+ <path
+ style="color:#000000;fill:none;stroke:#9b9b9b;stroke-width:1.20410001;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0.74510001"
+ d="M -5.0823,-17.113 H -8.3377"
+ id="path263" />
+ <path
+ style="color:#000000;fill:#9b9b9b;fill-opacity:0.74510001;fill-rule:evenodd"
+ d="m -33.295,-17.113 3.0519,-2.9229 -2.645,-1.5032 -0.40692,4.426 z"
+ id="path265" />
+ </g>
+ <g
+ transform="matrix(.92391 0 0 .875 -.53806 5.8515)"
+ id="g267">
+ <g
+ opacity=".3"
+ transform="translate(-1.5002,1.8117)"
+ id="g269">
+ <path
+ fill="url(#ba)"
+ d="m8.8251 42v4.9997c-3.2368 0.009-7.8251-1.12-7.8251-2.5s3.6121-2.4995 7.8251-2.4995z"
+ id="path271" />
+ <rect
+ height="5"
+ width="30.35"
+ y="42"
+ x="8.8251"
+ fill="url(#av)"
+ id="rect273" />
+ <path
+ fill="url(#bb)"
+ d="m39.175 42v4.9997c3.2369 0.0094 7.8251-1.1202 7.8251-2.5002s-3.612-2.5-7.825-2.5z"
+ id="path275" />
+ </g>
+ <rect
+ stroke-linejoin="round"
+ rx="2.4749"
+ ry="2.1004"
+ height="26.99"
+ width="40.99"
+ stroke="url(#bk)"
+ stroke-linecap="round"
+ y="19.317"
+ x="2.005"
+ stroke-width="1.01"
+ fill="url(#ce)"
+ id="rect277" />
+ <rect
+ rx="1.6573"
+ ry="1.6573"
+ height="24"
+ width="40"
+ y="21.812"
+ x="2.4998"
+ fill="url(#bj)"
+ id="rect279" />
+ <path
+ opacity=".27228"
+ fill="url(#cd)"
+ d="m30.74 24.195a2.7165 2.6372 0 0 1 -5.433 0 2.7165 2.6372 0 1 1 5.433 0z"
+ id="path281" />
+ <path
+ fill="url(#cc)"
+ d="m30.727 25.473a2.7042 1.8094 0 0 1 -5.4083 0 2.7042 1.8094 0 1 1 5.4083 0z"
+ id="path283" />
+ <path
+ fill="url(#cb)"
+ d="m38.5 33.812a10 10 0 0 1 -20 0 10 10 0 1 1 20 0z"
+ id="path285" />
+ <path
+ fill="#888a85"
+ d="m37.5 33.812a9 9 0 0 1 -18 0 9 9 0 1 1 18 0z"
+ id="path287" />
+ <path
+ fill="url(#ca)"
+ d="m39.5 33.812a11 11 0 0 1 -22 0 11 11 0 1 1 22 0z"
+ id="path289" />
+ <path
+ fill="url(#bz)"
+ d="m38.437 34.25a10 10 0 0 1 -20 0 10 10 0 1 1 20 0z"
+ id="path291" />
+ <path
+ fill="url(#by)"
+ d="m36.569 33.475a8.0691 7.8189 0 0 1 -16.138 0 8.0691 7.8189 0 1 1 16.138 0z"
+ id="path293" />
+ <path
+ opacity=".85149"
+ fill="url(#bx)"
+ d="m35.5 33.812a7 7 0 0 1 -14 0 7 7 0 1 1 14 0z"
+ id="path295" />
+ <path
+ fill="url(#bi)"
+ d="m35.5 33.812a7 7 0 0 1 -14 0 7 7 0 1 1 14 0z"
+ id="path297" />
+ <path
+ fill="url(#bw)"
+ d="m35.5 34.812a7 7 0 0 1 -14 0 7 7 0 1 1 14 0z"
+ id="path299" />
+ <path
+ opacity=".53960"
+ fill="url(#bv)"
+ d="m34.234 34.76a5.7665 6.0002 0 0 1 -11.533 0 5.7665 6.0002 0 1 1 11.533 0z"
+ id="path301" />
+ <path
+ stroke-linejoin="round"
+ d="m34.176 35.312a5.676 5.426 0 0 1 -11.352 0 5.676 5.426 0 1 1 11.352 0z"
+ stroke="url(#bu)"
+ stroke-linecap="round"
+ stroke-width=".1485"
+ fill="url(#bt)"
+ id="path303" />
+ <path
+ stroke-linejoin="round"
+ d="m33.928 36.812c0 2.9961-2.4316 5.4277-5.4277 5.4277s-5.4277-2.4316-5.4277-5.4277 2.4316-5.4277 5.4277-5.4277 5.4277 2.4316 5.4277 5.4277z"
+ stroke="url(#bh)"
+ stroke-linecap="round"
+ stroke-width=".14459"
+ fill="url(#bs)"
+ id="path305" />
+ <path
+ fill="#2e3436"
+ d="m33 36.812a4.5 4.5 0 0 1 -9 0 4.5 4.5 0 1 1 9 0z"
+ id="path307" />
+ <path
+ opacity=".23267"
+ fill="url(#bg)"
+ d="m27.724 34.278c-0.08496 0.02653-0.16639 0.05752-0.24728 0.0919 0.08079-0.03461 0.1624-0.06518 0.24728-0.0919zm1.77 0.05013c0.035 0.01321 0.06988 0.02718 0.10412 0.04177-0.03437-0.01461-0.06898-0.02856-0.10412-0.04177zm-2.308 0.18798c-0.05662 0.03268-0.11114 0.06787-0.16486 0.10443 0.05331-0.03657 0.10869-0.07167 0.16486-0.10443zm-0.16486 0.10443c-0.06461 0.04398-0.12648 0.08859-0.18655 0.13785 0.05929-0.04879 0.12287-0.09417 0.18655-0.13785zm3.2754 0.18798c0.03712 0.0324 0.07334 0.06589 0.10846 0.10026-0.03511-0.03423-0.07136-0.06799-0.10846-0.10026zm-3.5271 0.0084c-0.04762 0.0418-0.09456 0.08446-0.13883 0.12949 0.04437-0.04514 0.09114-0.08753 0.13883-0.12949zm-0.13883 0.12949c-0.33737 0.34318-0.57623 0.77514-0.6681 1.2574-0.000256 0.0014 0.000257 0.0028 0 0.0042v0.88976c0.13918 0.7375 0.61747 1.3607 1.2755 1.721h2.5987c0.63044-0.34525 1.0935-0.93177 1.2538-1.6291v-1.0652c-0.072-0.315-0.203-0.61-0.387-0.868-0.142-0.184-0.307-0.317-0.485-0.389h-3.488c-0.03512 0.02614-0.06746 0.05237-0.09978 0.07937zm-0.6681 2.1513c-0.01352-0.07159-0.02313-0.14322-0.03037-0.21722 0.007 0.07375 0.01683 0.14545 0.03037 0.21722zm0-0.88976c-0.01357 0.07192-0.0234 0.14331-0.03037 0.21722 0.0072-0.07297 0.01685-0.14567 0.03037-0.21722zm4.5596-1.1822c0.02542 0.02829 0.04966 0.05836 0.07375 0.08772-0.02444-0.02974-0.04796-0.05908-0.07375-0.08772zm-2.828 3.991c0.05283 0.01722 0.10623 0.03193 0.16052 0.04595-0.05432-0.01407-0.10765-0.02866-0.16052-0.04595zm1.6876 0c-0.05312 0.01729-0.10592 0.03188-0.16052 0.04595 0.05429-0.01402 0.10769-0.02874 0.16052-0.04595z"
+ id="path309" />
+ <path
+ opacity=".37557"
+ fill="url(#br)"
+ d="m26.234 33.812c-0.535 0.385-0.965 0.904-1.234 1.501v2.946c0.28195 0.62634 0.73926 1.1627 1.3106 1.5527h4.4296c0.538-0.369 0.975-0.868 1.259-1.45v-3.1465c-0.271-0.557-0.682-1.041-1.189-1.404h-4.5769z"
+ id="path311" />
+ <path
+ fill="#2e3436"
+ d="m27.734 34.215c-0.08393 0.02688-0.16438 0.05827-0.24429 0.0931 0.07981-0.03506 0.16044-0.06603 0.24429-0.0931zm1.7486 0.05078c0.03457 0.01338 0.06903 0.02754 0.10286 0.04232-0.03396-0.0148-0.06815-0.02893-0.10286-0.04232zm-2.28 0.19043c-0.05593 0.03311-0.1098 0.06876-0.16286 0.1058 0.05266-0.03705 0.10737-0.07261 0.16286-0.1058zm-0.16286 0.1058c-0.06383 0.04455-0.12495 0.08974-0.18429 0.13965 0.05858-0.04943 0.12138-0.0954 0.18429-0.13965zm3.2357 0.19043c0.03667 0.03282 0.07245 0.06675 0.10715 0.10157-0.03468-0.03468-0.07049-0.06888-0.10715-0.10157zm-3.4843 0.0085c-0.04705 0.04235-0.09341 0.08557-0.13714 0.13119 0.04384-0.04573 0.09004-0.08867 0.13714-0.13119zm-0.138 0.132c-0.33328 0.34766-0.56925 0.78528-0.66001 1.2738-0.000253 0.0014 0.000254 0.0029 0 0.0042v0.90138c0.13749 0.74714 0.60999 1.3785 1.26 1.7435h2.5672c0.6228-0.34977 1.0802-0.94395 1.2386-1.6504v-1.0792c-0.07047-0.31936-0.2005-0.61814-0.38143-0.88022-0.14074-0.1859-0.30357-0.32067-0.48-0.39356h-3.4458c-0.03469 0.02648-0.06665 0.05306-0.09857 0.0804zm-0.66001 2.1794c-0.01335-0.07252-0.02285-0.14509-0.03-0.22006 0.0069 0.07472 0.01662 0.14735 0.03 0.22006zm0-0.90138c-0.01341 0.07286-0.02312 0.14518-0.03 0.22006 0.0071-0.07392 0.01665-0.14757 0.03-0.22006zm4.5043-1.1976c0.02511 0.02866 0.04905 0.05912 0.07286 0.08887-0.02414-0.03013-0.04738-0.05985-0.07286-0.08887zm-2.7943 4.0414c0.05219 0.01744 0.10494 0.03235 0.15857 0.04655-0.05367-0.01425-0.10635-0.02904-0.15857-0.04655zm1.6672 0c-0.05248 0.01752-0.10464 0.0323-0.15857 0.04655 0.05363-0.0142 0.10638-0.02911 0.15857-0.04655z"
+ id="path313" />
+ <path
+ opacity=".23267"
+ fill="url(#bq)"
+ d="m27.734 34.153c-0.08393 0.02723-0.16438 0.05903-0.24429 0.0943 0.07981-0.03551 0.16044-0.06688 0.24429-0.0943zm1.7486 0.05144c0.03457 0.01355 0.06903 0.02789 0.10286 0.04286-0.034-0.015-0.069-0.029-0.103-0.043zm-2.281 0.193c-0.05593 0.03353-0.1098 0.06965-0.16286 0.10716 0.05266-0.03753 0.10737-0.07354 0.16286-0.10716zm-0.16286 0.10716c-0.06383 0.04513-0.12495 0.0909-0.18429 0.14145 0.05858-0.05006 0.12138-0.09663 0.18429-0.14145zm3.2357 0.19289c0.03667 0.03325 0.07245 0.06761 0.10715 0.10288-0.03468-0.03512-0.07049-0.06977-0.10715-0.10288zm-3.4843 0.0086c-0.04705 0.0429-0.09341 0.08667-0.13714 0.13288 0.04384-0.04632 0.09004-0.08982 0.13714-0.13288zm-0.138 0.133c-0.33328 0.35214-0.56925 0.7954-0.66001 1.2902-0.000253 0.0014 0.000254 0.0029 0 0.0043v0.91301c0.13749 0.75678 0.60999 1.3962 1.26 1.766h2.5672c0.6228-0.35428 1.0802-0.95612 1.2386-1.6717v-1.0931c-0.071-0.324-0.201-0.627-0.382-0.892-0.141-0.188-0.303-0.325-0.48-0.399h-3.4458c-0.03469 0.02682-0.06665 0.05374-0.09857 0.08144zm-0.66 2.207c-0.01335-0.07346-0.02285-0.14697-0.03-0.22289 0.0069 0.07568 0.01662 0.14925 0.03 0.22289zm0-0.91301c-0.01341 0.0738-0.02312 0.14705-0.03 0.2229 0.0071-0.07488 0.01665-0.14948 0.03-0.2229zm4.5043-1.2131c0.02511 0.02903 0.04905 0.05988 0.07286 0.09002-0.02414-0.03052-0.04738-0.06063-0.07286-0.09002zm-2.7943 4.0935c0.05219 0.01767 0.10494 0.03277 0.15857 0.04715-0.054-0.015-0.107-0.03-0.159-0.048zm1.6672 0c-0.05248 0.01774-0.10464 0.03271-0.15857 0.04715 0.05363-0.01438 0.10638-0.02949 0.15857-0.04715z"
+ id="path315" />
+ <path
+ fill-opacity=".50543"
+ d="m28.512 34.806c-1.0298-0.000002-1.8752 0.77295-1.9851 1.7632-0.0046 0.04174-0.008 0.08347-0.0099 0.12595-0.0014 0.0304-0.005 0.05994-0.005 0.09068 0 0.0067-0.000067 0.01348 0 0.02015-0.000067 0.0067 0 0.01346 0 0.02015 0.000605 0.03001 0.003 0.06102 0.005 0.09068 0.002 0.04247 0.0053 0.08421 0.0099 0.12595 0.10987 0.99028 0.95525 1.7632 1.9851 1.7632s1.8752-0.77295 1.9851-1.7632c0.0046-0.04174 0.008-0.08347 0.0099-0.12595 0.0024-0.03624 0.0046-0.07407 0.005-0.11083-0.00037-0.03675-0.0026-0.0746-0.005-0.11083-0.002-0.042-0.005-0.084-0.01-0.126-0.11-0.99-0.955-1.763-1.985-1.763z"
+ id="path317" />
+ <path
+ stroke-linejoin="round"
+ d="m39.016 33.812a10.516 10.516 0 0 1 -21.033 0 10.516 10.516 0 1 1 21.033 0z"
+ stroke="url(#bp)"
+ stroke-linecap="round"
+ fill="none"
+ id="path319" />
+ <path
+ d="m29.013 24.307a2.0122 2.017 0 0 1 -4.0244 0 2.0122 2.017 0 1 1 4.0244 0z"
+ fill-rule="evenodd"
+ stroke="url(#bf)"
+ stroke-miterlimit="10"
+ stroke-width=".97678"
+ fill="#2e3436"
+ id="path321" />
+ <path
+ opacity=".34706"
+ fill-rule="evenodd"
+ d="m27.834 24.314a0.81921 0.82118 0 0 1 -1.6384 0 0.81921 0.82118 0 1 1 1.6384 0z"
+ id="path323" />
+ <path
+ opacity=".30588"
+ d="m28.417 16.445a0.8397 0.8397 0 1 1 -1.6794 0 0.8397 0.8397 0 1 1 1.6794 0z"
+ fill-rule="evenodd"
+ transform="matrix(.6932 0 0 .69486 8.2648 13.585)"
+ filter="url(#ck)"
+ fill="#fff"
+ id="path325" />
+ <path
+ d="m23.18 25.395a1.2632 1.2632 0 0 1 -2.5263 0 1.2632 1.2632 0 1 1 2.5263 0z"
+ fill-rule="evenodd"
+ stroke="url(#be)"
+ stroke-miterlimit="10"
+ stroke-width=".47368"
+ fill="#2e3436"
+ id="path327" />
+ <rect
+ rx=".93042"
+ ry=".79550"
+ height="2.2404"
+ width="8.2404"
+ stroke="url(#bd)"
+ y="23.192"
+ x="32.88"
+ stroke-width=".75957"
+ fill="url(#bc)"
+ id="rect329" />
+ <path
+ d="m17.228 20.683a3.7307 1.6168 0 0 1 -7.4614 0 3.7307 1.6168 0 1 1 7.4614 0z"
+ stroke-opacity=".99608"
+ fill-rule="evenodd"
+ stroke="url(#bo)"
+ stroke-miterlimit="10"
+ stroke-width=".4957"
+ fill="#2e3436"
+ id="path331" />
+ <path
+ fill-rule="evenodd"
+ fill="url(#bn)"
+ d="m16.5 20.062a3 1.25 0 0 1 -6 0 3 1.25 0 1 1 6 0z"
+ id="path333" />
+ <rect
+ stroke-linejoin="round"
+ rx=".51201"
+ ry=".55650"
+ height="1.4607"
+ width="1.4429"
+ stroke="url(#bm)"
+ stroke-miterlimit="0"
+ y="20.772"
+ x="12.662"
+ stroke-width=".30218"
+ fill="#2e3436"
+ id="rect335" />
+ <rect
+ opacity=".4"
+ stroke-linejoin="round"
+ rx="1.3157"
+ ry="1.3157"
+ height="22.999"
+ width="38.999"
+ stroke="url(#cl)"
+ stroke-linecap="round"
+ y="22.312"
+ x="3.0002"
+ stroke-width="1.0008"
+ fill="none"
+ id="rect337" />
+ <path
+ opacity=".8"
+ d="m32 37.11c0 1.933-1.567 3.5-3.5 3.5s-3.5-1.567-3.5-3.5 1.567-3.5 3.5-3.5 3.5 1.567 3.5 3.5z"
+ fill-rule="evenodd"
+ fill="url(#bl)"
+ id="path339" />
+ <rect
+ opacity=".5"
+ rx="1"
+ ry="1"
+ height="10"
+ width="2"
+ y="29.812"
+ x="9.4998"
+ fill="#eeeeec"
+ id="rect341" />
+ <rect
+ opacity=".2"
+ rx="1"
+ ry="1"
+ height="8"
+ width="2"
+ y="30.812"
+ x="13.5"
+ fill="#555753"
+ id="rect343" />
+ </g>
+</svg>
diff --git a/resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-cvpcb-project.svg b/resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-cvpcb-project.svg
new file mode 100644
index 0000000..e4bc2f0
--- /dev/null
+++ b/resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-cvpcb-project.svg
@@ -0,0 +1,41 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs>
+ <linearGradient id="d" y2="-151.35" gradientUnits="userSpaceOnUse" x2="682.52" gradientTransform="matrix(-1,0,0,1,921.6,341.5)" y1="-167.43" x1="747">
+ <stop stop-color="#fcaf3e" offset="0"/>
+ <stop stop-color="#fcaf3e" stop-opacity="0.65" offset="1"/>
+ </linearGradient>
+ <linearGradient id="e" y2="-2857.7" gradientUnits="userSpaceOnUse" x2="-1416.8" gradientTransform="matrix(.2344 0 0 .2344 550.77 853.66)" y1="-2902.3" x1="-1597">
+ <stop stop-color="#fcaf3e" offset="0"/>
+ <stop stop-color="#f2983d" stop-opacity=".75660" offset=".49680"/>
+ <stop stop-color="#e77c3c" stop-opacity="0.51" offset="1"/>
+ </linearGradient>
+ <radialGradient id="f" gradientUnits="userSpaceOnUse" cy="-969.15" cx="-1035.3" gradientTransform="matrix(.4465 -.2703 -.2019 -.3361 447.65 -605.27)" r="76.859">
+ <stop stop-color="#fffbd7" offset="0"/>
+ <stop stop-color="#e77c3c" offset="1"/>
+ </radialGradient>
+ </defs>
+ <rect style="color:#000000" height=".67940" width="0" stroke="#000" y="32.209" x="7.913" stroke-width=".69494"/>
+ <image xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAABHNCSVQICAgIfAhkiAAAFaFJREFU eJztXWuQHNV1/rp7uqdnemZ2dlerfeixi0AIbD0WUY5Rgc26RMqmqBRKuUj8I0nZVU4oV8WxKomN YjBag7FXwQ6UHUgUUgT/MZIpqpZKiVAVGwTCNmALpBhsQNZKSNqHVjuP3Xl2973d+dGP6Xnt7uxs z/Zo5tu62zNz+3af7j59zrnnnD7N6LqONloX7FoT0Mbaos0ALY42A7Q42gzQ4mgzQIujzQAtjjYD tDjaDNDiaDNAi6PNAC2ONgO0ONoM0OJoM0CLw+fmxufnF2pa/+jRIwCA7du3Y9u2bRAEAff82Rd2 AXiGZZlhAGAYBgDAgCkeXPK1v78fd911F3w+4xA5jgMAsGxlnreiotaSUgoAIITg2LFjmJ6eLhlQ tACs8eYvmqafArD/uZ8eeVVRFHzwwQd49913AQD33nvvEmeiMm1ugHFz47UwwNGjRxCNRnHLLbeg s7MTAPD5z9+zi+W4U/39/fjEJ24G4LzwizPAu7/9LW697TabAawLX50BrKUGANA0Y0kIwcsvv4wd 27dD8PsdA8q2YP43lr/+9UlMT09Do3T4+eefOw0AiUQCb7zxBpLJZE1M4OY18oQKsC7+3r177Yt/ 4cIFMCw76vP54PNx4Dif2Tij+Yxm9Pvg44qbDgaJRAKKokBRFBBKQCgBpRSUUmiaVtQoJaCUgBCj WePi8Ti2XrcVGzduKt6HuV+LDpsus/lM2hiWHU2lUgCAzs5O7N27F9FoFIcPH17LU25jzSXA0aNH wPM8Pve5zyEUCgEAxsbGcOjQIezde4fu43kADhFuqgBYqoBhyjdqYuvW63HttVsAoEwVWONKRb/z zgeAs2cnMDy8CxcufIQ333yrbB96QXQY40tViKpizy23MLfediv27NkDXdeRTqfx0ksvQVXVZUkC N6+RqzbAcsnevXu3ffG/8pWv4NlnnzUuEMOCZY0LVtDhXNHYxRhgcvISenrWgfP5wFdhAJtW8yQT x4UjhCIQEBGJRDAxcQ4cV366yi6OOd7+mTEkzmuvvoZkMok777wToVAIu3fvxptvvrnoeWkE3FUB ur5o++nRI5AkCVu2GHfpk08+iZ/85CdF4/UqzdpGtX5d15HPyzg7MYFMJmO39BLNue6lS5dw4w03 YHp6GrFYfFl0VPpOiKF6Tr19Cid/cxIAsGXLFkiStOaqwFUJsBxs27YNAJBMJjE2NlbcyTisfoY1 l9Xv+Eq4MnsFDGDbFtUkgKYVi+5kMoHrr78eoVAIP3/5leo7sGcl5lfTyGRMVQIG0KhmH8srr7yC HTt3gOd5bNu2DW+//XZNx7PaWHMGiEajAIBjx47hm/c/AEHwA4yhs18YHy/T0SvRh5cvzyKflxGO hMGxi6sASilSqRR27tiBrdddh9dOvI5YLLbsfVWil1AChmHAgEE2k8X777+PHTt22Me+llhzBujt 7QUAKCqF3y9iaGjIsKAr6Ftg5QYRIQSTlyYRCATg431gmWLtp+kaiEoQjXZg5PZPo6u7C6+9dgJn /vCHFe2vaNsOCcCAwcTZCezcudM+9rXEmjMAAPzwR/+KYDAIAKCUWFK1oEdRvyUsyzJ27tyBcDiE WCyORCIBlmURi8XR2RmFJEnYuvU69Pf1I5VO4cUX/wfT0zM176eSBKCUFjFAPB4HwzCuWvfLhbuz gGUeIMOwYEzRfHl2FgDA84K1lZLlyqAoMt555x0IgoDBzZvxsRtvRCgUQnd3N2RZRjweRywWx7vv vocLFy7UsadyeqlGDQcWYziyNKrVbMu4BVcZ4D/PXAbDlnjunMd982dw+MMZ6B2doPkMAICQgo5e TQlgQZZlfHjmDD48c2ZVtleKShJg8ItfhW4al5QQZAE8+svf4u8/+TFXaKgFrjqCnjwzB4Zj8c7Y gQ6O4/YBGLJcsUyJI6fSHfHe734/KgiGJLAcOV65c6qhNJYgyzI+/rEbR6utV8URdZ5SOv7UU0/N O/vcgKsMcDqRwxP3fW0XgHHB7x8KBIIIBEQIggBRFOEX/OBNT19BUpiEAThy9Dn4TQbgmpgBvvDn 95QpMHs9QiArCuS8DFmRkc/lkclmIOfz5wHse+qpp043rSeQ5XlwHPcMw7BD0c4uhMNhdHV1IRIO o7OzE5FIBJIkGetWkAzPHjlqB1esIE1Z1MdjKNzRFr0aPvXp2x13sR0tBADk83ksLCwgkUgilVpA PJ5AKpXClSuXhzRKnwFwk5v0uuoJfOzv/uZuXdeHwaxwN4s7EpumrQQs54OuY/hLX/rS3SvbwvLg 9jRwWNN1cCxjR8l4Hw8fz0MQBFsVAJUlgI7VNwLdRrluBwKBQJlkoFSz1/P7/RAEHjwvwIp++jgf CKvC5/MNA3jBLXo94QeoCuct1CQMUEavx+luCAOUZe9UWseO8hbW1fRCeJWxlqtP3qrCtuZLlrWh cUfpbQkAHavlCGocSun1Nt1uM8B5wJjbatRohKggKoGiqlAUBfl8HgDAcZah6LABXHAEuY1K8/tc Lufot1LOjH5ZliErChRFhaoqdkYSoQSaRkEIOe8mvW4zwDjHskkGWFHY62phgBVuCCzDJAGMrxJp FeEqA+x68In59777tf2U0mdSqXmwHAtR9BuzAZ6zkyWAgg1QkACwEyuA5mEAlNKr60gk4g5bUHeu BkWRkUqlkcmkkc1mkcmkMZ9MIJ/LAsD+7r/4x3k3yXXVE/jYuSwA4Pff27+L47hRACMsy0aB5bmC T779th0UqpbI4T0Y55MQwxOoqgpu3r27fK3qruAkgOOU0sd7vnzw1XxqHj/Y617MoCHRwBsOPHYa wJ/WOv43f/nZ0pzLpoGT7qH932N0M0NIzhpBr3TsCgBg6MyvsGfPHiiKgv8WttjjKVEBAD6/6Cqd rjKAVudFM9wATaYCTDgdQbcn3i9fIWIub74ZiqIAAP5EmShfL+ASgSbclQCrwwGFz80EB90bNmxc W1oWgcsSoL6L1oyzAAtOugNmtpMX4W0VYP5Zn5sLDro9zLyeVgFNLQHQHHR7WgU4bQCvn8hSFML/ 3qbb0wxQpAI8fiJLYUsujysvl/0A9Y8vGNNePo3lcE4DvSwF3JUADhtg5okDdwMYLn1ev9Sz5/ze 1DaAg+6DBx88WPp76XfrqWQzl/DU2Ngh15JAnHBdBcw++U8dAI4HAoFhKRRGSJIg+P2QpCD8fj/8 ZtEF60kdKzmUYRiceP0XVwUD3HrbbaP2d83KEzAuuKqqkGUZ2WwOiiwjnUkjtZDCgQP3nQIwMjZ2 yNVYQCNcwccZlh0ORToQDoXR3d2NcDiM7u4uRCIR+7Hw0goeRjAIzRJWL4eD7ptuusnWAtadbi2t pFAjGXQBvlgMHOeDrMjDGqXHdZeTQl1lgKkffv1uv98/zPmMXDee5yH4Bfj9foiiCFEM2I+EVarh c7WogGBQsn8vZQCGYaCoKvxiFrLihyAI4HkBYiAIJZ8fPnDfN+4+fPhw0+YEDlNNB7f0ehWhF9nQ uuO/91FK94q2YTBRcyeFMgzMByNZMAwDlmXBcqyZJczad/6SEqCsLJe34aTbOjag3Oi16wqxLDiW Nc4Py4JlWDAs63r429M5gcUqwP51zeipBeV0exOuM0ChnIoGXdft/ECjWpdmP0LlXN/x5aqwASil VZ8LsKqWUU0zGi2uXub2cbvNAMn6RNjVEQ5eKcxTl1wNcqrBbQZ4Bro+qlEaJSqBqipQZAWyICOX z4Pnefh8lu4vrwV0dSSE6MhkMmUSoPBsYA75XA5yPg9ZlqEoMhRFgaoqoIQkVVV1NSnU1WcDw19+ ZB7Afo0aF1+rMTpYWpGrWVst0DQNspyHqsgAsP/R7//go5o2UCNcTQr96utTAIDM0w/czvP8fgD7 anEF/+xnPy8r8NgscNYavuOOvfbvy3QFjyuK8vijj37/VQDo7u5yjU5XGeBvT0zVNf7le/9YvxoY 4MSJE3XN5dxkAI+Hg68KT7Cn0bBo4IrQxNNA5yzAy5R7okpY1fFNWB/AQrPkMno7KdSRTNF0DOB0 A3iYdk/bAObZc3wuBs/zYDkOGqUQRRFWXX5PoEkcWE2QFVz4XIp169aBNQMpAMxqW/m69rlacKou L7OApyXAUvkArBlBcwzwjKpoFtulqRng0uQkBJ5HOBJGaiFlF5twQpIkRKNRzF65AtV8Bq8RKKLb w0zQsKzg0Av/vAvAvloqhS7FAJQQ5AhBb18fZi/PFvV1d3cjGo2C4zjMzs5CkeXVOKRlw0n3ww8/ dHCxfufS9AiOP/TQw6cbQafrfgBpfKwDwDjL8yOBYBDBYHDZlUKPHXtxUYdKNBpFNBrFwvx8xUqc xAzD6lXGuwnn/rZv3z5aiT6gvFJoNpNFJpMevf/+bx4HsO87j3y3uZNCOY4bZxh2pLN7Xc2VQscO HbKjZ5WkaEdHB86dm8DmTZsxF5sr6puLzWEuNgcpaKiAdDoNVW2kCjDphr7SSqEjGqXjAD7jJp2u RgPF5x+5W9f1kZVWCrXUZ7WWy+UwODgEhmUxODgEv1+0+3p7+zA4OISBgQF0RKNQFGXJ7bnVVgKz UujIgfu+0eyVQrHySqFL2AD5XA7UrDEEGOLUWi82N4ee9T2AruPS5KWGW+NOuldaKZRyV0Wl0DpO vPMWqnABE4kEJEkCy7HIZDLGq1lMbNgwAFEUkc/nsb6nB5eUSaiKunJaasUqOIIawbOerhS6lARY v369fZF7enpw9g9n7b6pqemiELIiN07/A/X6AdqVQgEszQDBYBDnzp2z+4PBIDIZowiTXxQhmDMM wPASrpkfwMNwPSkUunuVQnO5HIaGhuzv8UTcXk80nz6q1NcIOK3+OiuFNnVS6DjHsY+vdPBSDDA1 VT3jaGpqCpIkQZIkzM7OVl3PLRToXvEGrPckN2+l0Nk7v/7RwP8+9kVQ+kw6nQIv8Mhms8uvFFpH jaDOaCekkASBF7B+/XrjTeKN9AM46K6lUqiczyO1MG9VCv3id7835mpSqOvRwMm9+3+84eePJ/O5 7Oj0ZHb48rRx1y7LFYyldWlIMp4uzuVyoFrhIRNCCS7PXIaiKohEIpCVtXMFH3n22UX7nUvTFXwK wOhDD3/H9RoBriaF3nnkvbrG/98/fFYvfWzcic2Dm6EqKiilxhu+z03YU8HNg5tx4SPj/X/9/f1I JBIVg0VuwfkU8MmTJ+sy6/v63HvDqMczgha3AXRdRzqdBqUUftEP3scjT4yLTAlFX18fFFVBOBxe 1F5wA+18ANQ/BVqOEbhp4yYAwPzCfJG1ffHiRXR2doLlWJydOLumnkAvw9NZwcbJq34i+/r6kM1m QSlFOBzGfHIeqlrw9sXj8br2Xx/aDLBKCSH2tworGMafoiq2S1hXjPUGBvohioVKyxMTFQoxu4iC J7iOiFAD4PEycYuneFlivjMaRTwRRy5bUAGTk1Po7OrEunXrsDC/sKYqwLuX3+MSAFicAQYHB8EL vGkEishlc7YdMDQ0BEmSkEgkoOs6WJYtq0XgJkrj/16Ft7OCsbgDaHJyEr29xhRpbm4OWcN5AgCY mZkpCgYZd2IDXcFNUtPI0xJgKRWQzWVx7vw5XLvlWsiyXLRuNBpFIFCwAS5evAhCSaXNuIJ2UiiK /QA3/OJHuwCMoIY3iL2/7JOnQynx9E1NTS53N+7AkQ/wwAP3H6xhZBLA8Ye/80jzJ4Xqmo7tb/1b B4BxXhRHAoEgglIQfsEPv+iHXxDAm6+Ht3MGHD6zF154YVEJwAsCQqEQpmdmEJQko7KGGfKVQiHk cjlwHAdBEJBJp906zIpwSoA/+uQnRwsd1sL4QAiBoihGdRBZQTqdRjabwejBB48D2Df67YeaNynU VAHjDMuOSOEIwuEwOiLGMhKJIBwO2YUinWFgC0sJgE0bNyGdSaN3fS9yuRzm5q7YiR+SJAG6Dsms RJpONZoBCsvt23c4fi82DmXzoi8sLCCVSkHw+8ELAhLx2AhtQFKoqwzw8Tee2MXy/AjnE4y7XhAQ CAQRCAQRCoUQDkcgSQYDOGsDWVjKBiCEIB6LIR6LYWjomqL1WYbF+vUFH/paTgM7Ojoq/G6VijVU l2rmAeTzeSiKAt4vArI88q0H7t/19NNPu6YO3M4H2KfVUSl0qWngzMwMZFlBX18v4vEYUo67fGZm Br29veA4zp4KNhKrMQ001cQ+AE3LAGAYxqgQyrFgOc6sGeyzs4IFwawWbkf9LAnAmGK0+gkcGOjH zMwMWJa1nw7O5Yxg0KZNGxGPx0GpERRq9JPDzoQQURSrhX2h6zpkWQDP82bVNB84nw8+zgfCcq6X xnH1uYB6oS/xl81m0dvbi1gsBmKmhFt/VmqVcYLlJbfl5p+X4bmkUEtyMszSeptQio8uXDCsaLNZ Y1LptG0A6sCaeQKXrXqquIzdrhXstgSoq1KoZdRVa5Ik2Xe5pQasvmQyCUIIIpGIUUiCZZfcnltt pbgaKoWOMwwe13XNrolLiArVzAiWZRnldQOtocySTqB4LIbrrr3WvrMnL12y+64ZGgLHcVBkGVeu XGn408HOeaCRiaSX/Gy+S1g2JJdKCIiqghACSggoJdA1DYSQ5q0U+sbOv/4IwKhGSdEjXMvFUndW NptFMplEKpXC7OxsUd+ZM2fs3wYGBsDzfNNIAEKIVSl09NsPPdy8SaGaruP4tr/69qd+91/nk4n4 aDIRH7pQY32AxTAwMICZmRls2LABoigim83aL2Lu6uoCLwjI5nK2bdDIqaDTBviPw/9etb/0uzk7 OE8pHf3Wgwd/7DadriaFbvuXX9Y1/uIDd+iLMcjGjRsRCASMjGBKkUwm7XBwf38/enp6MD8/j1gs jnR6raaBOt7/4MO6LLnNm9x7+bTHU8IW719YSIEQAo7jTAlQSAiZmprG5cuzGBzcjGuuGcJ77/2u wbOA4qVX4elwsOEJrN7b1dUJRVEgiiJyuRxCIcn2BnZ3d6Gnpwe5XA4ffnjGfgClUWiWhBBXjcDV MZ70qi2bzUIUA9B1IBLpQDabtftEUQQhFDwvYMOGDRAEftFtude8Dc8/F7AYpqenAUxX7LvkmBKu BZy0NzoOUQu8nRKm67bx5+WTuBi8TrenbQCvn7zloNHTz1rh8ixAc7r2aoaXT9xyoet6ww3QWuD+ NLCOGXCbAdyHuwxAVDCsFc8uz/mr8kMRrgYmaF0G0AGmjguo6/o4jIyYZsa42qoMoCtZ6FaRyDJb YFm6YVTX9aZmAIZhRonqXQZw1RGkaXpd7a233joN4+3Zp9yk0yWcAjDy+i9+ddou/LTC5iZcDQa1 4X14OiewDffRZoAWR5sBWhxtBmhxtBmgxdFmgBZHmwFaHG0GaHG0GaDF0WaAFkebAVocbQZocbQZ oMXx/5Om/1dUVK3NAAAAAElFTkSuQmCC " transform="matrix(1.851e-4,1,-1,9.0254e-5,0,0)" height="43.358" width="30.618" y="-49.799" x="20.211"/>
+ <g transform="matrix(.35542 0 0 .3397 -57.093 12.065)">
+ <path d="m268.36 5.3407a30.675 31.406 0 0 1 -61.349 0 30.675 31.406 0 1 1 61.349 0z" stroke="#000" stroke-linecap="square" stroke-width="4" fill="none"/>
+ <path d="m241.62-16.098v42.878" stroke="#000" stroke-width="8" fill="none"/>
+ <path fill-rule="evenodd" d="m220.12-8.3133v26.29l21.37-13.145-21.37-13.145z"/>
+ <path d="m234.77 5.2219h-35.043l-6.4932 0.031719" stroke="#000" stroke-width="4" fill="none"/>
+ <path d="m287.15 19.957h-45.27" stroke="#000" stroke-width="4" fill="none"/>
+ <path d="m287.14-9.1501h-45.45" stroke="#000" stroke-width="4" fill="none"/>
+ </g>
+ <g transform="matrix(.43344 .11715 -.11752 .40543 8.0669 -13.586)">
+ <g transform="matrix(-.84925 -.56427 .49404 -.84925 117.62 338.15)">
+ <path fill="none" d="m168.24 132.25h63.709v63.708h-63.71v-63.71z"/>
+ <path opacity=".2" d="m199.09 132.53c0.77 0.38 1.247 1.148 1.247 2.008v7.581c3.479 0.669 9.808 2.597 15.847 8.141 9.58 8.798 14.438 22.806 14.438 41.633 0 1.148-0.861 2.104-2.001 2.228-1.141 0.122-2.184-0.633-2.426-1.754-2.696-12.449-7.87-21.021-15.379-25.474-4.047-2.399-7.848-3.052-10.478-3.154v7.201c0 0.857-0.478 1.627-1.247 2.008s-1.67 0.293-2.353-0.229l-23.823-18.201c-0.552-0.421-0.88-1.084-0.88-1.779 0-0.692 0.328-1.358 0.88-1.779l23.824-18.2c0.681-0.523 1.582-0.611 2.351-0.23z"/>
+ <path fill="url(#d)" d="m196.94 134.29c0.769 0.38 1.247 1.147 1.247 2.008v7.58c3.479 0.669 9.808 2.597 15.846 8.142 9.581 8.795 14.438 22.804 14.438 41.633 0 1.148-0.86 2.104-2.001 2.228-1.141 0.121-2.184-0.633-2.427-1.755-2.695-12.449-7.869-21.02-15.379-25.472-4.047-2.4-7.847-3.052-10.477-3.154v7.201c0 0.856-0.479 1.626-1.247 2.008-0.77 0.38-1.67 0.292-2.354-0.229l-23.824-18.199c-0.551-0.424-0.88-1.088-0.88-1.781 0-0.691 0.329-1.357 0.88-1.779l23.824-18.201c0.684-0.524 1.585-0.611 2.354-0.23z"/>
+ <path d="m196.49 135.01c0.424 0.211 0.693 0.643 0.693 1.116v8.412c3.083 0.491 9.801 2.196 16.167 8.043 9.368 8.601 14.117 22.36 14.117 40.9 0 0.635-0.479 1.169-1.112 1.235-0.632 0.069-1.214-0.352-1.35-0.974-2.756-12.73-8.087-21.52-15.843-26.118-4.977-2.951-9.641-3.347-11.979-3.32v8.224c0 0.474-0.27 0.906-0.693 1.115-0.424 0.211-0.931 0.16-1.307-0.127l-23.82-18.2c-0.309-0.234-0.489-0.601-0.489-0.987 0-0.388 0.182-0.755 0.489-0.989l23.823-18.201c0.376-0.288 0.883-0.337 1.307-0.128z" stroke="#a66e4b" stroke-miterlimit="10" fill="none"/>
+ <path fill="url(#e)" d="m226.23 193.48c-6.72-31.037-26.985-30.646-30.285-30.369v9.414l-23.824-18.199 23.824-18.201v9.501c3.809 0.39 30.285 4.647 30.285 47.854z"/>
+ <path fill="#fef39e" d="m172.3 154.35 23.823-18.199v2.133l-23.1 16.62-0.72-0.55z"/>
+ <g transform="matrix(.99571 0 0 -.9918 8.8182 167.26)">
+ <path fill="url(#f)" d="m186.18 15.654c-10.237 1.25-14.215-0.656-19.607-4.083-0.132-0.1-0.245-0.187-0.333-0.256 2.111-1.611 17.373-13.271 20.53-15.683v8.109l-0.127-0.063c5.505 0.981 8.872-0.175 15.625-2.917-3.299 7.286-10.145 14.168-16.088 14.893z"/>
+ </g>
+ <path fill="#fef39e" d="m225.58 180.92c0.205 1.348-6.825-31.207-29.488-33.5-0.058-0.005-0.065-0.021-0.065 0.054v-1.77c-0.002-0.001 24.488 1.95 29.553 35.217z"/>
+ </g>
+ </g>
+</svg>
diff --git a/resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-eeschema-project.svg b/resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-eeschema-project.svg
new file mode 100644
index 0000000..b6599cb
--- /dev/null
+++ b/resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-eeschema-project.svg
@@ -0,0 +1,394 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48" version="1.0" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs>
+ <filter id="bb" color-interpolation-filters="sRGB">
+ <feGaussianBlur stdDeviation="2.0786429"/>
+ </filter>
+ <clipPath id="ba">
+ <path fill-rule="evenodd" d="m72 88-32 32h-8v-40h40v8z"/>
+ </clipPath>
+ <filter id="bc" height="1.384" width="1.384" color-interpolation-filters="sRGB" y="-.192" x="-.192">
+ <feGaussianBlur stdDeviation="1.9447689"/>
+ </filter>
+ <linearGradient id="bn" y2="172.44" gradientUnits="userSpaceOnUse" x2="153" gradientTransform="translate(-143,-64)" y1="175.56" x1="153">
+ <stop stop-color="#fff" offset="0"/>
+ <stop stop-color="#ddd" offset="1"/>
+ </linearGradient>
+ <linearGradient id="bo" y2="172.9" gradientUnits="userSpaceOnUse" x2="151.9" gradientTransform="translate(-143,-64)" y1="175.1" x1="154.1">
+ <stop stop-color="#bbb" offset="0"/>
+ <stop stop-color="#616161" offset="1"/>
+ </linearGradient>
+ <linearGradient id="bp" y2="164.44" gradientUnits="userSpaceOnUse" x2="153" gradientTransform="translate(-143,-64)" y1="167.56" x1="153">
+ <stop stop-color="#fff" offset="0"/>
+ <stop stop-color="#ddd" offset="1"/>
+ </linearGradient>
+ <linearGradient id="bq" y2="164.9" gradientUnits="userSpaceOnUse" x2="151.9" gradientTransform="translate(-143,-64)" y1="167.1" x1="154.1">
+ <stop stop-color="#bbb" offset="0"/>
+ <stop stop-color="#616161" offset="1"/>
+ </linearGradient>
+ <linearGradient id="br" y2="156.44" gradientUnits="userSpaceOnUse" x2="153" gradientTransform="translate(-143,-64)" y1="159.56" x1="153">
+ <stop stop-color="#fff" offset="0"/>
+ <stop stop-color="#ddd" offset="1"/>
+ </linearGradient>
+ <linearGradient id="bs" y2="156.9" gradientUnits="userSpaceOnUse" x2="151.9" gradientTransform="translate(-143,-64)" y1="159.1" x1="154.1">
+ <stop stop-color="#bbb" offset="0"/>
+ <stop stop-color="#616161" offset="1"/>
+ </linearGradient>
+ <linearGradient id="bt" y2="148.44" gradientUnits="userSpaceOnUse" x2="153" gradientTransform="translate(-143,-64)" y1="151.56" x1="153">
+ <stop stop-color="#fff" offset="0"/>
+ <stop stop-color="#ddd" offset="1"/>
+ </linearGradient>
+ <linearGradient id="bu" y2="148.9" gradientUnits="userSpaceOnUse" x2="151.9" gradientTransform="translate(-143,-64)" y1="151.1" x1="154.1">
+ <stop stop-color="#bbb" offset="0"/>
+ <stop stop-color="#616161" offset="1"/>
+ </linearGradient>
+ <linearGradient id="bv" y2="140.44" gradientUnits="userSpaceOnUse" x2="153" gradientTransform="translate(-143,-64)" y1="143.56" x1="153">
+ <stop stop-color="#fff" offset="0"/>
+ <stop stop-color="#ddd" offset="1"/>
+ </linearGradient>
+ <linearGradient id="bw" y2="140.9" gradientUnits="userSpaceOnUse" x2="151.9" gradientTransform="translate(-143,-64)" y1="143.1" x1="154.1">
+ <stop stop-color="#bbb" offset="0"/>
+ <stop stop-color="#616161" offset="1"/>
+ </linearGradient>
+ <linearGradient id="bx" y2="132.44" gradientUnits="userSpaceOnUse" x2="153" gradientTransform="translate(-143,-64)" y1="135.56" x1="153">
+ <stop stop-color="#fff" offset="0"/>
+ <stop stop-color="#ddd" offset="1"/>
+ </linearGradient>
+ <linearGradient id="by" y2="132.9" gradientUnits="userSpaceOnUse" x2="151.9" gradientTransform="translate(-143,-64)" y1="135.1" x1="154.1">
+ <stop stop-color="#bbb" offset="0"/>
+ <stop stop-color="#616161" offset="1"/>
+ </linearGradient>
+ <linearGradient id="bz" y2="124.44" gradientUnits="userSpaceOnUse" x2="153" gradientTransform="translate(-143,-64)" y1="127.56" x1="153">
+ <stop stop-color="#fff" offset="0"/>
+ <stop stop-color="#ddd" offset="1"/>
+ </linearGradient>
+ <linearGradient id="ca" y2="124.9" gradientUnits="userSpaceOnUse" x2="151.9" gradientTransform="translate(-143,-64)" y1="127.1" x1="154.1">
+ <stop stop-color="#bbb" offset="0"/>
+ <stop stop-color="#616161" offset="1"/>
+ </linearGradient>
+ <linearGradient id="cb" y2="116.44" gradientUnits="userSpaceOnUse" x2="153" gradientTransform="translate(-143,-64)" y1="119.56" x1="153">
+ <stop stop-color="#fff" offset="0"/>
+ <stop stop-color="#ddd" offset="1"/>
+ </linearGradient>
+ <linearGradient id="cc" y2="116.9" gradientUnits="userSpaceOnUse" x2="151.9" gradientTransform="translate(-143,-64)" y1="119.1" x1="154.1">
+ <stop stop-color="#bbb" offset="0"/>
+ <stop stop-color="#616161" offset="1"/>
+ </linearGradient>
+ <linearGradient id="cd" y2="108.44" gradientUnits="userSpaceOnUse" x2="153" gradientTransform="translate(-143,-64)" y1="111.56" x1="153">
+ <stop stop-color="#fff" offset="0"/>
+ <stop stop-color="#ddd" offset="1"/>
+ </linearGradient>
+ <linearGradient id="ce" y2="108.9" gradientUnits="userSpaceOnUse" x2="151.9" gradientTransform="translate(-143,-64)" y1="111.1" x1="154.1">
+ <stop stop-color="#bbb" offset="0"/>
+ <stop stop-color="#616161" offset="1"/>
+ </linearGradient>
+ <linearGradient id="cf" y2="100.44" gradientUnits="userSpaceOnUse" x2="153" gradientTransform="translate(-143,-64)" y1="103.56" x1="153">
+ <stop stop-color="#fff" offset="0"/>
+ <stop stop-color="#ddd" offset="1"/>
+ </linearGradient>
+ <linearGradient id="as" y2="100.9" gradientUnits="userSpaceOnUse" x2="151.9" gradientTransform="translate(-143,-64)" y1="103.1" x1="154.1">
+ <stop stop-color="#bbb" offset="0"/>
+ <stop stop-color="#616161" offset="1"/>
+ </linearGradient>
+ <linearGradient id="at" y2="92.444" gradientUnits="userSpaceOnUse" x2="153" gradientTransform="translate(-143,-64)" y1="95.557" x1="153">
+ <stop stop-color="#fff" offset="0"/>
+ <stop stop-color="#ddd" offset="1"/>
+ </linearGradient>
+ <linearGradient id="au" y2="92.9" gradientUnits="userSpaceOnUse" x2="151.9" gradientTransform="translate(-143,-64)" y1="95.101" x1="154.1">
+ <stop stop-color="#bbb" offset="0"/>
+ <stop stop-color="#616161" offset="1"/>
+ </linearGradient>
+ <linearGradient id="av" y2="84.444" gradientUnits="userSpaceOnUse" x2="153" gradientTransform="translate(-143,-64)" y1="87.557" x1="153">
+ <stop stop-color="#fff" offset="0"/>
+ <stop stop-color="#ddd" offset="1"/>
+ </linearGradient>
+ <linearGradient id="aw" y2="84.9" gradientUnits="userSpaceOnUse" x2="151.9" gradientTransform="translate(-143,-64)" y1="87.101" x1="154.1">
+ <stop stop-color="#bbb" offset="0"/>
+ <stop stop-color="#616161" offset="1"/>
+ </linearGradient>
+ <linearGradient id="ax" y2="76.444" gradientUnits="userSpaceOnUse" x2="153" gradientTransform="translate(-143,-64)" y1="79.557" x1="153">
+ <stop stop-color="#fff" offset="0"/>
+ <stop stop-color="#ddd" offset="1"/>
+ </linearGradient>
+ <linearGradient id="ay" y2="76.9" gradientUnits="userSpaceOnUse" x2="151.9" gradientTransform="translate(-143,-64)" y1="79.101" x1="154.1">
+ <stop stop-color="#bbb" offset="0"/>
+ <stop stop-color="#616161" offset="1"/>
+ </linearGradient>
+ <linearGradient id="bd" y2="99.255" gradientUnits="userSpaceOnUse" x2="91.229" gradientTransform="matrix(.42792 0 0 .39077 -4.2833 -.67735)" y1="106.41" x1="98.617">
+ <stop stop-color="#a2a2a2" offset="0"/>
+ <stop stop-color="#fff" offset="1"/>
+ </linearGradient>
+ <radialGradient id="az" gradientUnits="userSpaceOnUse" cy="112.3" cx="102" gradientTransform="matrix(.42792 0 0 .39064 -4.2833 -.67617)" r="139.56">
+ <stop stop-color="#b7b8b9" offset="0"/>
+ <stop stop-color="#ececec" offset=".18851"/>
+ <stop stop-color="#fafafa" offset=".25718"/>
+ <stop stop-color="#fff" offset=".30111"/>
+ <stop stop-color="#fafafa" offset=".53130"/>
+ <stop stop-color="#ebecec" offset=".84490"/>
+ <stop stop-color="#e1e2e3" offset="1"/>
+ </radialGradient>
+ <linearGradient id="ar">
+ <stop stop-color="#3e3e3e" offset="0"/>
+ <stop stop-color="#828282" offset=".5"/>
+ <stop stop-color="#3c3c3c" offset="1"/>
+ </linearGradient>
+ <linearGradient id="aq">
+ <stop stop-color="#999" offset="0"/>
+ <stop stop-color="#fff" offset=".5"/>
+ <stop stop-color="#777" offset="1"/>
+ </linearGradient>
+ <linearGradient id="be" y2="31.211" gradientUnits="userSpaceOnUse" x2="23.576" gradientTransform="matrix(.64407 -.64045 .66092 .65072 61.423 86.661)" y1="25.357" x1="23.576">
+ <stop offset="0"/>
+ <stop stop-color="#c3c3c3" offset=".13483"/>
+ <stop stop-color="#8c8c8c" offset=".20224"/>
+ <stop stop-color="#fff" offset=".26966"/>
+ <stop stop-color="#757575" offset=".4465"/>
+ <stop stop-color="#7d7d7d" offset=".57114"/>
+ <stop stop-color="#b6b6b6" offset=".72038"/>
+ <stop offset="1"/>
+ </linearGradient>
+ <linearGradient id="bf" y2="30" xlink:href="#ar" gradientUnits="userSpaceOnUse" x2="30.038" gradientTransform="matrix(.62586 -.62234 .77349 .76155 59.298 83.616)" y1="24.99" x1="30.038"/>
+ <linearGradient id="bg" y2="30" xlink:href="#aq" gradientUnits="userSpaceOnUse" x2="30.038" gradientTransform="matrix(.62586 -.62234 .77349 .76155 59.119 83.794)" y1="24.99" x1="30.038"/>
+ <linearGradient id="bh" y2="30" xlink:href="#ar" gradientUnits="userSpaceOnUse" x2="30.038" gradientTransform="matrix(.62586 -.62234 .77349 .76155 59.921 82.996)" y1="24.99" x1="30.038"/>
+ <linearGradient id="bi" y2="30" xlink:href="#aq" gradientUnits="userSpaceOnUse" x2="30.038" gradientTransform="matrix(.62586 -.62234 .77349 .76155 59.742 83.175)" y1="24.99" x1="30.038"/>
+ <linearGradient id="bj" y2="30" xlink:href="#ar" gradientUnits="userSpaceOnUse" x2="30.038" gradientTransform="matrix(.62586 -.62234 .77349 .76155 60.547 82.374)" y1="24.99" x1="30.038"/>
+ <linearGradient id="bk" y2="30" xlink:href="#aq" gradientUnits="userSpaceOnUse" x2="30.038" gradientTransform="matrix(.62586 -.62234 .77349 .76155 60.367 82.552)" y1="24.99" x1="30.038"/>
+ <linearGradient id="bl" y2="26.03" gradientUnits="userSpaceOnUse" x2="9" gradientTransform="matrix(.62586 -.62234 .77349 .76155 59.06 83.852)" y1="29.057" x1="9">
+ <stop stop-color="#e4db7b" offset="0"/>
+ <stop stop-color="#f4f0c8" offset="1"/>
+ </linearGradient>
+ <linearGradient id="bm" y2="41.392" gradientUnits="userSpaceOnUse" x2="9.5221" gradientTransform="matrix(.52586 0 0 .51993 81.027 79.545)" y1="37.372" x1="5.5179">
+ <stop stop-color="#cbbd27" offset="0"/>
+ <stop stop-color="#9b901d" offset="1"/>
+ </linearGradient>
+ </defs>
+ <path opacity=".5" d="m16 8v112h63.187c0.000003 0 11.906-9.9062 17.406-15.406 5.497-5.488 15.407-17.394 15.407-17.394v-79.188h-96z" transform="matrix(.44575 0 0 .40124 -5.4244 -1.1519)" filter="url(#bb)"/>
+ <path fill="#fff" d="m2.5634 2.4488v43.767h27.022l14.059-12.839v-30.928h-41.08z"/>
+ <path fill="url(#az)" d="m3.4193 2.8396c-0.23578 0-0.42792 0.1754-0.42792 0.39064v42.189c0 0.21563 0.19214 0.39064 0.42792 0.39064h25.321c0.11254 0 1.1822 0.04616 1.2618-0.02689l13.089-12.036c0.08002-0.07305 0.12538-1.0491 0.12538-1.1519v-29.365c0-0.21524-0.19171-0.39064-0.42792-0.39064h-39.369z"/>
+ <g stroke-linejoin="round" fill-rule="evenodd" transform="matrix(.82576 0 0 .64612 -28.052 -37.166)" stroke="#d3d7cf" stroke-linecap="round" stroke-width="1.3337px" fill="#d3d7cf">
+ <path d="m43.101 70.118"/>
+ <path d="m47.211 70.118"/>
+ <path d="m51.321 70.118"/>
+ <path d="m55.431 70.118"/>
+ <path d="m59.541 70.118"/>
+ <path d="m63.651 70.118"/>
+ <path d="m67.761 70.118"/>
+ <path d="m71.871 70.118"/>
+ <path d="m75.981 70.118"/>
+ <path d="m80.091 70.118"/>
+ <path d="m84.201 70.118"/>
+ <path d="m43.101 65.009"/>
+ <path d="m47.211 65.009"/>
+ <path d="m51.321 65.009"/>
+ <path d="m55.431 65.009"/>
+ <path d="m59.541 65.009"/>
+ <path d="m63.651 65.009"/>
+ <path d="m67.761 65.009"/>
+ <path d="m71.871 65.009"/>
+ <path d="m75.981 65.009"/>
+ <path d="m80.091 65.009"/>
+ <path d="m84.201 65.009"/>
+ <path d="m43.101 85.443"/>
+ <path d="m47.211 85.443"/>
+ <path d="m51.321 85.443"/>
+ <path d="m55.431 85.443"/>
+ <path d="m59.541 85.443"/>
+ <path d="m63.651 85.443"/>
+ <path d="m67.761 85.443"/>
+ <path d="m71.871 85.443"/>
+ <path d="m75.981 85.443"/>
+ <path d="m80.091 85.443"/>
+ <path d="m84.201 85.443"/>
+ <path d="m43.101 75.226"/>
+ <path d="m47.211 75.226"/>
+ <path d="m51.321 75.226"/>
+ <path d="m55.431 75.226"/>
+ <path d="m59.541 75.226"/>
+ <path d="m63.651 75.226"/>
+ <path d="m67.761 75.226"/>
+ <path d="m71.871 75.226"/>
+ <path d="m75.981 75.226"/>
+ <path d="m80.091 75.226"/>
+ <path d="m84.201 75.226"/>
+ <path d="m43.101 90.551"/>
+ <path d="m47.211 90.551"/>
+ <path d="m51.321 90.551"/>
+ <path d="m55.431 90.551"/>
+ <path d="m59.541 90.551"/>
+ <path d="m63.651 90.551"/>
+ <path d="m67.761 90.551"/>
+ <path d="m71.871 90.551"/>
+ <path d="m75.981 90.551"/>
+ <path d="m80.091 90.551"/>
+ <path d="m84.201 90.551"/>
+ <path d="m43.101 80.335"/>
+ <path d="m47.211 80.335"/>
+ <path d="m51.321 80.335"/>
+ <path d="m55.431 80.335"/>
+ <path d="m59.541 80.335"/>
+ <path d="m63.651 80.335"/>
+ <path d="m67.761 80.335"/>
+ <path d="m71.871 80.335"/>
+ <path d="m75.981 80.335"/>
+ <path d="m80.091 80.335"/>
+ <path d="m84.201 80.335"/>
+ <path d="m43.101 95.66"/>
+ <path d="m47.211 95.66"/>
+ <path d="m51.321 95.66"/>
+ <path d="m55.431 95.66"/>
+ <path d="m59.541 95.66"/>
+ <path d="m63.651 95.66"/>
+ <path d="m67.761 95.66"/>
+ <path d="m71.871 95.66"/>
+ <path d="m75.981 95.66"/>
+ <path d="m80.091 95.66"/>
+ <path d="m84.201 95.66"/>
+ <path d="m43.101 100.77"/>
+ <path d="m47.211 100.77"/>
+ <path d="m51.321 100.77"/>
+ <path d="m55.431 100.77"/>
+ <path d="m59.541 100.77"/>
+ <path d="m63.651 100.77"/>
+ <path d="m67.761 100.77"/>
+ <path d="m71.871 100.77"/>
+ <path d="m75.981 100.77"/>
+ <path d="m80.091 100.77"/>
+ <path d="m84.201 100.77"/>
+ <path d="m43.101 105.88"/>
+ <path d="m47.211 105.88"/>
+ <path d="m51.321 105.88"/>
+ <path d="m55.431 105.88"/>
+ <path d="m59.541 105.88"/>
+ <path d="m63.651 105.88"/>
+ <path d="m67.761 105.88"/>
+ <path d="m71.871 105.88"/>
+ <path d="m75.981 105.88"/>
+ <path d="m80.091 105.88"/>
+ <path d="m84.201 105.88"/>
+ <path d="m43.101 116.1"/>
+ <path d="m47.211 116.1"/>
+ <path d="m51.321 116.1"/>
+ <path d="m55.431 116.1"/>
+ <path d="m59.541 116.1"/>
+ <path d="m63.651 116.1"/>
+ <path d="m67.761 116.1"/>
+ <path d="m71.871 116.1"/>
+ <path d="m75.981 116.1"/>
+ <path d="m80.091 116.1"/>
+ <path d="m43.101 110.99"/>
+ <path d="m47.211 110.99"/>
+ <path d="m51.321 110.99"/>
+ <path d="m55.431 110.99"/>
+ <path d="m59.541 110.99"/>
+ <path d="m63.651 110.99"/>
+ <path d="m67.761 110.99"/>
+ <path d="m71.871 110.99"/>
+ <path d="m75.981 110.99"/>
+ <path d="m80.091 110.99"/>
+ <path d="m84.201 110.99"/>
+ <path d="m43.101 121.21"/>
+ <path d="m47.211 121.21"/>
+ <path d="m51.321 121.21"/>
+ <path d="m55.431 121.21"/>
+ <path d="m59.541 121.21"/>
+ <path d="m63.651 121.21"/>
+ <path d="m67.761 121.21"/>
+ <path d="m71.871 121.21"/>
+ <path d="m75.981 121.21"/>
+ <path d="m43.101 126.31"/>
+ <path d="m47.211 126.31"/>
+ <path d="m51.321 126.31"/>
+ <path d="m55.431 126.31"/>
+ <path d="m59.541 126.31"/>
+ <path d="m63.651 126.31"/>
+ <path d="m67.761 126.31"/>
+ <path d="m71.871 126.31"/>
+ </g>
+ <path opacity=".4" d="m41.88 115.98 24.31-24.31s-9.3531 2.9131-19.603 2.9131c0 10.25-4.7065 21.396-4.7065 21.396z" clip-path="url(#ba)" transform="matrix(.42792 0 0 .39077 12.834 -.67735)" filter="url(#bc)"/>
+ <g transform="matrix(.42793 0 0 .39077 -48.788 13.39)">
+ <g transform="translate(116,-32)">
+ <circle cy="110" cx="10" r="2" fill="url(#bn)"/>
+ <circle cy="110" cx="10" r="1.556" fill="url(#bo)"/>
+ </g>
+ <g transform="translate(116,-32)">
+ <circle cy="102" cx="10" r="2" fill="url(#bp)"/>
+ <circle cy="102" cx="10" r="1.556" fill="url(#bq)"/>
+ </g>
+ <g transform="translate(116,-32)">
+ <circle cy="94" cx="10" r="2" fill="url(#br)"/>
+ <circle cy="94" cx="10" r="1.556" fill="url(#bs)"/>
+ </g>
+ <g transform="translate(116,-32)">
+ <circle cy="86" cx="10" r="2" fill="url(#bt)"/>
+ <circle cy="86" cx="10" r="1.556" fill="url(#bu)"/>
+ </g>
+ <g transform="translate(116,-32)">
+ <circle cy="78" cx="10" r="2" fill="url(#bv)"/>
+ <circle cy="78" cx="10" r="1.556" fill="url(#bw)"/>
+ </g>
+ <g transform="translate(116,-32)">
+ <circle cy="70" cx="10" r="2" fill="url(#bx)"/>
+ <circle cy="70" cx="10" r="1.556" fill="url(#by)"/>
+ </g>
+ <g transform="translate(116,-32)">
+ <circle cy="62" cx="10" r="2" fill="url(#bz)"/>
+ <circle cy="62" cx="10" r="1.556" fill="url(#ca)"/>
+ </g>
+ <g transform="translate(116,-32)">
+ <circle cy="54" cx="10" r="2" fill="url(#cb)"/>
+ <circle cy="54" cx="10" r="1.556" fill="url(#cc)"/>
+ </g>
+ <g transform="translate(116,-32)">
+ <circle cy="46" cx="10" r="2" fill="url(#cd)"/>
+ <circle cy="46" cx="10" r="1.556" fill="url(#ce)"/>
+ </g>
+ <g transform="translate(116,-32)">
+ <circle cy="38" cx="10" r="2" fill="url(#cf)"/>
+ <circle cy="38" cx="10" r="1.556" fill="url(#as)"/>
+ </g>
+ <g transform="translate(116,-32)">
+ <circle cy="30" cx="10" r="2" fill="url(#at)"/>
+ <circle cy="30" cx="10" r="1.556" fill="url(#au)"/>
+ </g>
+ <g transform="translate(116,-32)">
+ <circle cy="22" cx="10" r="2" fill="url(#av)"/>
+ <circle cy="22" cx="10" r="1.556" fill="url(#aw)"/>
+ </g>
+ <g transform="translate(116,-32)">
+ <circle cy="14" cx="10" r="2" fill="url(#ax)"/>
+ <circle cy="14" cx="10" r="1.556" fill="url(#ay)"/>
+ </g>
+ </g>
+ <path stroke-linejoin="round" style="color:#000000" d="m15.884 33.281h-6.1275" fill-rule="evenodd" fill-opacity=".75" stroke="#204a87" stroke-linecap="round" stroke-width="1.399" fill="#4e9a06"/>
+ <path stroke-linejoin="round" style="color:#000000" d="m15.884 27.147v12.267" fill-rule="evenodd" fill-opacity=".75" stroke="#4e9a06" stroke-linecap="round" stroke-width="1.399" fill="#4e9a06"/>
+ <path stroke-linejoin="round" style="color:#000000" d="m20.99 39.414v3.6801" fill-rule="evenodd" fill-opacity=".75" stroke="#204a87" stroke-linecap="round" stroke-width="1.399" fill="#4e9a06"/>
+ <path stroke-linejoin="round" style="color:#000000" d="m20.99 39.414-5.106-3.68" fill-rule="evenodd" fill-opacity=".75" stroke="#4e9a06" stroke-linecap="round" stroke-width="1.399" fill="#4e9a06"/>
+ <path stroke-linejoin="round" style="color:#000000" d="m23.033 43.094h-4.085" fill-rule="evenodd" fill-opacity=".75" stroke="#4e9a06" stroke-linecap="round" stroke-width="1.399" fill="#4e9a06"/>
+ <path stroke-linejoin="round" style="color:#000000" d="m20.99 27.147v-7.36" fill-rule="evenodd" fill-opacity=".75" stroke="#204a87" stroke-linecap="round" stroke-width="1.399" fill="#4e9a06"/>
+ <path stroke-linejoin="round" style="color:#000000" d="m20.99 27.147-5.1063 3.6801" fill-rule="evenodd" fill-opacity=".75" stroke="#4e9a06" stroke-linecap="round" stroke-width="1.399" fill="#4e9a06"/>
+ <path stroke-linejoin="round" style="color:#000000" d="m20.99 7.5199v3.6801" fill-rule="evenodd" fill-opacity=".75" stroke="#204a87" stroke-linecap="round" stroke-width="1.399" fill="#4e9a06"/>
+ <path stroke-linejoin="round" style="color:#000000" d="m22.011 19.787h-2.043v-8.587h2.0425v8.587" stroke="#4e9a06" stroke-linecap="round" stroke-width="1.399" fill="none"/>
+ <path stroke-linejoin="round" style="color:#000000" d="m20.99 23.557h8.17" fill-rule="evenodd" fill-opacity=".75" stroke="#204a87" stroke-linecap="round" stroke-width="1.399" fill="#4e9a06"/>
+ <path stroke-linejoin="round" style="color:#000000" d="m9.7564 12.427h5.1063" stroke="#888a85" stroke-linecap="round" stroke-width="1.399" fill="none"/>
+ <path stroke-linejoin="round" style="color:#000000" d="m9.7564 14.88h5.1063" stroke="#888a85" stroke-linecap="round" stroke-width="1.399" fill="none"/>
+ <path stroke-linejoin="round" style="color:#000000" d="m24.054 32.054h5.1063" stroke="#888a85" stroke-linecap="round" stroke-width="1.399" fill="none"/>
+ <path stroke-linejoin="round" style="color:#000000" d="m24.054 34.507h5.1063" stroke="#888a85" stroke-linecap="round" stroke-width="1.399" fill="none"/>
+ <path style="color:#000000" fill-rule="evenodd" fill="#4e9a06" d="m20.99 39.414-2.234-3.45-1.149 2.99 3.383 0.46z"/>
+ <path fill="#a40000" d="m9.9459 33.281a1.2631 1.2453 0 0 1 -2.5262 0 1.2631 1.2453 0 1 1 2.5262 0z"/>
+ <path stroke-linejoin="round" style="color:#000000" d="m21.001 7.1011h8.17" fill-rule="evenodd" fill-opacity=".75" stroke="#204a87" stroke-linecap="round" stroke-width="1.399" fill="#4e9a06"/>
+ <path fill="#a40000" d="m31.492 23.557a1.2631 1.2453 0 0 1 -2.5262 0 1.2631 1.2453 0 1 1 2.5262 0z"/>
+ <path fill="#a40000" d="m31.43 7.1011a1.2631 1.2453 0 0 1 -2.5262 0 1.2631 1.2453 0 1 1 2.5262 0z"/>
+ <path fill="url(#bd)" d="m29.596 46.215s5.0982-3.8741 7.4518-6.0234c2.3536-2.1492 6.596-6.8049 6.596-6.8049s-5.8839 3.4497-10.27 3.4497c0 4.0054-3.7777 9.3785-3.7777 9.3785z"/>
+ <g transform="matrix(1.1923 -.15242 .16247 1.1185 -91.73 -61.864)">
+ <path stroke-linejoin="round" d="m85.365 96.011c0.43282-0.32935 1.5837 0.20459 2.6183 1.2232 1.0321 1.0162 1.5508 2.1286 1.225 2.5608-0.0012 0.0017 0.02544 0.02217 0.02417 0.0238l15.708-15.618c0.39874-0.3965-0.14267-1.5725-1.2101-2.6235-1.0674-1.0509-2.2586-1.5808-2.6574-1.1843l-15.705 15.618z" stroke="#000" stroke-width=".55533" fill="url(#be)"/>
+ <path opacity=".8" stroke-linejoin="round" d="m99.153 82.3c0.43282-0.32935 1.5837 0.20459 2.6183 1.2232 1.0321 1.0162 1.5508 2.1285 1.225 2.5608-0.001 0.0017 0.0254 0.02217 0.0242 0.0238l1.9167-1.9059 0.0391-0.0389c0.001-0.0017-0.0254-0.02215-0.0242-0.0238 0.32575-0.43223-0.19292-1.5446-1.225-2.5608-1.0346-1.0186-2.1854-1.5525-2.6183-1.2232l-0.04 0.039-1.917 1.906z" stroke="#dd78c5" stroke-width=".55533" fill="#ff9de8"/>
+ <path opacity=".6" d="m85.365 96.011c0.43282-0.32935 1.5837 0.20459 2.6183 1.2232 1.0321 1.0162 1.5508 2.1286 1.225 2.5608-0.0012 0.0017 0.02544 0.02217 0.02417 0.0238l10.816-10.755 0.0391-0.0389c0.001-0.0017-0.0254-0.02216-0.0242-0.0238 0.32575-0.43223-0.19293-1.5446-1.225-2.5608-1.0346-1.0186-2.1854-1.5525-2.6183-1.2232l-0.03911 0.0389-10.816 10.755z"/>
+ <path fill="url(#bf)" d="m97.005 84.436c0.43282-0.32935 1.5837 0.20459 2.6183 1.2232 1.0321 1.0162 1.5508 2.1286 1.225 2.5608-0.001 0.0017 0.0254 0.02217 0.0242 0.0238l0.19558-0.19448c0.001-0.0017-0.0254-0.02215-0.0242-0.0238 0.32575-0.43222-0.19292-1.5446-1.225-2.5608-1.0346-1.0186-2.1854-1.5525-2.6183-1.2232l-0.197 0.194z"/>
+ <path fill="url(#bg)" d="m96.826 84.614c0.43282-0.32935 1.5837 0.20459 2.6183 1.2232 1.0321 1.0162 1.5508 2.1285 1.225 2.5608-0.001 0.0017 0.0254 0.02217 0.0242 0.0238l0.19558-0.19448c0.001-0.0017-0.0254-0.02215-0.0242-0.0238 0.32575-0.43222-0.19292-1.5446-1.225-2.5608-1.0346-1.0186-2.1854-1.5525-2.6183-1.2232l-0.196 0.194z"/>
+ <path fill="url(#bh)" d="m97.628 83.817c0.43282-0.32935 1.5837 0.20459 2.6183 1.2232 1.0321 1.0162 1.5508 2.1286 1.225 2.5608-0.001 0.0017 0.0254 0.02217 0.0242 0.0238l0.19558-0.19448c0.001-0.0017-0.0254-0.02215-0.0242-0.0238 0.32575-0.43222-0.19292-1.5446-1.225-2.5608-1.0346-1.0186-2.1854-1.5525-2.6183-1.2232l-0.204 0.194z"/>
+ <path fill="url(#bi)" d="m97.448 83.995c0.43282-0.32935 1.5837 0.20459 2.6183 1.2232 1.0321 1.0162 1.5508 2.1286 1.225 2.5608-0.001 0.0017 0.0254 0.02217 0.0242 0.0238l0.19558-0.19448c0.001-0.0017-0.0254-0.02215-0.0242-0.0238 0.32575-0.43222-0.19292-1.5446-1.225-2.5608-1.0346-1.0186-2.1854-1.5525-2.6183-1.2232l-0.204 0.194z"/>
+ <path fill="url(#bj)" d="m98.254 83.194c0.43282-0.32935 1.5837 0.20459 2.6183 1.2232 1.0321 1.0162 1.5508 2.1286 1.225 2.5608-0.001 0.0017 0.0254 0.02217 0.0242 0.0238l0.19558-0.19448c0.001-0.0017-0.0254-0.02216-0.0242-0.0238 0.32575-0.43223-0.19292-1.5446-1.225-2.5608-1.0346-1.0186-2.1854-1.5525-2.6183-1.2232l-0.198 0.194z"/>
+ <path fill="url(#bk)" d="m98.074 83.372c0.43282-0.32935 1.5837 0.20459 2.6183 1.2232 1.0321 1.0162 1.5508 2.1286 1.225 2.5608-0.001 0.0017 0.0254 0.02217 0.0242 0.0238l0.19558-0.19448c0.001-0.0017-0.0254-0.02216-0.0242-0.0238 0.32575-0.43223-0.19292-1.5446-1.225-2.5608-1.0346-1.0186-2.1854-1.5525-2.6183-1.2232l-0.198 0.194z"/>
+ <path d="m82.185 102.91 7.0294-3.0074c0.32575-0.43222-0.20033-1.6548-1.2324-2.671-1.0346-1.0186-2.2398-1.5494-2.6726-1.22l-3.124 6.898z" fill-rule="evenodd" stroke="url(#bm)" stroke-width=".55533" fill="url(#bl)"/>
+ <path d="m83.046 101.04-0.86146 1.8612 1.9008-0.83782c-0.14196-0.17111-0.2723-0.3455-0.44432-0.51486-0.19804-0.19498-0.39436-0.35167-0.59506-0.50847z" fill-rule="evenodd" stroke="#000" stroke-width=".55533"/>
+ </g>
+</svg>
diff --git a/resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-gerbview-project.svg b/resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-gerbview-project.svg
new file mode 100644
index 0000000..a60a930
--- /dev/null
+++ b/resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-gerbview-project.svg
@@ -0,0 +1,109 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs>
+ <linearGradient id="ab" y2="4.2193" gradientUnits="userSpaceOnUse" x2="7.5955" gradientTransform="matrix(.88924 0 0 .78227 .62292 7.6472)" y1="43.994" x1="40.752">
+ <stop stop-color="#333" offset="0"/>
+ <stop stop-color="#474747" offset="1"/>
+ </linearGradient>
+ <linearGradient id="a" y2="19500" gradientUnits="userSpaceOnUse" x2="15000" y1="14100" x1="10100">
+ <stop stop-color="#f5fffa" offset="0"/>
+ <stop stop-color="#6f6d7f" offset="1.2"/>
+ </linearGradient>
+ <linearGradient id="b" y2="19500" gradientUnits="userSpaceOnUse" x2="15000" y1="14100" x1="10100">
+ <stop stop-color="#fff" stop-opacity="0.698" offset="0"/>
+ <stop stop-color="#5db7ff" stop-opacity="0.698" offset="1.2"/>
+ </linearGradient>
+ <linearGradient id="x" y2="19500" gradientUnits="userSpaceOnUse" x2="15000" y1="14100" x1="10100">
+ <stop stop-color="#f5ffff" offset="0"/>
+ <stop stop-color="#fff" stop-opacity="0" offset="1.2"/>
+ </linearGradient>
+ <radialGradient id="ac" xlink:href="#b" gradientUnits="userSpaceOnUse" cy="17526" cx="18632" gradientTransform="matrix(.001928 0 0 .001928 33.623 -11.394)" r="40170"/>
+ <radialGradient id="ad" xlink:href="#b" gradientUnits="userSpaceOnUse" cy="17831" cx="18969" r="23166"/>
+ <radialGradient id="ae" xlink:href="#a" gradientUnits="userSpaceOnUse" cy="27370" cx="25538" gradientTransform="translate(-1.2031e-5,1.3252e-4)" r="3682.2"/>
+ <radialGradient id="af" xlink:href="#x" gradientUnits="userSpaceOnUse" cy="17398" cx="18401" gradientTransform="translate(-7.3214e-5,-4.3295e-5)" r="11888"/>
+ <radialGradient id="ag" xlink:href="#a" gradientUnits="userSpaceOnUse" cy="15674" cx="16467" gradientTransform="matrix(.0019151 0 0 .0019411 33.623 -11.394)" r="3055.2"/>
+ <radialGradient id="ah" xlink:href="#b" gradientUnits="userSpaceOnUse" cy="17753" cx="18300" gradientTransform="matrix(.001928 0 0 .001928 33.623 -11.394)" r="28430"/>
+ <radialGradient id="ai" xlink:href="#a" gradientUnits="userSpaceOnUse" cy="33086" cx="-2182.7" gradientTransform="matrix(.0032733 0 0 .0011356 31.832 15.718)" r="11540"/>
+ <radialGradient id="aj" xlink:href="#a" gradientUnits="userSpaceOnUse" cy="18596" cx="17070" gradientTransform="matrix(.0019519 0 0 .0019044 33.623 -11.394)" r="3734.6"/>
+ <linearGradient id="ao" y2="39368" xlink:href="#x" gradientUnits="userSpaceOnUse" x2="2312.5" gradientTransform="matrix(.0032733 0 0 .0011356 31.832 15.718)" y1="39560" x1="-1083.8"/>
+ <radialGradient id="ak" xlink:href="#b" gradientUnits="userSpaceOnUse" cy="11246" cx="13190" gradientTransform="translate(-9.4474e-5,4.0964e-5)" r="16248"/>
+ <linearGradient id="ap" y2="18951" xlink:href="#a" gradientUnits="userSpaceOnUse" x2="12508" gradientTransform="matrix(.001928 0 0 .001928 33.623 -11.394)" y1="-85.143" x1="-1308.3"/>
+ <radialGradient id="al" xlink:href="#a" gradientUnits="userSpaceOnUse" cy="29167" cx="26631" gradientTransform="translate(-1.1897e-4,-1.2562e-4)" r="4284.2"/>
+ <linearGradient id="y" y2="20482" gradientUnits="userSpaceOnUse" x2="6753.4" gradientTransform="matrix(.0013312 0 0 .0027924 31.832 15.718)" y1="27356" x1="850.32">
+ <stop stop-color="#a66500" offset="0"/>
+ <stop stop-color="#da8f00" offset="1"/>
+ </linearGradient>
+ <linearGradient id="z" y2="21261" gradientUnits="userSpaceOnUse" x2="3741.1" gradientTransform="matrix(.00126 0 0 .0029504 31.832 15.718)" y1="21261" x1="-3478.9">
+ <stop stop-color="#eda700" offset="0"/>
+ <stop stop-color="#fff" offset=".38824"/>
+ <stop stop-color="#ffcb50" offset="0.7"/>
+ <stop stop-color="#ed8700" offset="1"/>
+ </linearGradient>
+ <linearGradient id="aa" y2="29618" xlink:href="#a" gradientUnits="userSpaceOnUse" x2="24590" gradientTransform="matrix(.001928 0 0 .001928 33.623 -11.394)" y1="405.03" x1="389.4"/>
+ <radialGradient id="am" gradientUnits="userSpaceOnUse" cy="10444" cx="11715" gradientTransform="matrix(.001928 0 0 .001928 33.623 -11.394)" r="15132">
+ <stop stop-color="#f5ffff" stop-opacity=".49020" offset="0"/>
+ <stop stop-color="#fff" stop-opacity="0" offset="1.2"/>
+ </radialGradient>
+ </defs>
+ <g id="w" stroke-linejoin="round" stroke-linecap="round" transform="translate(-40.995 -.010729)">
+ <rect style="color:#000000" fill-opacity=".63253" ry=".89887" height="32.011" width="31.012" stroke="url(#ab)" display="block" y="10.5" x="6.495" fill="#fff"/>
+ <rect opacity=".79121" style="color:#000000" display="block" rx=".13187" ry=".11482" height="30.004" width="28.998" stroke="#fff" y="11.496" x="7.5" fill="none"/>
+ <path style="color:#000000" d="m10.184 13.468 5.316 0.032 3 3v8" stroke="#000" display="block" fill="none"/>
+ <path style="color:#000000" d="m22.5 24.5v-8l-3-3" stroke="#000" display="block" fill="none"/>
+ <path style="color:#000000" d="m26.5 24.5v-8l-3-3" stroke="#000" display="block" fill="none"/>
+ <path style="color:#000000" d="m30.5 24.5v-11" stroke="#000" display="block" fill="none"/>
+ <path style="color:#000000" d="m18.5 24.5v2" stroke="#000" stroke-width="2" display="block" fill="none"/>
+ <path style="color:#000000" d="m22.5 24.5v2" stroke="#000" stroke-width="2" display="block" fill="none"/>
+ <path style="color:#000000" d="m26.5 24.5v2" stroke="#000" stroke-width="2" display="block" fill="none"/>
+ <path style="color:#000000" d="m30.5 24.5v2" stroke="#000" stroke-width="2" display="block" fill="none"/>
+ <path style="color:#000000" d="m30.5 31.5v2" stroke="#000" stroke-width="2" display="block" fill="none"/>
+ <path style="color:#000000" d="m26.5 31.5v2" stroke="#000" stroke-width="2" display="block" fill="none"/>
+ <path style="color:#000000" d="m22.5 31.5v2" stroke="#000" stroke-width="2" display="block" fill="none"/>
+ <path style="color:#000000" d="m18.5 31.5v2" stroke="#000" stroke-width="2" display="block" fill="none"/>
+ <path style="color:#000000" d="m18.5 33.5v3l-2 2h-6" stroke="#000" display="block" fill="none"/>
+ <path style="color:#000000" d="m22.5 33.5v5" stroke="#000" display="block" fill="none"/>
+ <path style="color:#000000" d="m26.5 33.5v3l2 2h6" stroke="#000" display="block" fill="none"/>
+ <path style="color:#000000" d="m30.5 33.5 2 2h2" stroke="#000" display="block" fill="none"/>
+ <path style="color:#000000" display="block" d="m10.5 15.5h4l2 2v18l-1 1h-5v-21z" stroke="#000"/>
+ <rect style="color:#000000" display="block" ry="2.028" height="13" width="16" stroke="#000" y="22.5" x="16.5" fill="none"/>
+ </g>
+ <g transform="matrix(1.0331,0,0,1.042,-1.5828,-1.9714)">
+ <use xlink:href="#w" transform="matrix(.88516 .46528 -.46528 .88516 55.782 10.953)" height="48" width="48" y="0" x="0"/>
+ <use xlink:href="#an" transform="matrix(.97367 .22797 -.22797 .97367 9.8015 -7.4372)" height="48" width="48" y="0" x="0"/>
+ <use id="an" xlink:href="#w" transform="translate(37,1)" height="48" width="48" y="0" x="0"/>
+ <path d="m4 26v-13h28v13c-7 6-18-6-28 0z" fill-opacity=".51807" fill-rule="evenodd" fill="#fff"/>
+ </g>
+ <g fill-rule="evenodd" transform="translate(-6,53)" stroke="#000">
+ <path style="color:#000000" d="m31 31a10 10 0 1 1 -20 0 10 10 0 1 1 20 0z" fill-opacity=".3012" transform="translate(-47,-29)" stroke-width="1.5" fill="#72a0cf"/>
+ <rect style="color:#000000" stroke-width="1.0006" transform="matrix(.3824 -.924 .90415 .42722 0 0)" ry="1.1087" width="3.1174" y="-13.376" x="-14.434" height="17.268" fill="#2e3436"/>
+ </g>
+ <g transform="matrix(.69611 .19209 -.17663 .64009 -8.9202 1.1447)">
+ <g opacity=".18470" transform="matrix(.001928 0 0 .001928 33.342 -9.8361)">
+ <path fill-rule="evenodd" d="m29524 27524a2285.7 2285.7 0 1 1 -4571.4 0 2285.7 2285.7 0 1 1 4571.4 0z" transform="matrix(1.0366,0,0,1.0366,-8223.8,-8828.8)"/>
+ <path fill-rule="evenodd" d="m19683 17385c-463.92-572.08-642.91-183.34-885.67-376.28-186.31-167 126.72-430.52-278.63-868.94l-1977.9 1967.3c337.99 333.5 523.59 91.68 730.6 375.12 155.23 225.6-17.11 519.24 482.71 842.41l1928.9-1939.6z"/>
+ <rect fill-rule="evenodd" transform="rotate(-45)" height="1619" width="4666.7" y="23097" x="-2064"/>
+ <path fill-rule="evenodd" d="m19649 9700.3c0 5058-4100.3 9158.4-9158.4 9158.4-5058 0-9158.4-4100.3-9158.4-9158.4 0-5058 4100.3-9158.4 9158.4-9158.4 5058 0 9158.4 4100.3 9158.4 9158.4zm-907.21-0.01c0 4557-3694.2 8251.2-8251.2 8251.2s-8251.1-3694.2-8251.1-8251.2 3694.2-8251.2 8251.1-8251.2c4557 0 8251.2 3694.2 8251.2 8251.2z"/>
+ <path fill-rule="evenodd" d="m29524 27524a2285.7 2285.7 0 1 1 -4571.4 0 2285.7 2285.7 0 1 1 4571.4 0z" transform="matrix(.95833 0 0 .95833 944.44 99.204)"/>
+ <rect transform="rotate(-45)" height="11583" width="5522.2" y="26814" x="-2375"/>
+ </g>
+ <path opacity="0.293" d="m68.252 9.6924c0 6.7349-5.4597 12.195-12.195 12.195-6.7348 0-12.194-5.4597-12.194-12.195 0-6.7348 5.4597-12.195 12.194-12.195 6.7349 0 12.195 5.4597 12.195 12.195zm1.8483-2.402c0 8.9894-7.2873 16.277-16.277 16.277-8.9894 0-16.277-7.2874-16.277-16.277 0-8.9894 7.2874-16.277 16.277-16.277 8.9894 0 16.277 7.2874 16.277 16.277z" fill-rule="evenodd" fill="url(#ac)"/>
+ <path opacity=".68790" d="m19395 9325.5a9226.1 9226.1 0 1 1 -18452 0 9226.1 9226.1 0 1 1 18452 0z" fill-rule="evenodd" transform="matrix(.0017972 0 0 .0017972 36.035 -9.712)" fill="url(#ad)"/>
+ <path d="m29524 27524a2285.7 2285.7 0 1 1 -4571.4 0 2285.7 2285.7 0 1 1 4571.4 0z" fill-rule="evenodd" transform="matrix(.0019986 0 0 .0019986 17.768 -28.417)" fill="#717386"/>
+ <path d="m29524 27524a2285.7 2285.7 0 1 1 -4571.4 0 2285.7 2285.7 0 1 1 4571.4 0z" fill-rule="evenodd" transform="matrix(.0018239 0 0 .0018239 22.527 -23.608)" fill="url(#ae)"/>
+ <path d="m19395 9325.5a9226.1 9226.1 0 1 1 -18452 0 9226.1 9226.1 0 1 1 18452 0z" fill-rule="evenodd" transform="matrix(.0017972 0 0 .0017972 35.852 -9.712)" fill="url(#af)"/>
+ <path fill-rule="evenodd" fill="url(#ag)" d="m71.572 22.125c-0.89446-1.103-1.2396-0.35349-1.7076-0.72548-0.35921-0.32198 0.24432-0.83006-0.53721-1.6754l-3.8136 3.793c0.65166 0.643 1.0095 0.17676 1.4086 0.72325 0.29929 0.43497-0.03299 1.0011 0.93068 1.6242l3.7191-3.7396z"/>
+ <path fill-rule="evenodd" fill="url(#ah)" d="m69.601 7.6645c0 8.3692-6.7354 15.154-15.044 15.154-8.3085 0-15.044-6.7846-15.044-15.154 0-8.3692 6.7354-15.154 15.044-15.154 8.3085 0 15.044 6.7845 15.044 15.154zm1.2321-0.61426c0 9.3909-7.6128 17.004-17.004 17.004-9.3909 0-17.004-7.6128-17.004-17.004 0-9.3909 7.6128-17.004 17.004-17.004 9.3909 0 17.004 7.6128 17.004 17.004z"/>
+ <rect opacity=".9554" fill-rule="evenodd" transform="rotate(-45)" height="3.1216" width="8.9976" y="60.251" x="27.853" fill="url(#ai)"/>
+ <path fill-rule="evenodd" fill="url(#aj)" d="m71.343 22.447c-0.89446-1.103-1.2396-0.35349-1.7076-0.72548-0.35921-0.32198 0.19842-1.1055-0.81263-2.0885l-3.5381 3.4717c0.78938 0.78072 1.5604 0.26858 1.9595 0.81506 0.29929 0.43497-0.03299 1.0011 0.93068 1.6242l3.1682-3.0969z"/>
+ <rect fill-rule="evenodd" transform="rotate(-45)" height="2.5865" width="7.4552" y="60" x="28.624" fill="url(#ao)"/>
+ <path opacity=".21020" d="m13469 7844a3569.2 3569.2 0 1 1 -7138.4 0 3569.2 3569.2 0 1 1 7138.4 0z" fill-rule="evenodd" transform="matrix(.0016277 0 0 .0016277 32.568 -13.102)" fill="url(#ak)"/>
+ <path fill-rule="evenodd" fill="url(#ap)" d="m71.507 7.3083c0 9.7521-7.9056 17.658-17.658 17.658-9.7521 0-17.658-7.9056-17.658-17.658 0-9.7521 7.9056-17.658 17.658-17.658 9.7521 0 17.658 7.9056 17.658 17.658zm-1.7491-0.000017c0 8.7861-7.1225 15.909-15.909 15.909-8.786 0-15.909-7.1225-15.909-15.909 0-8.7861 7.1225-15.909 15.909-15.909 8.7861 0 15.909 7.1225 15.909 15.909z"/>
+ <path d="m29524 27524a2285.7 2285.7 0 1 1 -4571.4 0 2285.7 2285.7 0 1 1 4571.4 0z" fill-rule="evenodd" transform="matrix(.0018477 0 0 .0018477 35.444 -11.203)" fill="#808080"/>
+ <path d="m29524 27524a2285.7 2285.7 0 1 1 -4571.4 0 2285.7 2285.7 0 1 1 4571.4 0z" fill-rule="evenodd" transform="matrix(.0016773 0 0 .0016773 39.925 -6.5347)" fill="url(#al)"/>
+ <rect transform="rotate(-45)" height="22.333" width="10.647" y="67.417" x="27.253" fill="url(#y)"/>
+ <rect transform="rotate(-45)" height="19.934" width="8.513" y="68.617" x="28.321" fill="url(#z)"/>
+ <path fill-rule="evenodd" fill="url(#aa)" d="m70.103 7.1705c0 8.9219-7.2327 16.155-16.155 16.155-8.9219 0-16.155-7.2327-16.155-16.155 0-8.9219 7.2327-16.155 16.155-16.155 8.9219 0 16.155 7.2327 16.155 16.155zm-0.48222 0.000006c0 8.6556-7.0168 15.672-15.672 15.672-8.6556 0-15.672-7.0168-15.672-15.672 0-8.6556 7.0168-15.672 15.672-15.672 8.6556 0 15.672 7.0168 15.672 15.672z"/>
+ <path opacity=".54140" d="m56.503 2.2283c0 3.6148-2.9304 6.5452-6.5451 6.5452-3.6148 0-6.5451-2.9304-6.5451-6.5452s2.9304-6.5451 6.5451-6.5451c3.6148 0 6.5451 2.9304 6.5451 6.5451zm1.1483-0.68894c0 4.6295-3.7529 8.3824-8.3824 8.3824s-8.3824-3.7529-8.3824-8.3824 3.7529-8.3824 8.3824-8.3824 8.3824 3.7529 8.3824 8.3824z" fill-rule="evenodd" fill="url(#am)"/>
+ <path fill-rule="evenodd" fill="#9a9ba9" d="m67.861 25.319 0.29067 0.23655 3.2223-3.1012-0.17592-0.28246-3.337 3.1471z"/>
+ <path fill-rule="evenodd" fill="#8a92a0" d="m65.338 23.174 0.2678 0.19753 3.4914-3.4229-0.20844-0.32179-3.5507 3.5472z"/>
+ </g>
+</svg>
diff --git a/resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-kicad-project.svg b/resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-kicad-project.svg
new file mode 100644
index 0000000..f4f1e94
--- /dev/null
+++ b/resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-kicad-project.svg
@@ -0,0 +1,276 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ height="26"
+ width="26"
+ version="1.1"
+ id="svg2"
+ inkscape:version="0.48.1 r9760"
+ sodipodi:docname="icon_kicad.svg"
+ inkscape:export-filename="F:\kicad-launchpad\testing\bitmaps_png\sources\icon_kicad.png"
+ inkscape:export-xdpi="60"
+ inkscape:export-ydpi="60">
+ <metadata
+ id="metadata581">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1600"
+ inkscape:window-height="876"
+ id="namedview579"
+ showgrid="true"
+ inkscape:zoom="22.774716"
+ inkscape:cx="23.593222"
+ inkscape:cy="12.172521"
+ inkscape:window-x="0"
+ inkscape:window-y="24"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg2"
+ inkscape:snap-grids="false"
+ inkscape:snap-to-guides="false">
+ <inkscape:grid
+ type="xygrid"
+ id="grid3548"
+ empspacing="5"
+ visible="true"
+ enabled="true"
+ snapvisiblegridlinesonly="true" />
+ </sodipodi:namedview>
+ <defs
+ id="defs4">
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10292-9-2-6-6"
+ id="linearGradient10600-2"
+ gradientUnits="userSpaceOnUse"
+ x1="110.96875"
+ y1="976.29968"
+ x2="110.96875"
+ y2="992.375" />
+ <linearGradient
+ id="linearGradient10292-9-2-6-6"
+ inkscape:collect="always">
+ <stop
+ id="stop10294-9-9-7-8"
+ offset="0"
+ style="stop-color: rgb(102, 102, 102); stop-opacity: 1;" />
+ <stop
+ id="stop10296-6-6-7-4"
+ offset="1"
+ style="stop-color: rgb(51, 51, 51); stop-opacity: 1;" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10125-12"
+ id="linearGradient10596-0"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1,0,0,1.00011,0,-0.10551)"
+ x1="110.96875"
+ y1="976.29968"
+ x2="110.99982"
+ y2="991.87488" />
+ <linearGradient
+ id="linearGradient10125-12">
+ <stop
+ style="stop-color: rgb(201, 156, 0); stop-opacity: 1;"
+ offset="0"
+ id="stop10127-04" />
+ <stop
+ style="stop-color: rgb(135, 105, 0); stop-opacity: 1;"
+ offset="1"
+ id="stop10129-4" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10125-12"
+ id="linearGradient10598-1"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.00399,0,0,1.00959,-1.1658,-9.36636)"
+ x1="110.96875"
+ y1="976.29968"
+ x2="110.96875"
+ y2="992.375" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient10286-3"
+ id="linearGradient10602-2"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.99213,0,0,3.00466,-933.712,-1623.08)"
+ x1="294.3429"
+ y1="256.58133"
+ x2="294.41818"
+ y2="243.13852" />
+ <linearGradient
+ id="linearGradient10286-3">
+ <stop
+ id="stop10288-8"
+ offset="0"
+ style="stop-color: rgb(179, 179, 179); stop-opacity: 1;" />
+ <stop
+ id="stop10290-7"
+ offset="1"
+ style="stop-color: rgb(230, 230, 230); stop-opacity: 1;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient15606-1">
+ <stop
+ style="stop-color: rgb(255, 255, 255); stop-opacity: 0.588235;"
+ offset="0"
+ id="stop15608-0" />
+ <stop
+ style="stop-color: rgb(255, 255, 255); stop-opacity: 0.862745;"
+ offset="1"
+ id="stop15610-5" />
+ </linearGradient>
+ <linearGradient
+ y2="-781.62268"
+ x2="209.0625"
+ y1="-765.46082"
+ x1="209.0625"
+ gradientTransform="matrix(1.5,0,0,1.5,-368,281.32)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient8673"
+ xlink:href="#linearGradient15606-1"
+ inkscape:collect="always" />
+ </defs>
+ <g
+ style="display:inline"
+ id="g10552"
+ transform="matrix(0.59915384,0,0,0.51444411,36.445696,461.74209)">
+ <g
+ transform="matrix(2.9999,0,0,2.99884,-391.989,-3823.61)"
+ id="g10554"
+ style="fill:url(#linearGradient10600-2);fill-opacity:1;display:inline">
+ <path
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccccccccccc"
+ id="path10556"
+ d="m 111.8125,976.3733 c -0.4584,0.0875 -0.82072,0.53346 -0.8125,1.00011 l 0,14.00148 c 5e-5,0.52364 0.47642,1.00006 1,1.00011 l 11.33333,0 c 0.52358,-5e-5 0.99995,-0.47647 1,-1.00011 l 0,-8.73078 c 0.006,-0.26409 -0.0975,-0.52903 -0.28125,-0.71883 l -5.27042,-5.2707 c -0.18978,-0.18372 -0.45469,-0.2874 -0.71875,-0.28128 l -6.06291,0 c -0.0623,-0.006 -0.12518,-0.006 -0.1875,0 z"
+ style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0pt;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;text-anchor:start;color:#000000;fill:url(#linearGradient10596-0);fill-opacity:1;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;font-family:Andale Mono" />
+ <path
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccccccc"
+ id="path10558"
+ d="m 120.66272,976.3765 c -0.33282,0.10664 -0.44483,0.61349 -0.18825,0.85184 l 3.01199,3.02879 c 0.27825,0.26531 0.83246,0.0383 0.84712,-0.34705 l 0,-3.02878 c -3e-5,-0.26431 -0.23916,-0.50477 -0.502,-0.5048 l -3.01199,0 c -0.0519,-0.008 -0.105,-0.008 -0.15687,0 z"
+ style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0pt;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;text-anchor:start;color:#000000;fill:url(#linearGradient10598-1);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;font-family:Andale Mono" />
+ </g>
+ <path
+ sodipodi:nodetypes="cccccccccccccccc"
+ id="path10560"
+ d="m -55.9999,-892.61507 0,0.75 0,40.50004 0,0.72723 0.74999,0 32.49943,0 0.74998,0 0,-0.72723 0,-25.50003 0,-0.3282 -0.23436,-0.2343 -14.90598,-15.00001 -0.18748,-0.1875 -0.32813,0 -17.59346,0 -0.74999,0 z"
+ style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0pt;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;text-anchor:start;color:#000000;fill:url(#linearGradient10602-2);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;font-family:Andale Mono"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:url(#linearGradient8673);fill-opacity:1;stroke:none;display:inline"
+ d="m -54,-890.6378 16,0 14,14 0,24 -30,0 0,-38 z"
+ id="path10562"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccccc"
+ inkscape:export-filename="/home/bedipp/DocumentFoundation/Writer_16.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90" />
+ </g>
+ <path
+ id="path303-2"
+ d="m 15.299838,22.117153 0,-3.724729 6.791318,0"
+ style="fill:none;stroke:#a33e03;stroke-width:0.58063614px;stroke-opacity:1"
+ inkscape:connector-curvature="0" />
+ <path
+ id="path305-2"
+ d="m 16.19962,19.500792 1.785093,0"
+ style="fill:none;stroke:#a33e03;stroke-width:0.73576558px;stroke-opacity:1"
+ inkscape:connector-curvature="0" />
+ <path
+ id="path307-0"
+ d="m 19.099716,19.5447 2.802461,0"
+ style="fill:none;stroke:#a33e03;stroke-width:0.74753916px;stroke-opacity:1"
+ inkscape:connector-curvature="0" />
+ <path
+ id="path309-5"
+ d="m 15.947174,21.474047 2.114702,0"
+ style="fill:none;stroke:#a33e03;stroke-width:0.86295766px;stroke-opacity:1"
+ inkscape:connector-curvature="0" />
+ <path
+ id="path311-5"
+ d="m 20.091621,21.496001 1.868901,0"
+ style="fill:none;stroke:#a33e03;stroke-width:1.01523209px;stroke-opacity:1"
+ inkscape:connector-curvature="0" />
+ <path
+ style="color:#000000;fill:none;stroke:#a33e03;stroke-width:0.9867416;stroke-linecap:square;stroke-opacity:1"
+ d="m 8.5368289,14.555249 0,3.93792"
+ id="path315-9"
+ inkscape:connector-curvature="0" />
+ <path
+ style="color:#000000;fill:none;stroke:#a33e03;stroke-width:0.94667083;stroke-linecap:round;stroke-opacity:1"
+ d="m 10.555193,14.642423 -1.9744558,1.81556"
+ id="path317-0"
+ inkscape:connector-curvature="0" />
+ <path
+ style="color:#000000;fill:none;stroke:#a33e03;stroke-width:0.94667083;stroke-linecap:round;stroke-opacity:1"
+ d="M 10.555193,18.62481 8.5807372,16.80925"
+ id="path319-2"
+ inkscape:connector-curvature="0" />
+ <path
+ style="color:#000000;fill:none;stroke:#a33e03;stroke-width:0.94667083;stroke-linecap:square;stroke-opacity:1"
+ d="m 8.5954516,16.501892 -3.7958892,0"
+ id="path321-8"
+ inkscape:connector-curvature="0" />
+ <path
+ style="color:#000000;fill:none;stroke:#a33e03;stroke-width:0.88937062;stroke-linecap:round;stroke-opacity:1"
+ d="m 10.555193,18.98439 0,2.135555"
+ id="path323-3"
+ inkscape:connector-curvature="0" />
+ <path
+ style="color:#000000;fill:none;stroke:#a33e03;stroke-width:0.94667083;stroke-linecap:square;stroke-opacity:1"
+ d="m 11.586328,21.454457 -1.9527374,0"
+ id="path325-8"
+ inkscape:connector-curvature="0" />
+ <path
+ style="color:#000000;fill:none;stroke:#a33e03;stroke-width:0.90969408;stroke-linecap:round;stroke-opacity:1"
+ d="m 10.496239,14.485005 0,-2.89048"
+ id="path327-0"
+ inkscape:connector-curvature="0" />
+ <path
+ style="color:#000000;fill:none;stroke:#a33e03;stroke-width:0.95496774;stroke-linecap:square;stroke-opacity:1"
+ d="m 11.530374,11.420821 -1.9661595,0 0,-3.7106413 1.9661595,0 0,3.7106413"
+ id="path329-4"
+ inkscape:connector-curvature="0" />
+ <path
+ style="color:#000000;fill:none;stroke:#a33e03;stroke-width:0.95042706;stroke-linecap:square;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ d="m 10.965511,13.559011 4.47239,0"
+ id="path331-0"
+ inkscape:connector-curvature="0" />
+ <path
+ style="color:#000000;fill:#a33e03;fill-opacity:1;stroke:#a33e03;stroke-width:0.94674802;stroke-linecap:square;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ d="m 10.539398,4.0749115 0,3.5088427"
+ id="path333-9"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:none;stroke:#a33e03;stroke-width:1.00222099px;stroke-opacity:1"
+ d="m 16.356524,12.501212 4.102533,-0.02269 1.085413,0.990712 -1.075141,0.937806 -4.112805,-0.03024 0,-1.875614 z"
+ id="path361-0"
+ inkscape:connector-curvature="0" />
+</svg>
diff --git a/resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-pcbcalculator-project.svg b/resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-pcbcalculator-project.svg
new file mode 100644
index 0000000..c71e211
--- /dev/null
+++ b/resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-pcbcalculator-project.svg
@@ -0,0 +1,1100 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ height="48"
+ width="48"
+ version="1.1"
+ id="svg2"
+ inkscape:version="0.48.1 "
+ sodipodi:docname="icon_pcbcalculator.svg"
+ inkscape:export-filename="F:\kicad-launchpad\testing\bitmaps_png\sources\icon_pcbcalculator.png"
+ inkscape:export-xdpi="60"
+ inkscape:export-ydpi="60">
+ <metadata
+ id="metadata297">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1143"
+ inkscape:window-height="741"
+ id="namedview295"
+ showgrid="false"
+ inkscape:zoom="5.4791667"
+ inkscape:cx="24"
+ inkscape:cy="24"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="svg2" />
+ <defs
+ id="defs4">
+ <radialGradient
+ id="ay"
+ xlink:href="#ap"
+ gradientUnits="userSpaceOnUse"
+ cy="486.65"
+ cx="605.71"
+ gradientTransform="matrix(-2.7744,0,0,1.9697,112.76,-872.89)"
+ r="117.14" />
+ <linearGradient
+ id="ap">
+ <stop
+ offset="0"
+ id="stop8" />
+ <stop
+ stop-opacity="0"
+ offset="1"
+ id="stop10" />
+ </linearGradient>
+ <radialGradient
+ id="ax"
+ xlink:href="#ap"
+ gradientUnits="userSpaceOnUse"
+ cy="486.65"
+ cx="605.71"
+ gradientTransform="matrix(2.7744,0,0,1.9697,-1891.6,-872.89)"
+ r="117.14" />
+ <linearGradient
+ id="aq"
+ y2="609.51"
+ gradientUnits="userSpaceOnUse"
+ x2="302.86"
+ gradientTransform="matrix(2.7744,0,0,1.9697,-1892.2,-872.89)"
+ y1="366.65"
+ x1="302.86">
+ <stop
+ stop-opacity="0"
+ offset="0"
+ id="stop14" />
+ <stop
+ offset=".5"
+ id="stop16" />
+ <stop
+ stop-opacity="0"
+ offset="1"
+ id="stop18" />
+ </linearGradient>
+ <radialGradient
+ id="ar"
+ gradientUnits="userSpaceOnUse"
+ cy="3.7561"
+ cx="8.8244"
+ gradientTransform="matrix(.96827 0 0 1.0328 3.3536 .64645)"
+ r="37.752">
+ <stop
+ stop-color="#a3a3a3"
+ offset="0"
+ id="stop21" />
+ <stop
+ stop-color="#4c4c4c"
+ offset="1"
+ id="stop23" />
+ </radialGradient>
+ <radialGradient
+ id="as"
+ gradientUnits="userSpaceOnUse"
+ cy="35.737"
+ cx="33.967"
+ gradientTransform="scale(.96049 1.0411)"
+ r="86.708">
+ <stop
+ stop-color="#fafafa"
+ offset="0"
+ id="stop26" />
+ <stop
+ stop-color="#bbb"
+ offset="1"
+ id="stop28" />
+ </radialGradient>
+ <radialGradient
+ id="at"
+ gradientUnits="userSpaceOnUse"
+ cy="7.2679"
+ cx="8.1436"
+ gradientTransform="matrix(.96827 0 0 1.0328 3.3536 .64645)"
+ r="38.159">
+ <stop
+ stop-color="#fff"
+ offset="0"
+ id="stop31" />
+ <stop
+ stop-color="#f8f8f8"
+ offset="1"
+ id="stop33" />
+ </radialGradient>
+ <radialGradient
+ id="au"
+ gradientUnits="userSpaceOnUse"
+ cy="114.57"
+ cx="20.892"
+ gradientTransform="matrix(.2297 0 0 .2297 4.6135 3.9798)"
+ r="5.256">
+ <stop
+ stop-color="#f0f0f0"
+ offset="0"
+ id="stop36" />
+ <stop
+ stop-color="#9a9a9a"
+ offset="1"
+ id="stop38" />
+ </radialGradient>
+ <radialGradient
+ id="av"
+ gradientUnits="userSpaceOnUse"
+ cy="64.568"
+ cx="20.892"
+ gradientTransform="matrix(.2297 0 0 .2297 4.6135 3.9798)"
+ r="5.257">
+ <stop
+ stop-color="#f0f0f0"
+ offset="0"
+ id="stop41" />
+ <stop
+ stop-color="#9a9a9a"
+ offset="1"
+ id="stop43" />
+ </radialGradient>
+ <linearGradient
+ id="al"
+ y2="90.239"
+ xlink:href="#an"
+ gradientUnits="userSpaceOnUse"
+ x2="32.251"
+ gradientTransform="matrix(1.0238,0,0,1.0119,-1.1429,-98.071)"
+ y1="6.1317"
+ x1="32.251" />
+ <linearGradient
+ id="an"
+ y2="7.0165"
+ gradientUnits="userSpaceOnUse"
+ x2="45.448"
+ gradientTransform="matrix(1.0059 0 0 .99417 100 0)"
+ y1="92.54"
+ x1="45.448">
+ <stop
+ offset="0"
+ id="stop47" />
+ <stop
+ stop-opacity=".58824"
+ offset="1"
+ id="stop49" />
+ </linearGradient>
+ <linearGradient
+ id="bu"
+ y2="49"
+ xlink:href="#ao"
+ gradientUnits="userSpaceOnUse"
+ x2="37"
+ y1="16"
+ x1="37" />
+ <linearGradient
+ id="ao">
+ <stop
+ stop-color="#717249"
+ offset="0"
+ id="stop53" />
+ <stop
+ stop-color="#717249"
+ stop-opacity="0"
+ offset="1"
+ id="stop55" />
+ </linearGradient>
+ <linearGradient
+ id="bt"
+ y2="39"
+ xlink:href="#ao"
+ gradientUnits="userSpaceOnUse"
+ x2="34"
+ y1="14"
+ x1="34" />
+ <linearGradient
+ id="bb"
+ y2="5.9877"
+ gradientUnits="userSpaceOnUse"
+ x2="48"
+ y1="90"
+ x1="48">
+ <stop
+ stop-color="#bcb5ac"
+ offset="0"
+ id="stop59" />
+ <stop
+ stop-color="#e9e7e4"
+ offset="1"
+ id="stop61" />
+ </linearGradient>
+ <linearGradient
+ id="az"
+ y2="63.893"
+ gradientUnits="userSpaceOnUse"
+ x2="36.357"
+ y1="6"
+ x1="36.357">
+ <stop
+ stop-color="#fff"
+ offset="0"
+ id="stop64" />
+ <stop
+ stop-color="#fff"
+ stop-opacity="0"
+ offset="1"
+ id="stop66" />
+ </linearGradient>
+ <linearGradient
+ id="bs"
+ y2="35.771"
+ gradientUnits="userSpaceOnUse"
+ x2="64"
+ gradientTransform="matrix(1.025,0,0,1.0625,-9.6,-4)"
+ y1="15.5"
+ x1="64">
+ <stop
+ stop-color="#ccc7c1"
+ offset="0"
+ id="stop69" />
+ <stop
+ stop-color="#fefefd"
+ stop-opacity=".58015"
+ offset="1"
+ id="stop71" />
+ </linearGradient>
+ <radialGradient
+ id="aw"
+ gradientUnits="userSpaceOnUse"
+ cy="90.172"
+ cx="48"
+ gradientTransform="matrix(1.1573 0 0 .99591 -7.551 .19713)"
+ r="42">
+ <stop
+ stop-color="#fff"
+ offset="0"
+ id="stop74" />
+ <stop
+ stop-color="#fff"
+ stop-opacity="0"
+ offset="1"
+ id="stop76" />
+ </radialGradient>
+ <linearGradient
+ id="bk"
+ y2="112"
+ xlink:href="#a"
+ gradientUnits="userSpaceOnUse"
+ x2="30.247"
+ gradientTransform="matrix(.75 0 0 .75 -3 -12.75)"
+ y1="102"
+ x1="30.247" />
+ <linearGradient
+ id="a">
+ <stop
+ stop-color="#323232"
+ offset="0"
+ id="stop80" />
+ <stop
+ stop-color="#505050"
+ offset="1"
+ id="stop82" />
+ </linearGradient>
+ <linearGradient
+ id="bl"
+ y2="112"
+ xlink:href="#a"
+ gradientUnits="userSpaceOnUse"
+ x2="30.247"
+ gradientTransform="matrix(.75 0 0 .75 15 -12.75)"
+ y1="102"
+ x1="30.247" />
+ <linearGradient
+ id="bm"
+ y2="112"
+ xlink:href="#a"
+ gradientUnits="userSpaceOnUse"
+ x2="30.247"
+ gradientTransform="matrix(.75 0 0 .75 33 -12.75)"
+ y1="102"
+ x1="30.247" />
+ <linearGradient
+ id="bn"
+ y2="85.997"
+ xlink:href="#am"
+ gradientUnits="userSpaceOnUse"
+ x2="95.746"
+ gradientTransform="matrix(.75 0 0 .75 3 -.75002)"
+ y1="96"
+ x1="95.746" />
+ <linearGradient
+ id="am">
+ <stop
+ stop-color="#928075"
+ offset="0"
+ id="stop88" />
+ <stop
+ stop-color="#5b4f48"
+ offset="1"
+ id="stop90" />
+ </linearGradient>
+ <linearGradient
+ id="bg"
+ y2="112"
+ xlink:href="#a"
+ gradientUnits="userSpaceOnUse"
+ x2="30.247"
+ gradientTransform="matrix(.75 0 0 .75 -3 -24.75)"
+ y1="102"
+ x1="30.247" />
+ <linearGradient
+ id="bh"
+ y2="112"
+ xlink:href="#a"
+ gradientUnits="userSpaceOnUse"
+ x2="30.247"
+ gradientTransform="matrix(.75 0 0 .75 15 -24.75)"
+ y1="102"
+ x1="30.247" />
+ <linearGradient
+ id="bi"
+ y2="112"
+ xlink:href="#a"
+ gradientUnits="userSpaceOnUse"
+ x2="30.247"
+ gradientTransform="matrix(.75 0 0 .75 33 -24.75)"
+ y1="102"
+ x1="30.247" />
+ <linearGradient
+ id="bj"
+ y2="70"
+ xlink:href="#am"
+ gradientUnits="userSpaceOnUse"
+ x2="96"
+ gradientTransform="matrix(.75 0 0 .75 3 -.75001)"
+ y1="80"
+ x1="96" />
+ <linearGradient
+ id="bc"
+ y2="112"
+ xlink:href="#a"
+ gradientUnits="userSpaceOnUse"
+ x2="30.247"
+ gradientTransform="matrix(.75 0 0 .75 -3 -36.75)"
+ y1="102"
+ x1="30.247" />
+ <linearGradient
+ id="bd"
+ y2="112"
+ xlink:href="#a"
+ gradientUnits="userSpaceOnUse"
+ x2="30.247"
+ gradientTransform="matrix(.75 0 0 .75 15 -36.75)"
+ y1="102"
+ x1="30.247" />
+ <linearGradient
+ id="be"
+ y2="112"
+ xlink:href="#a"
+ gradientUnits="userSpaceOnUse"
+ x2="30.247"
+ gradientTransform="matrix(.75 0 0 .75 33 -36.75)"
+ y1="102"
+ x1="30.247" />
+ <linearGradient
+ id="bf"
+ y2="64"
+ gradientUnits="userSpaceOnUse"
+ x2="93.747"
+ gradientTransform="matrix(.75 0 0 .75 3 -.75001)"
+ y1="54"
+ x1="93.747">
+ <stop
+ stop-color="#802b27"
+ offset="0"
+ id="stop100" />
+ <stop
+ stop-color="#a93833"
+ offset="1"
+ id="stop102" />
+ </linearGradient>
+ <linearGradient
+ id="bo"
+ y2="112"
+ xlink:href="#a"
+ gradientUnits="userSpaceOnUse"
+ x2="30.247"
+ gradientTransform="matrix(.75 0 0 .75 -3 -.75002)"
+ y1="102"
+ x1="30.247" />
+ <linearGradient
+ id="bp"
+ y2="112"
+ xlink:href="#am"
+ gradientUnits="userSpaceOnUse"
+ x2="53.333"
+ gradientTransform="matrix(.75 0 0 .75 -.99999 -159.75)"
+ y1="102"
+ x1="53.333" />
+ <linearGradient
+ id="bq"
+ y2="102"
+ xlink:href="#am"
+ gradientUnits="userSpaceOnUse"
+ x2="74.667"
+ gradientTransform="matrix(.75 0 0 .75 1 -.75002)"
+ y1="112"
+ x1="74.667" />
+ <linearGradient
+ id="br"
+ y2="112.05"
+ gradientUnits="userSpaceOnUse"
+ x2="94.746"
+ gradientTransform="matrix(.75 0 0 .75 3 -.75002)"
+ y1="102"
+ x1="94.746">
+ <stop
+ stop-color="#c06517"
+ offset="0"
+ id="stop108" />
+ <stop
+ stop-color="#e88b3b"
+ offset="1"
+ id="stop110" />
+ </linearGradient>
+ <linearGradient
+ id="ba"
+ y2="90.239"
+ xlink:href="#an"
+ gradientUnits="userSpaceOnUse"
+ x2="32.251"
+ gradientTransform="translate(0,-97)"
+ y1="6.1317"
+ x1="32.251" />
+ </defs>
+ <g
+ transform="matrix(.95571 0 0 .92396 -1.3711 2.5805)"
+ id="g113">
+ <g
+ transform="matrix(.021055 0 0 .020868 42.852 41.154)"
+ id="g115">
+ <rect
+ opacity=".40206"
+ style="color:#000000"
+ height="478.36"
+ width="1339.6"
+ y="-150.7"
+ x="-1559.3"
+ fill="url(#aq)"
+ id="rect117" />
+ <path
+ opacity=".40206"
+ style="color:#000000"
+ fill="url(#ax)"
+ d="m-219.62-150.68v478.33c142.87 0.90045 345.4-107.17 345.4-239.2s-159.44-239.13-345.4-239.13z"
+ id="path119" />
+ <path
+ opacity=".40206"
+ style="color:#000000"
+ fill="url(#ay)"
+ d="m-1559.3-150.68v478.33c-142.87 0.90045-345.4-107.17-345.4-239.2s159.44-239.13 345.4-239.13z"
+ id="path121" />
+ </g>
+ </g>
+ <g
+ transform="matrix(.95571 0 0 .92396 -1.3711 2.5805)"
+ id="g123">
+ <rect
+ stroke-linejoin="round"
+ style="color:#000000"
+ display="block"
+ ry="1.149"
+ height="40.92"
+ width="34.875"
+ stroke="url(#ar)"
+ stroke-linecap="round"
+ y="3.6464"
+ x="6.6036"
+ fill="url(#as)"
+ id="rect125" />
+ <rect
+ stroke-linejoin="round"
+ style="color:#000000"
+ display="block"
+ rx=".14905"
+ ry=".14905"
+ height="38.946"
+ width="32.776"
+ stroke="url(#at)"
+ stroke-linecap="round"
+ y="4.5839"
+ x="7.6661"
+ fill="none"
+ id="rect127" />
+ <g
+ transform="translate(.64645 -.037989)"
+ id="g129">
+ <g
+ fill="#fff"
+ transform="matrix(.2297 0 0 .2297 4.9671 4.245)"
+ stroke="#000"
+ id="g131">
+ <path
+ d="m23.428 113.07c0 1.973-1.6 3.572-3.573 3.572-1.974 0-3.573-1.6-3.573-3.572 0-1.974 1.6-3.573 3.573-3.573s3.573 1.6 3.573 3.573z"
+ id="path133" />
+ <path
+ d="m23.428 63.07c0 1.973-1.6 3.573-3.573 3.573-1.974 0-3.573-1.6-3.573-3.573 0-1.974 1.6-3.573 3.573-3.573s3.573 1.6 3.573 3.573z"
+ id="path135" />
+ </g>
+ <path
+ fill="url(#au)"
+ d="m9.995 29.952c0 0.4532-0.36752 0.8205-0.82073 0.8205-0.45343 0-0.82073-0.36752-0.82073-0.8205 0-0.45343 0.36752-0.82073 0.82073-0.82073 0.4532 0 0.82073 0.36752 0.82073 0.82073z"
+ id="path137" />
+ <path
+ fill="url(#av)"
+ d="m9.995 18.467c0 0.4532-0.36752 0.82073-0.82073 0.82073-0.45343 0-0.82073-0.36752-0.82073-0.82073 0-0.45343 0.36752-0.82073 0.82073-0.82073 0.4532 0 0.82073 0.36752 0.82073 0.82073z"
+ id="path139" />
+ </g>
+ <path
+ d="m11.506 5.4943v37.907"
+ stroke-opacity=".017544"
+ stroke="#000"
+ stroke-width=".98855"
+ fill="none"
+ id="path141" />
+ <path
+ d="m12.5 5.0205v38.018"
+ stroke-opacity=".20468"
+ stroke="#fff"
+ fill="none"
+ id="path143" />
+ </g>
+ <path
+ stroke-linejoin="round"
+ style="color:#000000"
+ d="m12.044 29.784v9.2396"
+ stroke-opacity=".74510"
+ stroke="#9b9b9b"
+ stroke-linecap="round"
+ stroke-width=".93970"
+ fill="none"
+ id="path145" />
+ <path
+ stroke-linejoin="round"
+ style="color:#000000"
+ d="m16.822 29.784-4.7786 2.7719"
+ stroke-opacity=".74510"
+ stroke="#9b9b9b"
+ stroke-linecap="round"
+ stroke-width=".93970"
+ fill="none"
+ id="path147" />
+ <path
+ stroke-linejoin="round"
+ style="color:#000000"
+ d="m16.822 39.023-4.778-2.771"
+ stroke-opacity=".74510"
+ stroke="#9b9b9b"
+ stroke-linecap="round"
+ stroke-width=".93970"
+ fill="none"
+ id="path149" />
+ <path
+ stroke-linejoin="round"
+ style="color:#000000"
+ d="m11.531 34.365h-3.3791"
+ stroke-opacity=".74510"
+ stroke="#9b9b9b"
+ stroke-linecap="round"
+ stroke-width=".89703"
+ fill="none"
+ id="path151" />
+ <path
+ stroke-linejoin="round"
+ style="color:#000000"
+ d="m16.822 39.023v2.7719"
+ stroke-opacity=".74510"
+ stroke="#9b9b9b"
+ stroke-linecap="round"
+ stroke-width=".93970"
+ fill="none"
+ id="path153" />
+ <path
+ stroke-linejoin="round"
+ style="color:#000000"
+ d="m18.734 41.795h-3.823"
+ stroke-opacity=".74510"
+ stroke="#9b9b9b"
+ stroke-linecap="round"
+ stroke-width=".93970"
+ fill="none"
+ id="path155" />
+ <path
+ stroke-linejoin="round"
+ style="color:#000000"
+ d="m16.822 29.784v-5.544"
+ stroke-opacity=".74510"
+ stroke="#9b9b9b"
+ stroke-linecap="round"
+ stroke-width=".93970"
+ fill="none"
+ id="path157" />
+ <path
+ stroke-linejoin="round"
+ style="color:#000000"
+ d="m17.778 24.24h-1.911v-6.468h1.911v6.468"
+ stroke-opacity=".74510"
+ stroke="#9b9b9b"
+ stroke-linecap="round"
+ stroke-width=".93970"
+ fill="none"
+ id="path159" />
+ <path
+ stroke-linejoin="round"
+ style="color:#000000"
+ d="m16.822 27.012h7.6457"
+ stroke-opacity=".74510"
+ stroke="#9b9b9b"
+ stroke-linecap="round"
+ stroke-width=".93970"
+ fill="none"
+ id="path161" />
+ <path
+ stroke-linejoin="round"
+ style="color:#000000"
+ d="m16.822 15v2.7719"
+ stroke-opacity=".74510"
+ stroke="#9b9b9b"
+ stroke-linecap="round"
+ stroke-width=".93970"
+ fill="none"
+ id="path163" />
+ <path
+ stroke-linejoin="round"
+ style="color:#000000"
+ d="m29.758 36.663h4.7786"
+ stroke-opacity=".74510"
+ stroke="#9b9b9b"
+ stroke-linecap="round"
+ stroke-width=".93970"
+ fill="none"
+ id="path165" />
+ <path
+ stroke-linejoin="round"
+ style="color:#000000"
+ d="m29.758 38.511h4.7786"
+ stroke-opacity=".74510"
+ stroke="#9b9b9b"
+ stroke-linecap="round"
+ stroke-width=".93970"
+ fill="none"
+ id="path167" />
+ <path
+ stroke-linejoin="round"
+ style="color:#000000"
+ d="m22.646 36.665h4.7786"
+ stroke-opacity=".74510"
+ stroke="#9b9b9b"
+ stroke-linecap="round"
+ stroke-width=".93970"
+ fill="none"
+ id="path169" />
+ <path
+ stroke-linejoin="round"
+ style="color:#000000"
+ d="m22.646 38.513h4.7786"
+ stroke-opacity=".74510"
+ stroke="#9b9b9b"
+ stroke-linecap="round"
+ stroke-width=".93970"
+ fill="none"
+ id="path171" />
+ <path
+ style="color:#000000"
+ d="m16.822 39.023-2.0906-2.5986-1.0752 2.2522 3.1658 0.34649z"
+ fill-opacity=".74510"
+ fill-rule="evenodd"
+ fill="#9b9b9b"
+ id="path173" />
+ <path
+ stroke-linejoin="round"
+ style="color:#000000"
+ d="m27.376 38.551 0.0283 2.4666"
+ stroke-opacity=".74510"
+ stroke="#9b9b9b"
+ stroke-linecap="round"
+ stroke-width=".92714"
+ fill="none"
+ id="path177" />
+ <g
+ transform="matrix(.84783 0 0 .86922 -52.414 -25.974)"
+ id="g179">
+ <g
+ transform="matrix(.34827 0 0 .37406 84.233 30.234)"
+ id="g181">
+ <path
+ opacity="0.08"
+ d="m12-95.031c-5.5111 0-10.031 4.5202-10.031 10.031v71c0 5.5111 4.5202 10.031 10.031 10.031h72c5.5111 0 10.031-4.5202 10.031-10.031v-71c0-5.5111-4.5202-10.031-10.031-10.031h-72z"
+ transform="scale(1,-1)"
+ fill="url(#al)"
+ id="path183" />
+ <path
+ opacity=".1"
+ d="m12-94.031c-4.9716 0-9.0312 4.0596-9.0312 9.0312v71c0 4.9716 4.0596 9.0312 9.0312 9.0312h72c4.9716 0 9.0312-4.0596 9.0312-9.0312v-71c0-4.9716-4.0596-9.0312-9.0312-9.0312h-72z"
+ transform="scale(1,-1)"
+ fill="url(#al)"
+ id="path185" />
+ <path
+ opacity=".2"
+ d="m12-93c-4.4091 0-8 3.5909-8 8v71c0 4.4091 3.5909 8 8 8h72c4.4091 0 8-3.5909 8-8v-71c0-4.4091-3.5909-8-8-8h-72z"
+ transform="scale(1,-1)"
+ fill="url(#al)"
+ id="path187" />
+ <rect
+ opacity=".3"
+ transform="scale(1,-1)"
+ rx="7"
+ ry="7"
+ height="85"
+ width="86"
+ y="-92"
+ x="5"
+ fill="url(#al)"
+ id="rect189" />
+ <rect
+ opacity=".45"
+ transform="scale(1,-1)"
+ rx="6"
+ ry="6"
+ height="84"
+ width="84"
+ y="-91"
+ x="6"
+ fill="url(#ba)"
+ id="rect191" />
+ </g>
+ <g
+ transform="matrix(.34827 0 0 .37406 84.233 30.234)"
+ id="g193">
+ <rect
+ rx="2.5647"
+ ry="2.4475"
+ height="26.107"
+ width="68.393"
+ y="10.714"
+ x="13.429"
+ fill="#bdc490"
+ id="rect195" />
+ <g
+ transform="matrix(.77679 0 0 .77679 18.259 5.3571)"
+ id="g197">
+ <path
+ opacity=".5"
+ d="m68.217 22.238-1.896 1.496-1.921-1.496v-3.0174l1.921-1.496 1.896 1.496v3.0174m-2.2204-4.7417-1.896 1.5214h-3.817l-1.896-1.5214 1.896-1.496h3.817l1.896 1.496m2.2204 11.208-1.896 1.496-1.921-1.496v-3.0174l1.921-1.496 1.896 1.496v3.0174m-8.183-6.4659-1.896 1.496-1.921-1.496v-3.017l1.921-1.496 1.896 1.496v3.0174m5.963 8.266-1.921 1.496h-3.7921l-1.921-1.496 1.921-1.5214h3.7921l1.921 1.5214m-5.9626-1.8003-1.896 1.496-1.921-1.496v-3.0174l1.921-1.496 1.896 1.496v3.0174"
+ id="path199" />
+ <path
+ opacity=".5"
+ d="m74.139 30.473-2.044 1.527l-1.8893-1.5269 1.9669-1.5528 1.9669 1.5528"
+ id="path201" />
+ </g>
+ <path
+ opacity=".3"
+ style="color:#000000"
+ fill="url(#bu)"
+ d="m17 14c-0.554 0-1 0.446-1 1v18c0 0.554 0.446 1 1 1h62c0.554 0 1-0.446 1-1v-18c0-0.554-0.446-1-1-1zm1.0312 2.0312h59.938v15.938h-59.938z"
+ id="path203" />
+ <path
+ opacity=".6"
+ style="color:#000000"
+ fill="url(#bt)"
+ d="m17 14c-0.554 0-1 0.446-1 1v18c0 0.554 0.446 1 1 1h62c0.554 0 1-0.446 1-1v-18c0-0.554-0.446-1-1-1zm0.03125 1.0312h61.938v17.938h-61.938z"
+ id="path205" />
+ </g>
+ <g
+ transform="matrix(.34827 0 0 .37406 84.233 30.234)"
+ id="g207">
+ <path
+ fill="url(#bb)"
+ d="m12 6c-3.324 0-6 2.676-6 6v72c0 3.324 2.676 6 6 6h72c3.324 0 6-2.676 6-6v-72c0-3.324-2.676-6-6-6h-72zm6 7h60c1.108 0 2 0.892 2 2v18c0 1.108-0.892 2-2 2h-60c-1.108 0-2-0.892-2-2v-18c0-1.108 0.892-2 2-2z"
+ id="path209" />
+ <path
+ opacity=".5"
+ fill="url(#az)"
+ d="m12 6c-3.324 0-6 2.676-6 6v72c0 0.33472 0.04135 0.6507 0.09375 0.96875 0.0487 0.2956 0.09704 0.59692 0.1875 0.875 0.00988 0.03038 0.020892 0.0636 0.03125 0.09375 0.098865 0.28777 0.23488 0.54745 0.375 0.8125 0.14459 0.27351 0.31562 0.53562 0.5 0.78125 0.18438 0.24564 0.37378 0.47347 0.59375 0.6875 0.43995 0.42806 0.94291 0.81453 1.5 1.0938 0.27854 0.13961 0.57347 0.24695 0.875 0.34375-0.2562-0.10022-0.48671-0.23627-0.71875-0.375-0.00741-0.0044-0.023866 0.0045-0.03125 0-0.0319-0.019-0.0622-0.042-0.0937-0.062-0.1204-0.077-0.231-0.164-0.3437-0.25-0.1062-0.081-0.2133-0.161-0.3126-0.25-0.1778-0.162-0.3473-0.346-0.4999-0.531-0.1075-0.131-0.2183-0.266-0.3124-0.407-0.0251-0.038-0.0385-0.086-0.0626-0.125-0.0647-0.103-0.1302-0.204-0.1874-0.312-0.1011-0.195-0.2057-0.416-0.2813-0.625-0.008-0.022-0.0236-0.041-0.0313-0.063-0.0318-0.092-0.0358-0.187-0.0624-0.281-0.0304-0.107-0.0704-0.203-0.0938-0.313-0.0729-0.341-0.125-0.698-0.125-1.062v-72c0-2.782 2.2182-5 5-5h72c2.7818 0 5 2.2182 5 5v72c0 0.3643-0.05212 0.72099-0.125 1.0625-0.04415 0.20689-0.08838 0.39766-0.15625 0.59375-0.0077 0.02195-0.0233 0.04069-0.03125 0.0625-0.06274 0.17374-0.13838 0.36745-0.21875 0.53125-0.04158 0.0828-0.07904 0.16995-0.125 0.25-0.0546 0.09721-0.12677 0.18835-0.1875 0.28125-0.09411 0.14096-0.20492 0.276-0.3125 0.40625-0.14317 0.17445-0.30314 0.347-0.46875 0.5-0.01117 0.0102-0.01998 0.02115-0.03125 0.03125-0.13839 0.12556-0.28509 0.23444-0.4375 0.34375-0.10257 0.07315-0.20432 0.15336-0.3125 0.21875-0.0074 0.0045-0.02384-0.0044-0.03125 0-0.23204 0.13873-0.46255 0.27478-0.71875 0.375 0.30153-0.0968 0.59646-0.20414 0.875-0.34375 0.55709-0.27922 1.0601-0.66569 1.5-1.0938 0.21997-0.21403 0.40937-0.44186 0.59375-0.6875s0.35541-0.50774 0.5-0.78125c0.14012-0.26505 0.27614-0.52473 0.375-0.8125 0.01041-0.03078 0.02133-0.06274 0.03125-0.09375 0.09046-0.27808 0.1388-0.5794 0.1875-0.875 0.053-0.318 0.094-0.634 0.094-0.969v-72c0-3.324-2.676-6-6-6h-72z"
+ id="path211" />
+ <rect
+ stroke-linejoin="round"
+ rx="2"
+ ry="2"
+ height="22"
+ width="66"
+ stroke="url(#bs)"
+ stroke-linecap="square"
+ y="13"
+ x="15"
+ stroke-width="2"
+ fill="none"
+ id="rect213" />
+ <path
+ opacity=".5"
+ fill="url(#aw)"
+ d="m12 90c-3.324 0-6-2.676-6-6v-72c0-0.33472 0.04135-0.6507 0.09375-0.96875 0.0487-0.2956 0.09704-0.59692 0.1875-0.875 0.01-0.03 0.0209-0.063 0.0313-0.094 0.0989-0.2873 0.2349-0.547 0.375-0.812 0.1446-0.2735 0.3156-0.5356 0.5-0.7812 0.1844-0.2457 0.3738-0.4735 0.5937-0.6876 0.44-0.428 0.943-0.8145 1.5-1.0937 0.2786-0.1396 0.5735-0.2469 0.8748-0.3437-0.256 0.1002-0.4865 0.2362-0.7185 0.375-0.0074 0.0044-0.02387-0.0045-0.03125 0-0.03193 0.0193-0.06229 0.04251-0.09375 0.0625-0.1204 0.0767-0.23102 0.16351-0.34375 0.25-0.10617 0.0808-0.21328 0.16111-0.3125 0.25-0.1779 0.1614-0.3474 0.3453-0.5 0.5312-0.1075 0.1303-0.2183 0.2653-0.3124 0.4063-0.0251 0.0383-0.0385 0.0858-0.0626 0.125-0.0647 0.103-0.1302 0.2045-0.1874 0.3124-0.1011 0.1948-0.2057 0.4158-0.2813 0.625-0.008 0.0219-0.0236 0.0406-0.0313 0.0626-0.0318 0.0919-0.0358 0.1868-0.0624 0.2812-0.0304 0.1066-0.0704 0.203-0.0938 0.3125-0.0729 0.3415-0.125 0.6985-0.125 1.0625v72c0 2.7818 2.2182 5 5 5h72c2.7818 0 5-2.2182 5-5v-72c0-0.364-0.052-0.721-0.125-1.0625-0.044-0.2069-0.088-0.3977-0.156-0.5937-0.008-0.022-0.024-0.0407-0.031-0.0626-0.063-0.1737-0.139-0.3674-0.219-0.5312-0.042-0.0828-0.079-0.17-0.125-0.25-0.055-0.0972-0.127-0.1884-0.188-0.2812-0.094-0.141-0.205-0.276-0.312-0.4063-0.143-0.1745-0.303-0.347-0.469-0.5-0.011-0.0102-0.02-0.0211-0.031-0.0313-0.139-0.1255-0.285-0.2344-0.438-0.3437-0.102-0.0731-0.204-0.1534-0.312-0.2187-0.0074-0.0045-0.02384 0.0044-0.03125 0-0.23204-0.13873-0.46255-0.27478-0.71875-0.375 0.30153 0.0968 0.59646 0.20414 0.875 0.34375 0.55709 0.27922 1.0601 0.66569 1.5 1.0938 0.21997 0.21403 0.40937 0.44186 0.59375 0.6875s0.35541 0.50774 0.5 0.78125c0.14012 0.26505 0.27614 0.52473 0.375 0.8125 0.01041 0.03078 0.02133 0.06274 0.03125 0.09375 0.09046 0.27808 0.1388 0.5794 0.1875 0.875 0.053 0.318 0.094 0.634 0.094 0.969v72c0 3.324-2.676 6-6 6h-72z"
+ id="path215" />
+ </g>
+ <g
+ transform="matrix(.34827 0 0 .37406 84.233 30.234)"
+ id="g217">
+ <path
+ opacity=".2"
+ d="m16.125 39.844c-0.97087 0-1.7812 0.81038-1.7812 1.7812v5.25c0 0.97087 0.81038 1.7813 1.7812 1.7812h9.75c0.97087 0 1.7812-0.81038 1.7812-1.7812v-5.25c0-0.97087-0.81038-1.7812-1.7812-1.7812h-9.75zm18 0c-0.97087 0-1.7812 0.81038-1.7812 1.7812v5.25c0 0.97087 0.81038 1.7813 1.7812 1.7812h9.75c0.97087 0 1.7812-0.81038 1.7812-1.7812v-5.25c0-0.97087-0.81038-1.7812-1.7812-1.7812h-9.75zm18 0c-0.97087 0-1.7812 0.81038-1.7812 1.7812v5.25c0 0.97087 0.81038 1.7813 1.7812 1.7812h9.75c0.97087 0 1.7812-0.81038 1.7812-1.7812v-5.25c0-0.97087-0.81038-1.7812-1.7812-1.7812h-9.75zm18 0c-0.97087 0-1.7812 0.81038-1.7812 1.7812v5.25c0 0.97087 0.81038 1.7813 1.7812 1.7812h9.75c0.97087 0 1.7812-0.81038 1.7812-1.7812v-5.25c0-0.97087-0.81038-1.7812-1.7812-1.7812h-9.75zm-54 12c-0.97087 0-1.7812 0.81038-1.7812 1.7812v5.25c0 0.97087 0.81038 1.7813 1.7812 1.7812h9.75c0.97087 0 1.7812-0.81038 1.7812-1.7812v-5.25c0-0.97087-0.81038-1.7812-1.7812-1.7812h-9.75zm18 0c-0.97087 0-1.7812 0.81038-1.7812 1.7812v5.25c0 0.97087 0.81038 1.7813 1.7812 1.7812h9.75c0.97087 0 1.7812-0.81038 1.7812-1.7812v-5.25c0-0.97087-0.81038-1.7812-1.7812-1.7812h-9.75zm18 0c-0.97087 0-1.7812 0.81038-1.7812 1.7812v5.25c0 0.97087 0.81038 1.7813 1.7812 1.7812h9.75c0.97087 0 1.7812-0.81038 1.7812-1.7812v-5.25c0-0.97087-0.81038-1.7812-1.7812-1.7812h-9.75zm18 0c-0.97087 0-1.7812 0.81038-1.7812 1.7812v5.25c0 0.97087 0.81038 1.7813 1.7812 1.7812h9.75c0.97087 0 1.7812-0.81038 1.7812-1.7812v-5.25c0-0.97087-0.81038-1.7812-1.7812-1.7812h-9.75zm-54 12c-0.97087 0-1.7812 0.81038-1.7812 1.7812v5.25c0 0.97087 0.81038 1.7813 1.7812 1.7812h9.75c0.97087 0 1.7812-0.81038 1.7812-1.7812v-5.25c0-0.97087-0.81038-1.7812-1.7812-1.7812h-9.75zm18 0c-0.97087 0-1.7812 0.81038-1.7812 1.7812v5.25c0 0.97087 0.81038 1.7813 1.7812 1.7812h9.75c0.97087 0 1.7812-0.81038 1.7812-1.7812v-5.25c0-0.97087-0.81038-1.7812-1.7812-1.7812h-9.75zm18 0c-0.97087 0-1.7812 0.81038-1.7812 1.7812v5.25c0 0.97087 0.81038 1.7813 1.7812 1.7812h9.75c0.97087 0 1.7812-0.81038 1.7812-1.7812v-5.25c0-0.97087-0.81038-1.7812-1.7812-1.7812h-9.75zm18 0c-0.97087 0-1.7812 0.81038-1.7812 1.7812v5.25c0 0.97087 0.81038 1.7813 1.7812 1.7812h9.75c0.97087 0 1.7812-0.81038 1.7812-1.7812v-5.25c0-0.97087-0.81038-1.7812-1.7812-1.7812h-9.75zm-54 12c-0.97087 0-1.7812 0.81038-1.7812 1.7812v5.25c0 0.97087 0.81038 1.7813 1.7812 1.7812h9.75c0.97087 0 1.7812-0.81038 1.7812-1.7812v-5.25c0-0.97087-0.81038-1.7812-1.7812-1.7812h-9.75zm18 0c-0.97088-0.000002-1.7812 0.81038-1.7812 1.7812v5.25c0 0.97087 0.81038 1.7812 1.7812 1.7812h9.75c0.97087 0 1.7812-0.81038 1.7812-1.7812v-5.25c-0.000002-0.97087-0.81038-1.7812-1.7812-1.7812h-9.75zm18 0c-0.97087 0-1.7812 0.81038-1.7812 1.7812v5.25c0 0.97087 0.81038 1.7813 1.7812 1.7812h9.75c0.97087 0 1.7812-0.81038 1.7812-1.7812v-5.25c0-0.97087-0.81038-1.7812-1.7812-1.7812h-9.75zm18 0c-0.97087 0-1.7812 0.81038-1.7812 1.7812v5.25c0 0.97087 0.81038 1.7813 1.7812 1.7812h9.75c0.97087 0 1.7812-0.81038 1.7812-1.7812v-5.25c0-0.97087-0.81038-1.7812-1.7812-1.7812h-9.75z"
+ id="path219" />
+ <path
+ opacity=".6"
+ d="m16.125 40.5c-0.623 0-1.125 0.502-1.125 1.125v5.25c0 0.623 0.502 1.125 1.125 1.125h9.75c0.623 0 1.125-0.502 1.125-1.125v-5.25c0-0.623-0.502-1.125-1.125-1.125h-9.75zm18 0c-0.623 0-1.125 0.502-1.125 1.125v5.25c0 0.623 0.502 1.125 1.125 1.125h9.75c0.623 0 1.125-0.502 1.125-1.125v-5.25c0-0.623-0.502-1.125-1.125-1.125h-9.75zm18 0c-0.623 0-1.125 0.502-1.125 1.125v5.25c0 0.623 0.502 1.125 1.125 1.125h9.75c0.623 0 1.125-0.502 1.125-1.125v-5.25c0-0.623-0.502-1.125-1.125-1.125h-9.75zm18 0c-0.623 0-1.125 0.502-1.125 1.125v5.25c0 0.623 0.502 1.125 1.125 1.125h9.75c0.623 0 1.125-0.502 1.125-1.125v-5.25c0-0.623-0.502-1.125-1.125-1.125h-9.75zm-54 12c-0.623 0-1.125 0.502-1.125 1.125v5.25c0 0.623 0.502 1.125 1.125 1.125h9.75c0.623 0 1.125-0.502 1.125-1.125v-5.25c0-0.623-0.502-1.125-1.125-1.125h-9.75zm18 0c-0.623 0-1.125 0.502-1.125 1.125v5.25c0 0.623 0.502 1.125 1.125 1.125h9.75c0.623 0 1.125-0.502 1.125-1.125v-5.25c0-0.623-0.502-1.125-1.125-1.125h-9.75zm18 0c-0.623 0-1.125 0.502-1.125 1.125v5.25c0 0.623 0.502 1.125 1.125 1.125h9.75c0.623 0 1.125-0.502 1.125-1.125v-5.25c0-0.623-0.502-1.125-1.125-1.125h-9.75zm18 0c-0.623 0-1.125 0.502-1.125 1.125v5.25c0 0.623 0.502 1.125 1.125 1.125h9.75c0.623 0 1.125-0.502 1.125-1.125v-5.25c0-0.623-0.502-1.125-1.125-1.125h-9.75zm-54 12c-0.623 0-1.125 0.502-1.125 1.125v5.25c0 0.623 0.502 1.125 1.125 1.125h9.75c0.623 0 1.125-0.502 1.125-1.125v-5.25c0-0.623-0.502-1.125-1.125-1.125h-9.75zm18 0c-0.623 0-1.125 0.502-1.125 1.125v5.25c0 0.623 0.502 1.125 1.125 1.125h9.75c0.623 0 1.125-0.502 1.125-1.125v-5.25c0-0.623-0.502-1.125-1.125-1.125h-9.75zm18 0c-0.623 0-1.125 0.502-1.125 1.125v5.25c0 0.623 0.502 1.125 1.125 1.125h9.75c0.623 0 1.125-0.502 1.125-1.125v-5.25c0-0.623-0.502-1.125-1.125-1.125h-9.75zm18 0c-0.623 0-1.125 0.502-1.125 1.125v5.25c0 0.623 0.502 1.125 1.125 1.125h9.75c0.623 0 1.125-0.502 1.125-1.125v-5.25c0-0.623-0.502-1.125-1.125-1.125h-9.75zm-54 12c-0.623 0-1.125 0.502-1.125 1.125v5.25c0 0.623 0.502 1.125 1.125 1.125h9.75c0.623 0 1.125-0.502 1.125-1.125v-5.25c0-0.623-0.502-1.125-1.125-1.125h-9.75zm18 0c-0.623 0-1.125 0.502-1.125 1.125v5.25c0 0.623 0.502 1.125 1.125 1.125h9.75c0.623 0 1.125-0.502 1.125-1.125v-5.25c0-0.623-0.502-1.125-1.125-1.125h-9.75zm18 0c-0.623 0-1.125 0.502-1.125 1.125v5.25c0 0.623 0.502 1.125 1.125 1.125h9.75c0.623 0 1.125-0.502 1.125-1.125v-5.25c0-0.623-0.502-1.125-1.125-1.125h-9.75zm18 0c-0.623 0-1.125 0.502-1.125 1.125v5.25c0 0.623 0.502 1.125 1.125 1.125h9.75c0.623 0 1.125-0.502 1.125-1.125v-5.25c0-0.623-0.502-1.125-1.125-1.125h-9.75z"
+ id="path221" />
+ <g
+ transform="translate(0 .25)"
+ id="g223">
+ <rect
+ rx="1.125"
+ ry="1.125"
+ height="7.5"
+ width="12"
+ y="63.75"
+ x="15"
+ fill="url(#bk)"
+ id="rect225" />
+ <path
+ fill="#fff"
+ d="m19.43 69.822v-0.68884h1.1503v-3.1674l-1.114 0.69543v-0.728l1.1635-0.75476h0.87671v3.9551h1.0646v0.68884h-3.141"
+ id="path227" />
+ <rect
+ rx="1.125"
+ ry="1.125"
+ height="7.5"
+ width="12"
+ y="63.75"
+ x="33"
+ fill="url(#bl)"
+ id="rect229" />
+ <path
+ fill="#fff"
+ d="m37.375 69.857v-0.6427c0.10547-0.23071 0.23401-0.43835 0.38562-0.62292 0.15161-0.18677 0.30981-0.35705 0.47461-0.51086 0.16699-0.15601 0.33179-0.29993 0.49438-0.43176 0.16479-0.13403 0.31201-0.26257 0.44165-0.38562 0.13183-0.12524 0.2384-0.25049 0.3197-0.37573 0.08129-0.12524 0.12194-0.25818 0.12195-0.3988-0.000007-0.20654-0.05164-0.35925-0.15491-0.45813-0.10327-0.09887-0.25488-0.14831-0.45483-0.14832-0.19776 0.000005-0.34937 0.05384-0.45483 0.1615-0.10327 0.10547-0.17029 0.26477-0.20105 0.4779l-0.93274-0.05273c0.02197-0.18237 0.06702-0.35376 0.13513-0.51416s0.16479-0.30102 0.29004-0.42188c0.12744-0.12084 0.28564-0.21533 0.47461-0.28345 0.19116-0.07031 0.41858-0.10546 0.68225-0.10547 0.25048 0.000004 0.47241 0.02967 0.66577 0.08899 0.19335 0.05713 0.35595 0.14173 0.48779 0.25378 0.13183 0.11206 0.2318 0.24939 0.29992 0.41199 0.06811 0.1604 0.10216 0.34388 0.10217 0.55042-0.000007 0.21753-0.04285 0.41419-0.12854 0.58997-0.0857 0.17578-0.19556 0.33838-0.32959 0.48779-0.13404 0.14722-0.28345 0.28565-0.44824 0.41528-0.1626 0.12964-0.3208 0.25598-0.47461 0.37903s-0.29444 0.24719-0.42188 0.37244c-0.12524 0.12525-0.21863 0.25928-0.28015 0.4021h2.1555v0.76135h-3.2498"
+ id="path231" />
+ <rect
+ rx="1.125"
+ ry="1.125"
+ height="7.5"
+ width="12"
+ y="63.75"
+ x="51"
+ fill="url(#bm)"
+ id="rect233" />
+ <path
+ fill="#fff"
+ d="m58.678 68.53c0 0.21094-0.03516 0.401-0.10547 0.57019-0.06813 0.16699-0.17139 0.30981-0.30982 0.42847-0.13842 0.11865-0.31092 0.20984-0.51746 0.27356-0.20434 0.06152-0.44274 0.09229-0.71521 0.09229-0.30322 0-0.55921-0.03845-0.76794-0.11536-0.20654-0.0769-0.37683-0.17798-0.51087-0.30322-0.13183-0.12744-0.23181-0.27136-0.29992-0.43176-0.06592-0.1604-0.10877-0.323-0.12854-0.48779l0.94263-0.0824c0.01318 0.09888 0.03735 0.19006 0.07251 0.27356 0.03735 0.0813 0.08679 0.15161 0.14831 0.21094s0.13623 0.10547 0.22412 0.13843c0.09008 0.03076 0.19556 0.04614 0.3164 0.04614 0.22192 0.000001 0.39442-0.05493 0.51746-0.1648 0.12304-0.10986 0.18457-0.27795 0.18458-0.50427-0.000008-0.13843-0.02967-0.24939-0.08899-0.33289-0.05713-0.08349-0.13073-0.14722-0.22082-0.19116-0.08789-0.04614-0.18348-0.0769-0.28674-0.09228-0.10328-0.01538-0.20105-0.02307-0.29334-0.02307h-0.324v-0.74817h0.30323c0.09228 0.000004 0.18566-0.0088 0.28015-0.02637 0.09667-0.01758 0.18347-0.04944 0.26038-0.09558 0.0769-0.04614 0.13952-0.10986 0.18787-0.19116 0.04833-0.08349 0.07251-0.18896 0.07251-0.31641 0-0.19116-0.05383-0.34057-0.1615-0.44824-0.10547-0.10986-0.26038-0.16479-0.46472-0.1648-0.19117 0.000003-0.34608 0.05274-0.46472 0.1582-0.11646 0.10547-0.18348 0.25489-0.20105 0.44824l-0.926-0.067c0.02637-0.21752 0.0857-0.40649 0.17798-0.5669 0.09449-0.1604 0.21204-0.29333 0.35267-0.3988 0.14282-0.10546 0.30652-0.18347 0.49108-0.23401 0.18458-0.05273 0.38013-0.0791 0.58668-0.0791 0.27246 0.000005 0.50647 0.03296 0.70203 0.09888 0.19554 0.06592 0.35595 0.15491 0.4812 0.26697 0.12523 0.11206 0.21752 0.2428 0.27685 0.39221 0.05932 0.14942 0.08899 0.30872 0.08899 0.4779 0 0.13623-0.01978 0.26368-0.05933 0.38232-0.03956 0.11646-0.09998 0.22193-0.18128 0.31641-0.07911 0.09229-0.18128 0.17249-0.30652 0.2406-0.12525 0.06592-0.27357 0.11536-0.44495 0.14832v0.01318c0.19116 0.02197 0.35706 0.06592 0.49768 0.13184 0.14062 0.06372 0.25598 0.14392 0.34607 0.2406 0.09008 0.09668 0.1571 0.20654 0.20105 0.32959 0.04394 0.12085 0.06592 0.24939 0.06592 0.38562"
+ id="path235" />
+ <rect
+ rx="1.125"
+ ry="1.125"
+ height="7.5"
+ width="12"
+ y="63.75"
+ x="69"
+ fill="url(#bn)"
+ id="rect237" />
+ <path
+ fill="#fff"
+ d="m75.408 67.91v1.4941h-0.828v-1.494h-1.461v-0.82h1.461v-1.494h0.828v1.4941h1.4722v0.82h-1.472"
+ id="path239" />
+ </g>
+ <g
+ transform="translate(0 .25)"
+ id="g241">
+ <rect
+ rx="1.125"
+ ry="1.125"
+ height="7.5"
+ width="12"
+ y="51.75"
+ x="15"
+ fill="url(#bg)"
+ id="rect243" />
+ <path
+ fill="#fff"
+ d="m22.188 56.876v0.94592h-0.883v-0.946h-2.113v-0.69543l1.9611-3.0026h1.0349v3.0092h0.61963v0.68884h-0.62m-0.8833-2.2083c-0.000003-0.03735-0.000003-0.08569 0-0.14502 0.0022-0.06152 0.0044-0.12414 0.0066-0.18787 0.0044-0.06372 0.0077-0.12414 0.0099-0.18127 0.0044-0.05932 0.0088-0.10546 0.01318-0.13843-0.01099 0.02417-0.02857 0.05933-0.05273 0.10547-0.02417 0.04395-0.05164 0.09229-0.0824 0.14502-0.02857 0.05054-0.05933 0.10108-0.09229 0.15161-0.03076 0.05054-0.05823 0.09449-0.0824 0.13184l-1.0778 1.6381h1.3579v-1.5194"
+ id="path245" />
+ <rect
+ rx="1.125"
+ ry="1.125"
+ height="7.5"
+ width="12"
+ y="51.75"
+ x="33"
+ fill="url(#bh)"
+ id="rect247" />
+ <path
+ fill="#fff"
+ d="m40.679 56.243c-0.000008 0.23291-0.03736 0.44824-0.11206 0.646-0.07251 0.19556-0.18128 0.36585-0.32629 0.51087-0.14503 0.14282-0.3252 0.25488-0.54053 0.33618-0.21314 0.0791-0.46033 0.11865-0.74158 0.11865-0.25928-0.000001-0.4856-0.03076-0.67895-0.09228-0.19336-0.06372-0.35706-0.15051-0.49109-0.26038-0.13184-0.11206-0.23621-0.2428-0.31311-0.39221-0.07471-0.14941-0.12634-0.30981-0.15491-0.4812l0.92615-0.07581c0.01758 0.07251 0.04175 0.14282 0.07251 0.21094 0.03296 0.06812 0.078 0.12854 0.13513 0.18127 0.05713 0.05274 0.12744 0.09558 0.21094 0.12854 0.0835 0.03076 0.18457 0.04614 0.30322 0.04614 0.23071 0.000001 0.41418-0.07361 0.55041-0.22082 0.13843-0.14722 0.20764-0.35925 0.20764-0.63611 0-0.12304-0.01758-0.23401-0.05273-0.33289-0.03296-0.09887-0.08131-0.18347-0.14502-0.25378-0.06153-0.07031-0.13844-0.12414-0.23072-0.1615-0.09009-0.03735-0.19336-0.05603-0.30981-0.05603-0.15381 0.000003-0.28015 0.02967-0.37903 0.08899-0.09888 0.05713-0.18237 0.12744-0.25049 0.21094h-0.902l0.1615-2.6136h2.7916v0.68884h-1.9512l-0.07581 1.1733c0.09228-0.0813 0.20874-0.15051 0.34936-0.20764 0.14062-0.05932 0.30432-0.08899 0.49109-0.08899 0.22851 0.000003 0.43286 0.03736 0.61304 0.11206 0.18017 0.07471 0.33288 0.17908 0.45813 0.31311 0.12524 0.13404 0.22082 0.29553 0.28674 0.4845 0.06592 0.18677 0.09887 0.39441 0.09888 0.62292"
+ id="path249" />
+ <rect
+ rx="1.125"
+ ry="1.125"
+ height="7.5"
+ width="12"
+ y="51.75"
+ x="51"
+ fill="url(#bi)"
+ id="rect251" />
+ <path
+ fill="#fff"
+ d="m58.631 56.304c-0.000008 0.23291-0.03297 0.44714-0.09888 0.6427-0.06592 0.19336-0.16479 0.36035-0.29662 0.50098-0.12964 0.13843-0.29224 0.24719-0.48779 0.3263-0.19556 0.0769-0.42188 0.11536-0.67896 0.11536-0.54712 0-0.9679-0.19116-1.2623-0.57349-0.29223-0.38452-0.43836-0.95361-0.43836-1.7073 0-0.82837 0.14832-1.4513 0.44495-1.8688 0.29882-0.41967 0.72509-0.62951 1.2788-0.62952 0.18458 0.000004 0.35267 0.01978 0.50428 0.05933 0.1516 0.03736 0.28674 0.09888 0.40539 0.18457 0.11865 0.0857 0.21972 0.19666 0.30322 0.33289 0.08348 0.13404 0.15051 0.29774 0.20105 0.49109l-0.87342 0.12195c-0.04395-0.16259-0.11536-0.27905-0.21423-0.34937-0.09888-0.07251-0.21424-0.10876-0.34607-0.10876-0.24829 0.000003-0.44385 0.12415-0.58668 0.37244-0.14062 0.2483-0.21093 0.62513-0.21093 1.1305 0.09887-0.16479 0.2362-0.29114 0.41199-0.37903 0.17578-0.08789 0.37463-0.13183 0.59656-0.13184 0.20433 0.000003 0.38891 0.03406 0.55371 0.10217 0.16698 0.06592 0.30871 0.1626 0.42517 0.29004 0.11865 0.12525 0.20983 0.27906 0.27356 0.46142 0.06372 0.18238 0.09558 0.38782 0.09558 0.61633m-0.92944 0.02637c0-0.12524-0.01428-0.2373-0.04285-0.33618-0.02857-0.10107-0.07251-0.18677-0.13183-0.25708-0.05713-0.07031-0.12854-0.12414-0.21423-0.1615-0.08569-0.03955-0.18458-0.05932-0.29663-0.05933-0.0813 0.000001-0.1626 0.01319-0.2439 0.03955-0.0813 0.02637-0.15381 0.06921-0.21753 0.12854-0.06372 0.05713-0.11536 0.13074-0.15491 0.22082-0.03955 0.09009-0.05933 0.19885-0.05933 0.32629 0 0.13184 0.01647 0.25488 0.04944 0.36914 0.03295 0.11426 0.0791 0.21424 0.13842 0.29993 0.06152 0.0835 0.13512 0.14942 0.22082 0.19775 0.08569 0.04614 0.18237 0.06921 0.29004 0.06921 0.20873 0.000001 0.37133-0.07361 0.48779-0.22082 0.11645-0.14941 0.17468-0.35486 0.17468-0.61633"
+ id="path253" />
+ <rect
+ rx="1.125"
+ ry="1.125"
+ height="7.5"
+ width="12"
+ y="51.75"
+ x="69"
+ fill="url(#bj)"
+ id="rect255" />
+ <path
+ fill="#fff"
+ d="m73.385 55.947v-0.89356h3.2294v0.894h-3.229"
+ id="path257" />
+ </g>
+ <g
+ transform="translate(0 .25)"
+ id="g259">
+ <rect
+ rx="1.125"
+ ry="1.125"
+ height="7.5"
+ width="12"
+ y="39.75"
+ x="15"
+ fill="url(#bc)"
+ id="rect261" />
+ <path
+ fill="#fff"
+ d="m22.584 41.913c-0.20874 0.32959-0.4065 0.6493-0.59326 0.95911-0.18457 0.30982-0.34607 0.62183-0.4845 0.93604-0.13843 0.31201-0.24829 0.63391-0.32959 0.9657-0.0791 0.32959-0.11865 0.67896-0.11865 1.0481h-0.9657c-0.000001-0.36694 0.04285-0.71521 0.12854-1.0448 0.08789-0.33179 0.20654-0.65478 0.35596-0.96899 0.14941-0.31421 0.323-0.62402 0.52075-0.92944 0.19775-0.30761 0.40649-0.62072 0.62622-0.93933h-2.3071v-0.76135h3.1674v0.73498"
+ id="path263" />
+ <rect
+ rx="1.125"
+ ry="1.125"
+ height="7.5"
+ width="12"
+ y="39.75"
+ x="33"
+ fill="url(#bd)"
+ id="rect265" />
+ <path
+ fill="#fff"
+ d="m40.666 44.515c0 0.19995-0.03296 0.38452-0.09888 0.55371-0.06592 0.16699-0.167 0.31201-0.30322 0.43506-0.13624 0.12085-0.30872 0.21533-0.51746 0.28345-0.20875 0.06811-0.45704 0.10217-0.74488 0.10217-0.28565 0-0.53394-0.03406-0.74487-0.10217-0.20874-0.06812-0.38232-0.1626-0.52075-0.28345-0.13623-0.12305-0.2373-0.26807-0.30322-0.43506s-0.09888-0.34936-0.09888-0.54712c-0.000001-0.16919 0.02637-0.3186 0.0791-0.44824 0.05273-0.13183 0.12305-0.24499 0.21094-0.33948 0.08789-0.09448 0.18896-0.16919 0.30322-0.22412 0.11426-0.05713 0.23181-0.09558 0.35266-0.11536v-0.01318c-0.12964-0.02856-0.2461-0.0758-0.34936-0.14172-0.10327-0.06591-0.19116-0.14502-0.26367-0.2373s-0.12854-0.19446-0.16809-0.30652c-0.03735-0.11425-0.05603-0.23291-0.05603-0.35596 0-0.18457 0.03296-0.35266 0.09888-0.50427 0.06811-0.15161 0.16699-0.28124 0.29663-0.38892 0.12964-0.10766 0.29004-0.19006 0.4812-0.24719 0.19336-0.05932 0.41638-0.08898 0.66907-0.08899 0.27026 0.000004 0.50317 0.03077 0.69873 0.09229 0.19775 0.05933 0.36035 0.14392 0.48779 0.25378 0.12744 0.10767 0.22192 0.23731 0.28345 0.38892 0.06152 0.15162 0.09229 0.31861 0.09229 0.50098 0 0.12085-0.01978 0.23841-0.05933 0.35266-0.03736 0.11206-0.09229 0.21424-0.1648 0.30652-0.07251 0.09229-0.1615 0.17029-0.26696 0.23401-0.10328 0.06372-0.22083 0.10877-0.35266 0.13513v0.01318c0.13623 0.02197 0.26258 0.06262 0.37903 0.12195 0.11865 0.05713 0.22082 0.13294 0.30652 0.22742 0.08569 0.09229 0.15271 0.20435 0.20105 0.33618 0.04834 0.12964 0.07251 0.27686 0.07251 0.44165m-1.068-2.118c0-0.09228-0.0099-0.17688-0.02966-0.25378-0.01978-0.0791-0.05273-0.14611-0.09887-0.20105-0.04615-0.05713-0.10877-0.10107-0.18787-0.13184-0.07691-0.03295-0.17469-0.04943-0.29334-0.04944-0.11426 0.000004-0.20984 0.01648-0.28674 0.04944-0.07691 0.03077-0.13843 0.07471-0.18457 0.13184-0.04614 0.05494-0.0791 0.12195-0.09888 0.20105-0.01978 0.07691-0.02966 0.1615-0.02966 0.25378-0.000002 0.0813 0.0077 0.1615 0.02307 0.2406 0.01758 0.0791 0.04834 0.15052 0.09229 0.21423 0.04394 0.06153 0.10547 0.11206 0.18457 0.15161s0.18127 0.05933 0.30652 0.05933c0.13183 0.000002 0.2373-0.01977 0.31641-0.05933 0.08129-0.03955 0.14282-0.09118 0.18457-0.15491 0.04175-0.06372 0.06921-0.13513 0.0824-0.21423s0.01978-0.1582 0.01978-0.2373m0.10876 2.0435c0-0.09448-0.01099-0.18567-0.03296-0.27356-0.01978-0.08789-0.05713-0.16479-0.11206-0.23071-0.05273-0.06811-0.12634-0.12195-0.22082-0.1615-0.09448-0.04175-0.21424-0.06262-0.35925-0.06262-0.13184 0.000003-0.2428 0.02088-0.33289 0.06262-0.08789 0.03955-0.1593 0.09339-0.21423 0.1615-0.05274 0.06812-0.09119 0.14722-0.11536 0.2373-0.02197 0.08789-0.03296 0.18128-0.03296 0.28015-0.000001 0.11646 0.01098 0.22302 0.03296 0.3197 0.02417 0.09668 0.06372 0.18018 0.11865 0.25049 0.05713 0.06812 0.13184 0.12085 0.22412 0.1582s0.20764 0.05603 0.34607 0.05603c0.13843 0.000001 0.25268-0.01868 0.34277-0.05603 0.09009-0.03955 0.1615-0.09448 0.21424-0.1648 0.05273-0.07031 0.08899-0.15381 0.10876-0.25049 0.02197-0.09887 0.03296-0.20764 0.03296-0.32629"
+ id="path267" />
+ <rect
+ rx="1.125"
+ ry="1.125"
+ height="7.5"
+ width="12"
+ y="39.75"
+ x="51"
+ fill="url(#be)"
+ id="rect269" />
+ <path
+ fill="#fff"
+ d="m58.635 43.427c0 0.82398-0.15051 1.4403-0.45153 1.849-0.30103 0.40869-0.7284 0.61304-1.2821 0.61304-0.19995 0-0.38012-0.02087-0.54052-0.06262-0.1582-0.04175-0.29663-0.10657-0.41528-0.19446-0.11866-0.08789-0.21972-0.20105-0.30322-0.33948-0.0813-0.13843-0.14611-0.30322-0.19446-0.49438l0.87012-0.12195c0.04394 0.1648 0.11535 0.28674 0.21422 0.36584 0.10108 0.0791 0.22742 0.11865 0.37903 0.11865 0.12525 0.000001 0.23731-0.02966 0.33619-0.08899 0.09887-0.05932 0.18347-0.14941 0.25378-0.27026s0.12414-0.27356 0.1615-0.45813c0.03955-0.18677 0.06042-0.40649 0.06262-0.65918-0.04394 0.0857-0.10218 0.1615-0.17468 0.22742-0.07251 0.06592-0.15491 0.12085-0.24719 0.1648-0.09009 0.04395-0.18677 0.078-0.29004 0.10217-0.10327 0.02197-0.20763 0.03296-0.31311 0.03296-0.20215 0.000002-0.38562-0.03515-0.55041-0.10547-0.1648-0.07251-0.30542-0.17578-0.42188-0.30982-0.11646-0.13403-0.20654-0.29553-0.27027-0.4845-0.06152-0.19116-0.09228-0.40649-0.09228-0.646 0-0.24609 0.03625-0.46582 0.10877-0.65918 0.0747-0.19336 0.18128-0.35595 0.3197-0.48779 0.13842-0.13403 0.30761-0.2351 0.50757-0.30322 0.20215-0.07031 0.43066-0.10546 0.68555-0.10547 0.26806 0.000004 0.50427 0.04615 0.70861 0.13843 0.20655 0.09009 0.37903 0.23072 0.51746 0.42188 0.13842 0.18897 0.2428 0.42957 0.31311 0.7218 0.07251 0.29004 0.10877 0.63501 0.10877 1.0349m-0.979-0.649c-0.000008-0.13183-0.01538-0.25488-0.04615-0.36914-0.02856-0.11645-0.07251-0.21643-0.13183-0.29993-0.05933-0.08569-0.13183-0.15271-0.21753-0.20105-0.08569-0.04834-0.18457-0.0725-0.29662-0.07251-0.10328 0.000005-0.19556 0.01978-0.27685 0.05933-0.0813 0.03736-0.15052 0.09229-0.20765 0.1648s-0.10108 0.1604-0.13183 0.26367c-0.02857 0.10328-0.04285 0.21973-0.04285 0.34937 0 0.12085 0.01428 0.23401 0.04285 0.33948 0.02856 0.10327 0.07032 0.19336 0.12524 0.27026 0.05493 0.07471 0.12415 0.13404 0.20765 0.17798 0.08348 0.04175 0.17907 0.06262 0.28673 0.06262 0.0857 0.000002 0.16919-0.01538 0.25049-0.04614 0.08349-0.03076 0.15711-0.0769 0.22082-0.13843 0.06592-0.06152 0.11865-0.13842 0.15821-0.23071 0.03954-0.09448 0.05932-0.20434 0.05932-0.32959"
+ id="path271" />
+ <rect
+ rx="1.125"
+ ry="1.125"
+ height="7.5"
+ width="12"
+ y="39.75"
+ x="69"
+ fill="url(#bf)"
+ id="rect273" />
+ <path
+ fill="#fff"
+ d="m73.154 44.944c0.16211 0.000001 0.30176-0.02246 0.41895-0.06738 0.11913-0.04687 0.22168-0.10742 0.30761-0.18164 0.08789-0.07422 0.16016-0.1582 0.21681-0.25195 0.05858-0.09375 0.10643-0.18848 0.14355-0.28418l0.75292 0.28418c-0.06054 0.14844-0.13965 0.29395-0.2373 0.43652-0.09766 0.14258-0.21973 0.26855-0.36622 0.37793-0.14648 0.10938-0.32129 0.19824-0.52441 0.2666-0.20118 0.06641-0.43848 0.09961-0.71192 0.09961-0.35742 0-0.66699-0.05273-0.92871-0.1582-0.26172-0.10742-0.47852-0.25586-0.65039-0.44531-0.16992-0.19141-0.29688-0.41797-0.38086-0.67969-0.08203-0.26172-0.12305-0.54785-0.12305-0.8584 0-0.32226 0.04102-0.61328 0.12305-0.87305 0.08398-0.25976 0.20996-0.48046 0.37793-0.66211 0.16797-0.18359 0.37988-0.32422 0.63574-0.42188 0.25781-0.09961 0.56054-0.14941 0.9082-0.14941 0.26953 0.000004 0.50488 0.02832 0.70605 0.08496 0.20312 0.05665 0.37792 0.13672 0.52442 0.24023 0.14843 0.10157 0.27051 0.22266 0.36621 0.36328 0.09765 0.13868 0.17383 0.292 0.22852 0.45996l-0.76172 0.20801c-0.02735-0.08789-0.06934-0.17187-0.12598-0.25195-0.05664-0.08203-0.12892-0.15429-0.2168-0.2168-0.08594-0.0625-0.1875-0.1123-0.30468-0.14941-0.11719-0.03906-0.25001-0.05859-0.39844-0.05859-0.20898 0.000002-0.38867 0.03418-0.53906 0.10254-0.15039 0.06641-0.27442 0.16211-0.37207 0.28711-0.09571 0.125-0.167 0.27539-0.21388 0.45117-0.04492 0.17383-0.06737 0.36914-0.06737 0.58594 0 0.21484 0.02245 0.41211 0.06737 0.5918 0.04687 0.17774 0.11914 0.33106 0.2168 0.45996 0.09766 0.12891 0.22266 0.22949 0.375 0.30176 0.15429 0.07227 0.33886 0.1084 0.55371 0.1084m2.4082 0.62109v-4.1279h3.2461v0.66797h-2.3818v1.0371h2.2031v0.66797h-2.2031v1.0869h2.502v0.66797h-3.3662"
+ id="path275" />
+ </g>
+ <g
+ transform="translate(0 .25)"
+ id="g277">
+ <rect
+ rx="1.125"
+ ry="1.125"
+ height="7.5"
+ width="12"
+ y="75.75"
+ x="15"
+ fill="url(#bo)"
+ id="rect279" />
+ <path
+ fill="#fff"
+ d="m22.605 79.5c-0.000003 0.44385-0.04065 0.81848-0.12195 1.1239-0.0813 0.30322-0.19446 0.54932-0.33948 0.73828-0.14282 0.18677-0.31311 0.3219-0.51086 0.4054-0.19556 0.0813-0.40979 0.12195-0.6427 0.12195s-0.44714-0.04065-0.6427-0.12195-0.36365-0.21533-0.50427-0.4021c-0.141-0.189-0.25-0.436-0.33-0.739-0.079-0.305-0.118-0.681-0.118-1.127 0-0.46802 0.03845-0.85473 0.11536-1.1602 0.0791-0.30761 0.19006-0.55261 0.33289-0.73498 0.14282-0.18237 0.31311-0.30981 0.51086-0.38233 0.19995-0.0747 0.42077-0.11206 0.66248-0.11206 0.22632 0 0.43616 0.03736 0.62952 0.11206 0.19555 0.07252 0.36364 0.19996 0.50427 0.38233 0.14282 0.18238 0.25378 0.42737 0.33289 0.73498 0.08129 0.30542 0.12194 0.69214 0.12195 1.1602m-0.92944 0c-0.000002-0.32739-0.01209-0.59765-0.03626-0.81079-0.02417-0.21533-0.06262-0.38562-0.11536-0.51086-0.05274-0.12744-0.12195-0.21643-0.20764-0.26697-0.0835-0.05053-0.18567-0.0758-0.30652-0.07581-0.12964 0.000008-0.23841 0.02638-0.32629 0.0791-0.08789 0.05054-0.1593 0.13953-0.21423 0.26697-0.05274 0.12745-0.09119 0.29883-0.11536 0.51416-0.02197 0.21314-0.03296 0.4812-0.03296 0.8042-0.000001 0.31861 0.01208 0.58448 0.03625 0.79761 0.02417 0.21314 0.06262 0.38452 0.11536 0.51416 0.05273 0.12744 0.12195 0.21753 0.20764 0.27026s0.19116 0.0791 0.31641 0.0791c0.12085 0 0.22412-0.02637 0.30982-0.0791 0.08569-0.05273 0.156-0.14282 0.21094-0.27026 0.05493-0.12964 0.09448-0.30103 0.11865-0.51416 0.02636-0.21314 0.03955-0.479 0.03955-0.79761"
+ id="path281" />
+ <rect
+ transform="scale(1,-1)"
+ rx="1.125"
+ ry="1.125"
+ height="7.5"
+ width="12"
+ y="-83.25"
+ x="33"
+ fill="url(#bp)"
+ id="rect283" />
+ <path
+ fill="#fff"
+ d="m38.471 81.558v-1.1169h1.0583v1.117h-1.058"
+ id="path285" />
+ <rect
+ rx="1.125"
+ ry="1.125"
+ height="7.5"
+ width="12"
+ y="75.75"
+ x="51"
+ fill="url(#bq)"
+ id="rect287" />
+ <path
+ fill="#fff"
+ d="m59.828 80.398c0 0.27466-0.03077 0.50647-0.09228 0.69544-0.06153 0.18896-0.14502 0.34167-0.25049 0.45813-0.10548 0.11426-0.22962 0.19665-0.37243 0.24719-0.14063 0.05054-0.29114 0.0758-0.45154 0.0758-0.16479 0-0.3186-0.02527-0.46142-0.0758-0.14063-0.05054-0.26368-0.13294-0.36914-0.24719-0.10328-0.11646-0.18568-0.26917-0.24719-0.45813-0.05933-0.18897-0.089-0.42078-0.08899-0.69544-0.000008-0.29004 0.02966-0.52954 0.08899-0.71851 0.06152-0.19116 0.14501-0.34277 0.25048-0.45483 0.10547-0.11426 0.22961-0.19336 0.37244-0.23731 0.14501-0.04614 0.30102-0.06921 0.46802-0.06921 0.15819 0 0.30761 0.02307 0.44823 0.06921 0.14062 0.04395 0.26258 0.12305 0.36585 0.23731 0.10547 0.11206 0.18786 0.26367 0.24719 0.45483 0.06152 0.18897 0.09228 0.42847 0.09228 0.71851m-4.0045 1.4238h-0.67895l3.0322-4.6439h0.68885l-3.0421 4.6439m-0.47461-4.6967c0.1582 0.000007 0.30762 0.02308 0.44824 0.06922 0.14062 0.04395 0.26367 0.12195 0.36914 0.23401s0.18897 0.26368 0.25048 0.45483c0.06153 0.18897 0.09229 0.42848 0.09229 0.71851 0 0.27467-0.03077 0.50648-0.09229 0.69544s-0.14612 0.34168-0.25378 0.45813c-0.10548 0.11645-0.22962 0.19995-0.37244 0.25048-0.14282 0.05054-0.29553 0.07581-0.45813 0.0758-0.16039 0.000007-0.31201-0.02526-0.45482-0.0758-0.14062-0.05054-0.26368-0.13293-0.36914-0.24719-0.10328-0.11645-0.18568-0.26916-0.2472-0.45813-0.05933-0.18896-0.08898-0.42188-0.08898-0.69873 0-0.29003 0.02966-0.52954 0.08898-0.71851 0.05932-0.19115 0.14173-0.34276 0.2472-0.45483 0.10547-0.11206 0.22961-0.19006 0.37243-0.23401 0.14502-0.04614 0.30102-0.06921 0.46802-0.06922m3.7705 3.2728c-0.000008-0.19116-0.0099-0.34936-0.02966-0.47461-0.01758-0.12524-0.04506-0.22412-0.0824-0.29663-0.03737-0.07471-0.0835-0.12634-0.13842-0.1549-0.05494-0.03077-0.11976-0.04614-0.19446-0.04615-0.08131 0.000007-0.15162 0.01538-0.21094 0.04615-0.05713 0.02857-0.10438 0.0802-0.14172 0.1549-0.03736 0.07471-0.06592 0.17468-0.08569 0.29993-0.01758 0.12525-0.02637 0.28235-0.02637 0.47132-0.000008 0.18458 0.0088 0.33838 0.02637 0.46142 0.01758 0.12086 0.04504 0.21863 0.0824 0.29334 0.03954 0.07471 0.08788 0.12744 0.14502 0.1582 0.05712 0.03077 0.12523 0.04615 0.20435 0.04615 0.0725 0 0.13622-0.01538 0.19116-0.04615 0.05712-0.03076 0.10437-0.0824 0.14172-0.1549 0.03954-0.07471 0.06921-0.17358 0.08899-0.29663 0.01977-0.12304 0.02965-0.27685 0.02966-0.46142m-3.326-1.796c0-0.18896-0.0099-0.34497-0.02967-0.46802-0.01758-0.12524-0.04504-0.22412-0.0824-0.29663-0.03735-0.0747-0.0835-0.12634-0.13842-0.1549-0.05493-0.03076-0.11976-0.04614-0.19446-0.04615-0.0813 0.000008-0.15161 0.01539-0.21093 0.04615-0.05933 0.03076-0.10767 0.0835-0.14502 0.1582-0.03736 0.07252-0.06593 0.17139-0.0857 0.29663-0.01758 0.12305-0.02637 0.27796-0.02637 0.46472 0 0.18458 0.0088 0.33839 0.02637 0.46142 0.01977 0.12305 0.04834 0.22193 0.0857 0.29663 0.03954 0.07471 0.08788 0.12854 0.14502 0.1615 0.05712 0.03077 0.12524 0.04615 0.20434 0.04615 0.0747 0 0.13952-0.01538 0.19446-0.04615 0.05712-0.03076 0.10437-0.08349 0.14172-0.1582 0.03955-0.07471 0.06812-0.17358 0.08569-0.29663 0.01978-0.12524 0.02967-0.28015 0.02967-0.46472"
+ id="path289" />
+ <rect
+ rx="1.125"
+ ry="1.125"
+ height="7.5"
+ width="12"
+ y="75.75"
+ x="69"
+ fill="url(#br)"
+ id="rect291" />
+ <path
+ fill="#fff"
+ d="m73.12 78.899v-0.81665h3.761v0.817h-3.761m0 2.0178v-0.80932h3.761v0.809h-3.761"
+ id="path293" />
+ </g>
+ </g>
+ </g>
+</svg>
diff --git a/resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-pcbnew-project.svg b/resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-pcbnew-project.svg
new file mode 100644
index 0000000..8001830
--- /dev/null
+++ b/resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-pcbnew-project.svg
@@ -0,0 +1,1554 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ height="26"
+ width="26"
+ version="1.1"
+ id="svg2"
+ inkscape:version="0.47 r22583"
+ sodipodi:docname="icon_pcbnew.svg">
+ <metadata
+ id="metadata373">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1280"
+ inkscape:window-height="949"
+ id="namedview371"
+ showgrid="true"
+ inkscape:snap-to-guides="false"
+ inkscape:snap-grids="false"
+ inkscape:zoom="9.4280905"
+ inkscape:cx="12.473042"
+ inkscape:cy="20.702255"
+ inkscape:window-x="0"
+ inkscape:window-y="25"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg2">
+ <inkscape:grid
+ type="xygrid"
+ id="grid3350"
+ empspacing="5"
+ visible="true"
+ enabled="true"
+ snapvisiblegridlinesonly="true" />
+ </sodipodi:namedview>
+ <defs
+ id="defs4">
+ <filter
+ id="bb"
+ color-interpolation-filters="sRGB">
+ <feGaussianBlur
+ stdDeviation="2.0786429"
+ id="feGaussianBlur7" />
+ </filter>
+ <clipPath
+ id="ba">
+ <path
+ d="M 72,88 40,120 H 32 V 80 h 40 v 8 z"
+ id="path10"
+ inkscape:connector-curvature="0"
+ style="fill-rule:evenodd" />
+ </clipPath>
+ <filter
+ id="bc"
+ height="1.3839999"
+ width="1.3839999"
+ color-interpolation-filters="sRGB"
+ y="-0.192"
+ x="-0.192">
+ <feGaussianBlur
+ stdDeviation="1.9447689"
+ id="feGaussianBlur13" />
+ </filter>
+ <linearGradient
+ id="bn"
+ y2="172.44"
+ gradientUnits="userSpaceOnUse"
+ x2="153"
+ gradientTransform="translate(-143,-64)"
+ y1="175.56"
+ x1="153">
+ <stop
+ stop-color="#fff"
+ offset="0"
+ id="stop16" />
+ <stop
+ stop-color="#ddd"
+ offset="1"
+ id="stop18" />
+ </linearGradient>
+ <linearGradient
+ id="bo"
+ y2="172.89999"
+ gradientUnits="userSpaceOnUse"
+ x2="151.89999"
+ gradientTransform="translate(-143,-64)"
+ y1="175.10001"
+ x1="154.10001">
+ <stop
+ stop-color="#bbb"
+ offset="0"
+ id="stop21" />
+ <stop
+ stop-color="#616161"
+ offset="1"
+ id="stop23" />
+ </linearGradient>
+ <linearGradient
+ id="bp"
+ y2="164.44"
+ gradientUnits="userSpaceOnUse"
+ x2="153"
+ gradientTransform="translate(-143,-64)"
+ y1="167.56"
+ x1="153">
+ <stop
+ stop-color="#fff"
+ offset="0"
+ id="stop26" />
+ <stop
+ stop-color="#ddd"
+ offset="1"
+ id="stop28" />
+ </linearGradient>
+ <linearGradient
+ id="bq"
+ y2="164.89999"
+ gradientUnits="userSpaceOnUse"
+ x2="151.89999"
+ gradientTransform="translate(-143,-64)"
+ y1="167.10001"
+ x1="154.10001">
+ <stop
+ stop-color="#bbb"
+ offset="0"
+ id="stop31" />
+ <stop
+ stop-color="#616161"
+ offset="1"
+ id="stop33" />
+ </linearGradient>
+ <linearGradient
+ id="br"
+ y2="156.44"
+ gradientUnits="userSpaceOnUse"
+ x2="153"
+ gradientTransform="translate(-143,-64)"
+ y1="159.56"
+ x1="153">
+ <stop
+ stop-color="#fff"
+ offset="0"
+ id="stop36" />
+ <stop
+ stop-color="#ddd"
+ offset="1"
+ id="stop38" />
+ </linearGradient>
+ <linearGradient
+ id="bs"
+ y2="156.89999"
+ gradientUnits="userSpaceOnUse"
+ x2="151.89999"
+ gradientTransform="translate(-143,-64)"
+ y1="159.10001"
+ x1="154.10001">
+ <stop
+ stop-color="#bbb"
+ offset="0"
+ id="stop41" />
+ <stop
+ stop-color="#616161"
+ offset="1"
+ id="stop43" />
+ </linearGradient>
+ <linearGradient
+ id="bt"
+ y2="148.44"
+ gradientUnits="userSpaceOnUse"
+ x2="153"
+ gradientTransform="translate(-143,-64)"
+ y1="151.56"
+ x1="153">
+ <stop
+ stop-color="#fff"
+ offset="0"
+ id="stop46" />
+ <stop
+ stop-color="#ddd"
+ offset="1"
+ id="stop48" />
+ </linearGradient>
+ <linearGradient
+ id="bu"
+ y2="148.89999"
+ gradientUnits="userSpaceOnUse"
+ x2="151.89999"
+ gradientTransform="translate(-143,-64)"
+ y1="151.10001"
+ x1="154.10001">
+ <stop
+ stop-color="#bbb"
+ offset="0"
+ id="stop51" />
+ <stop
+ stop-color="#616161"
+ offset="1"
+ id="stop53" />
+ </linearGradient>
+ <linearGradient
+ id="bv"
+ y2="140.44"
+ gradientUnits="userSpaceOnUse"
+ x2="153"
+ gradientTransform="translate(-143,-64)"
+ y1="143.56"
+ x1="153">
+ <stop
+ stop-color="#fff"
+ offset="0"
+ id="stop56" />
+ <stop
+ stop-color="#ddd"
+ offset="1"
+ id="stop58" />
+ </linearGradient>
+ <linearGradient
+ id="bw"
+ y2="140.89999"
+ gradientUnits="userSpaceOnUse"
+ x2="151.89999"
+ gradientTransform="translate(-143,-64)"
+ y1="143.10001"
+ x1="154.10001">
+ <stop
+ stop-color="#bbb"
+ offset="0"
+ id="stop61" />
+ <stop
+ stop-color="#616161"
+ offset="1"
+ id="stop63" />
+ </linearGradient>
+ <linearGradient
+ id="bx"
+ y2="132.44"
+ gradientUnits="userSpaceOnUse"
+ x2="153"
+ gradientTransform="translate(-143,-64)"
+ y1="135.56"
+ x1="153">
+ <stop
+ stop-color="#fff"
+ offset="0"
+ id="stop66" />
+ <stop
+ stop-color="#ddd"
+ offset="1"
+ id="stop68" />
+ </linearGradient>
+ <linearGradient
+ id="by"
+ y2="132.89999"
+ gradientUnits="userSpaceOnUse"
+ x2="151.89999"
+ gradientTransform="translate(-143,-64)"
+ y1="135.10001"
+ x1="154.10001">
+ <stop
+ stop-color="#bbb"
+ offset="0"
+ id="stop71" />
+ <stop
+ stop-color="#616161"
+ offset="1"
+ id="stop73" />
+ </linearGradient>
+ <linearGradient
+ id="bz"
+ y2="124.44"
+ gradientUnits="userSpaceOnUse"
+ x2="153"
+ gradientTransform="translate(-143,-64)"
+ y1="127.56"
+ x1="153">
+ <stop
+ stop-color="#fff"
+ offset="0"
+ id="stop76" />
+ <stop
+ stop-color="#ddd"
+ offset="1"
+ id="stop78" />
+ </linearGradient>
+ <linearGradient
+ id="ca"
+ y2="124.9"
+ gradientUnits="userSpaceOnUse"
+ x2="151.89999"
+ gradientTransform="translate(-143,-64)"
+ y1="127.1"
+ x1="154.10001">
+ <stop
+ stop-color="#bbb"
+ offset="0"
+ id="stop81" />
+ <stop
+ stop-color="#616161"
+ offset="1"
+ id="stop83" />
+ </linearGradient>
+ <linearGradient
+ id="cb"
+ y2="116.44"
+ gradientUnits="userSpaceOnUse"
+ x2="153"
+ gradientTransform="translate(-143,-64)"
+ y1="119.56"
+ x1="153">
+ <stop
+ stop-color="#fff"
+ offset="0"
+ id="stop86" />
+ <stop
+ stop-color="#ddd"
+ offset="1"
+ id="stop88" />
+ </linearGradient>
+ <linearGradient
+ id="cc"
+ y2="116.9"
+ gradientUnits="userSpaceOnUse"
+ x2="151.89999"
+ gradientTransform="translate(-143,-64)"
+ y1="119.1"
+ x1="154.10001">
+ <stop
+ stop-color="#bbb"
+ offset="0"
+ id="stop91" />
+ <stop
+ stop-color="#616161"
+ offset="1"
+ id="stop93" />
+ </linearGradient>
+ <linearGradient
+ id="cd"
+ y2="108.44"
+ gradientUnits="userSpaceOnUse"
+ x2="153"
+ gradientTransform="translate(-143,-64)"
+ y1="111.56"
+ x1="153">
+ <stop
+ stop-color="#fff"
+ offset="0"
+ id="stop96" />
+ <stop
+ stop-color="#ddd"
+ offset="1"
+ id="stop98" />
+ </linearGradient>
+ <linearGradient
+ id="ce"
+ y2="108.9"
+ gradientUnits="userSpaceOnUse"
+ x2="151.89999"
+ gradientTransform="translate(-143,-64)"
+ y1="111.1"
+ x1="154.10001">
+ <stop
+ stop-color="#bbb"
+ offset="0"
+ id="stop101" />
+ <stop
+ stop-color="#616161"
+ offset="1"
+ id="stop103" />
+ </linearGradient>
+ <linearGradient
+ id="cf"
+ y2="100.44"
+ gradientUnits="userSpaceOnUse"
+ x2="153"
+ gradientTransform="translate(-143,-64)"
+ y1="103.56"
+ x1="153">
+ <stop
+ stop-color="#fff"
+ offset="0"
+ id="stop106" />
+ <stop
+ stop-color="#ddd"
+ offset="1"
+ id="stop108" />
+ </linearGradient>
+ <linearGradient
+ id="cg"
+ y2="100.9"
+ gradientUnits="userSpaceOnUse"
+ x2="151.89999"
+ gradientTransform="translate(-143,-64)"
+ y1="103.1"
+ x1="154.10001">
+ <stop
+ stop-color="#bbb"
+ offset="0"
+ id="stop111" />
+ <stop
+ stop-color="#616161"
+ offset="1"
+ id="stop113" />
+ </linearGradient>
+ <linearGradient
+ id="ch"
+ y2="92.444"
+ gradientUnits="userSpaceOnUse"
+ x2="153"
+ gradientTransform="translate(-143,-64)"
+ y1="95.556999"
+ x1="153">
+ <stop
+ stop-color="#fff"
+ offset="0"
+ id="stop116" />
+ <stop
+ stop-color="#ddd"
+ offset="1"
+ id="stop118" />
+ </linearGradient>
+ <linearGradient
+ id="at"
+ y2="92.900002"
+ gradientUnits="userSpaceOnUse"
+ x2="151.89999"
+ gradientTransform="translate(-143,-64)"
+ y1="95.100998"
+ x1="154.10001">
+ <stop
+ stop-color="#bbb"
+ offset="0"
+ id="stop121" />
+ <stop
+ stop-color="#616161"
+ offset="1"
+ id="stop123" />
+ </linearGradient>
+ <linearGradient
+ id="au"
+ y2="84.444"
+ gradientUnits="userSpaceOnUse"
+ x2="153"
+ gradientTransform="translate(-143,-64)"
+ y1="87.556999"
+ x1="153">
+ <stop
+ stop-color="#fff"
+ offset="0"
+ id="stop126" />
+ <stop
+ stop-color="#ddd"
+ offset="1"
+ id="stop128" />
+ </linearGradient>
+ <linearGradient
+ id="av"
+ y2="84.900002"
+ gradientUnits="userSpaceOnUse"
+ x2="151.89999"
+ gradientTransform="translate(-143,-64)"
+ y1="87.100998"
+ x1="154.10001">
+ <stop
+ stop-color="#bbb"
+ offset="0"
+ id="stop131" />
+ <stop
+ stop-color="#616161"
+ offset="1"
+ id="stop133" />
+ </linearGradient>
+ <linearGradient
+ id="aw"
+ y2="76.444"
+ gradientUnits="userSpaceOnUse"
+ x2="153"
+ gradientTransform="translate(-143,-64)"
+ y1="79.556999"
+ x1="153">
+ <stop
+ stop-color="#fff"
+ offset="0"
+ id="stop136" />
+ <stop
+ stop-color="#ddd"
+ offset="1"
+ id="stop138" />
+ </linearGradient>
+ <linearGradient
+ id="ax"
+ y2="76.900002"
+ gradientUnits="userSpaceOnUse"
+ x2="151.89999"
+ gradientTransform="translate(-143,-64)"
+ y1="79.100998"
+ x1="154.10001">
+ <stop
+ stop-color="#bbb"
+ offset="0"
+ id="stop141" />
+ <stop
+ stop-color="#616161"
+ offset="1"
+ id="stop143" />
+ </linearGradient>
+ <linearGradient
+ id="bd"
+ y2="99.254997"
+ gradientUnits="userSpaceOnUse"
+ x2="91.228996"
+ gradientTransform="matrix(0.21025443,0,0,0.20691162,-0.54460205,-0.4483919)"
+ y1="106.41"
+ x1="98.616997">
+ <stop
+ stop-color="#a2a2a2"
+ offset="0"
+ id="stop193" />
+ <stop
+ stop-color="#fff"
+ offset="1"
+ id="stop195" />
+ </linearGradient>
+ <radialGradient
+ id="az"
+ gradientUnits="userSpaceOnUse"
+ cy="112.3"
+ cx="102"
+ gradientTransform="matrix(0.21025443,0,0,0.20683793,-0.54460205,-0.44781288)"
+ r="139.56">
+ <stop
+ stop-color="#b7b8b9"
+ offset="0"
+ id="stop203" />
+ <stop
+ stop-color="#ececec"
+ offset=".18851"
+ id="stop205" />
+ <stop
+ stop-color="#fafafa"
+ offset=".25718"
+ id="stop207" />
+ <stop
+ stop-color="#fff"
+ offset=".30111"
+ id="stop209" />
+ <stop
+ stop-color="#fafafa"
+ offset=".53130"
+ id="stop211" />
+ <stop
+ stop-color="#ebecec"
+ offset=".84490"
+ id="stop213" />
+ <stop
+ stop-color="#e1e2e3"
+ offset="1"
+ id="stop215" />
+ </radialGradient>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#az"
+ id="radialGradient3418"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.21025443,0,0,0.20683793,-0.54460205,-0.44781288)"
+ cx="102"
+ cy="112.3"
+ r="139.56" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#bn"
+ id="linearGradient3420"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-143,-64)"
+ x1="153"
+ y1="175.56"
+ x2="153"
+ y2="172.44" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#bo"
+ id="linearGradient3422"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-143,-64)"
+ x1="154.10001"
+ y1="175.10001"
+ x2="151.89999"
+ y2="172.89999" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#bp"
+ id="linearGradient3424"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-143,-64)"
+ x1="153"
+ y1="167.56"
+ x2="153"
+ y2="164.44" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#bq"
+ id="linearGradient3426"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-143,-64)"
+ x1="154.10001"
+ y1="167.10001"
+ x2="151.89999"
+ y2="164.89999" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#br"
+ id="linearGradient3428"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-143,-64)"
+ x1="153"
+ y1="159.56"
+ x2="153"
+ y2="156.44" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#bs"
+ id="linearGradient3430"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-143,-64)"
+ x1="154.10001"
+ y1="159.10001"
+ x2="151.89999"
+ y2="156.89999" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#bt"
+ id="linearGradient3432"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-143,-64)"
+ x1="153"
+ y1="151.56"
+ x2="153"
+ y2="148.44" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#bu"
+ id="linearGradient3434"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-143,-64)"
+ x1="154.10001"
+ y1="151.10001"
+ x2="151.89999"
+ y2="148.89999" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#bv"
+ id="linearGradient3436"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-143,-64)"
+ x1="153"
+ y1="143.56"
+ x2="153"
+ y2="140.44" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#bw"
+ id="linearGradient3438"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-143,-64)"
+ x1="154.10001"
+ y1="143.10001"
+ x2="151.89999"
+ y2="140.89999" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#bx"
+ id="linearGradient3440"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-143,-64)"
+ x1="153"
+ y1="135.56"
+ x2="153"
+ y2="132.44" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#by"
+ id="linearGradient3442"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-143,-64)"
+ x1="154.10001"
+ y1="135.10001"
+ x2="151.89999"
+ y2="132.89999" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#bz"
+ id="linearGradient3444"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-143,-64)"
+ x1="153"
+ y1="127.56"
+ x2="153"
+ y2="124.44" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#ca"
+ id="linearGradient3446"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-143,-64)"
+ x1="154.10001"
+ y1="127.1"
+ x2="151.89999"
+ y2="124.9" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#cb"
+ id="linearGradient3448"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-143,-64)"
+ x1="153"
+ y1="119.56"
+ x2="153"
+ y2="116.44" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#cc"
+ id="linearGradient3450"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-143,-64)"
+ x1="154.10001"
+ y1="119.1"
+ x2="151.89999"
+ y2="116.9" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#cd"
+ id="linearGradient3452"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-143,-64)"
+ x1="153"
+ y1="111.56"
+ x2="153"
+ y2="108.44" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#ce"
+ id="linearGradient3454"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-143,-64)"
+ x1="154.10001"
+ y1="111.1"
+ x2="151.89999"
+ y2="108.9" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#cf"
+ id="linearGradient3456"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-143,-64)"
+ x1="153"
+ y1="103.56"
+ x2="153"
+ y2="100.44" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#cg"
+ id="linearGradient3458"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-143,-64)"
+ x1="154.10001"
+ y1="103.1"
+ x2="151.89999"
+ y2="100.9" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#ch"
+ id="linearGradient3460"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-143,-64)"
+ x1="153"
+ y1="95.556999"
+ x2="153"
+ y2="92.444" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#at"
+ id="linearGradient3462"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-143,-64)"
+ x1="154.10001"
+ y1="95.100998"
+ x2="151.89999"
+ y2="92.900002" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#au"
+ id="linearGradient3464"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-143,-64)"
+ x1="153"
+ y1="87.556999"
+ x2="153"
+ y2="84.444" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#av"
+ id="linearGradient3466"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-143,-64)"
+ x1="154.10001"
+ y1="87.100998"
+ x2="151.89999"
+ y2="84.900002" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#aw"
+ id="linearGradient3468"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-143,-64)"
+ x1="153"
+ y1="79.556999"
+ x2="153"
+ y2="76.444" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#ax"
+ id="linearGradient3470"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-143,-64)"
+ x1="154.10001"
+ y1="79.100998"
+ x2="151.89999"
+ y2="76.900002" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#bd"
+ id="linearGradient3472"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.21025443,0,0,0.20691162,-0.54460205,-0.4483919)"
+ x1="98.616997"
+ y1="106.41"
+ x2="91.228996"
+ y2="99.254997" />
+ <linearGradient
+ id="be-2-3"
+ y2="31.211"
+ gradientUnits="userSpaceOnUse"
+ x2="23.576"
+ gradientTransform="matrix(0.64407,-0.64045,0.66092,0.65072,61.423,86.661)"
+ y1="25.357"
+ x1="23.576">
+ <stop
+ offset="0"
+ id="stop180-9" />
+ <stop
+ stop-color="#c3c3c3"
+ offset=".13483"
+ id="stop182-1" />
+ <stop
+ stop-color="#8c8c8c"
+ offset=".20224"
+ id="stop184-9" />
+ <stop
+ stop-color="#fff"
+ offset=".26966"
+ id="stop186-6" />
+ <stop
+ stop-color="#757575"
+ offset=".4465"
+ id="stop188-5-9" />
+ <stop
+ stop-color="#7d7d7d"
+ offset=".57114"
+ id="stop190-8-3" />
+ <stop
+ stop-color="#b6b6b6"
+ offset=".72038"
+ id="stop192-3" />
+ <stop
+ offset="1"
+ id="stop194-8" />
+ </linearGradient>
+ <linearGradient
+ id="bf-6-0"
+ y2="30"
+ xlink:href="#ar-2-5"
+ gradientUnits="userSpaceOnUse"
+ x2="30.038"
+ gradientTransform="matrix(0.62586,-0.62234,0.77349,0.76155,59.298,83.616)"
+ y1="24.99"
+ x1="30.038" />
+ <linearGradient
+ id="ar-2-5">
+ <stop
+ stop-color="#3e3e3e"
+ offset="0"
+ id="stop166-8-6" />
+ <stop
+ stop-color="#828282"
+ offset=".5"
+ id="stop168-4-6" />
+ <stop
+ stop-color="#3c3c3c"
+ offset="1"
+ id="stop170-4" />
+ </linearGradient>
+ <linearGradient
+ id="bg-7-0"
+ y2="30"
+ xlink:href="#aq-0"
+ gradientUnits="userSpaceOnUse"
+ x2="30.038"
+ gradientTransform="matrix(0.62586,-0.62234,0.77349,0.76155,59.119,83.794)"
+ y1="24.99"
+ x1="30.038" />
+ <linearGradient
+ id="aq-0">
+ <stop
+ stop-color="#999"
+ offset="0"
+ id="stop173-4" />
+ <stop
+ stop-color="#fff"
+ offset=".5"
+ id="stop175-6" />
+ <stop
+ stop-color="#777"
+ offset="1"
+ id="stop177-2" />
+ </linearGradient>
+ <linearGradient
+ id="bh-2-6"
+ y2="30"
+ xlink:href="#ar-2-5"
+ gradientUnits="userSpaceOnUse"
+ x2="30.038"
+ gradientTransform="matrix(0.62586,-0.62234,0.77349,0.76155,59.921,82.996)"
+ y1="24.99"
+ x1="30.038" />
+ <linearGradient
+ id="bi-4-7"
+ y2="30"
+ xlink:href="#aq-0"
+ gradientUnits="userSpaceOnUse"
+ x2="30.038"
+ gradientTransform="matrix(0.62586,-0.62234,0.77349,0.76155,59.742,83.175)"
+ y1="24.99"
+ x1="30.038" />
+ <linearGradient
+ id="bj-0-7"
+ y2="30"
+ xlink:href="#ar-2-5"
+ gradientUnits="userSpaceOnUse"
+ x2="30.038"
+ gradientTransform="matrix(0.62586,-0.62234,0.77349,0.76155,60.547,82.374)"
+ y1="24.99"
+ x1="30.038" />
+ <linearGradient
+ id="bk-6-9"
+ y2="30"
+ xlink:href="#aq-0"
+ gradientUnits="userSpaceOnUse"
+ x2="30.038"
+ gradientTransform="matrix(0.62586,-0.62234,0.77349,0.76155,60.367,82.552)"
+ y1="24.99"
+ x1="30.038" />
+ <linearGradient
+ id="bl-2-6"
+ y2="26.030001"
+ gradientUnits="userSpaceOnUse"
+ x2="9"
+ gradientTransform="matrix(0.62586,-0.62234,0.77349,0.76155,59.06,83.852)"
+ y1="29.056999"
+ x1="9">
+ <stop
+ stop-color="#e4db7b"
+ offset="0"
+ id="stop203-9-1" />
+ <stop
+ stop-color="#f4f0c8"
+ offset="1"
+ id="stop205-9-3" />
+ </linearGradient>
+ <linearGradient
+ id="bm-0-2"
+ y2="41.391998"
+ gradientUnits="userSpaceOnUse"
+ x2="9.5221004"
+ gradientTransform="matrix(0.52586,0,0,0.51993,81.027,79.545)"
+ y1="37.372002"
+ x1="5.5179">
+ <stop
+ stop-color="#cbbd27"
+ offset="0"
+ id="stop208-1" />
+ <stop
+ stop-color="#9b901d"
+ offset="1"
+ id="stop210-5" />
+ </linearGradient>
+ </defs>
+ <g
+ id="g3352"
+ transform="matrix(1.0840142,0,0,1.0274941,-1.0097516,-0.11085364)">
+ <path
+ style="opacity:0.5;filter:url(#bb)"
+ inkscape:connector-curvature="0"
+ id="path217"
+ transform="matrix(0.21901212,0,0,0.21245446,-1.1052539,-0.69963571)"
+ d="m 16,8 v 112 h 63.187 c 3e-6,0 11.906,-9.9062 17.406,-15.406 C 102.09,99.106 112,87.2 112,87.2 V 8.012 H 16 z" />
+ <path
+ style="fill:#ffffff"
+ inkscape:connector-curvature="0"
+ id="path219"
+ d="M 2.8194588,1.20688 V 24.381003 H 16.096062 L 23.003525,17.58326 V 1.20688 H 2.8194588 z" />
+ <path
+ style="fill:url(#radialGradient3418)"
+ inkscape:connector-curvature="0"
+ id="path221"
+ d="m 3.2399477,1.4137496 c -0.1158487,0 -0.2102544,0.09287 -0.2102544,0.2068379 V 23.959337 c 0,0.114173 0.094406,0.206838 0.2102544,0.206838 H 15.681156 c 0.0553,0 0.580839,0.02444 0.619967,-0.01423 l 6.43045,-6.372949 c 0.03932,-0.03868 0.0616,-0.555495 0.0616,-0.609923 V 1.6206928 c 0,-0.1139678 -0.09419,-0.206838 -0.210255,-0.206838 H 3.2397334 z" />
+ <path
+ style="opacity:0.4;filter:url(#bc)"
+ inkscape:connector-curvature="0"
+ id="path223"
+ transform="matrix(0.21025443,0,0,0.20691162,7.8654255,-0.4483919)"
+ clip-path="url(#ba)"
+ d="M 41.88,115.98 66.19,91.67 c 0,0 -9.3531,2.9131 -19.603,2.9131 0,10.25 -4.7065,21.396 -4.7065,21.396 z" />
+ <g
+ id="g225"
+ transform="matrix(0.21025443,0,0,0.20691162,-22.411172,7.0004898)">
+ <g
+ id="g227"
+ transform="translate(116,-32)">
+ <circle
+ style="fill:url(#linearGradient3420)"
+ sodipodi:ry="2"
+ sodipodi:rx="2"
+ sodipodi:cy="110"
+ sodipodi:cx="10"
+ d="m 12,110 c 0,1.10457 -0.895431,2 -2,2 -1.1045695,0 -2,-0.89543 -2,-2 0,-1.10457 0.8954305,-2 2,-2 1.104569,0 2,0.89543 2,2 z"
+ id="circle229"
+ r="2"
+ cx="10"
+ cy="110" />
+ <circle
+ style="fill:url(#linearGradient3422)"
+ sodipodi:ry="1.556"
+ sodipodi:rx="1.556"
+ sodipodi:cy="110"
+ sodipodi:cx="10"
+ d="m 11.556,110 c 0,0.85936 -0.696645,1.556 -1.556,1.556 -0.8593551,0 -1.556,-0.69664 -1.556,-1.556 0,-0.85936 0.6966449,-1.556 1.556,-1.556 0.859355,0 1.556,0.69664 1.556,1.556 z"
+ id="circle231"
+ r="1.556"
+ cx="10"
+ cy="110" />
+ </g>
+ <g
+ id="g233"
+ transform="translate(116,-32)">
+ <circle
+ style="fill:url(#linearGradient3424)"
+ sodipodi:ry="2"
+ sodipodi:rx="2"
+ sodipodi:cy="102"
+ sodipodi:cx="10"
+ d="m 12,102 c 0,1.10457 -0.895431,2 -2,2 -1.1045695,0 -2,-0.89543 -2,-2 0,-1.10457 0.8954305,-2 2,-2 1.104569,0 2,0.89543 2,2 z"
+ id="circle235"
+ r="2"
+ cx="10"
+ cy="102" />
+ <circle
+ style="fill:url(#linearGradient3426)"
+ sodipodi:ry="1.556"
+ sodipodi:rx="1.556"
+ sodipodi:cy="102"
+ sodipodi:cx="10"
+ d="m 11.556,102 c 0,0.85936 -0.696645,1.556 -1.556,1.556 -0.8593551,0 -1.556,-0.69664 -1.556,-1.556 0,-0.85936 0.6966449,-1.556 1.556,-1.556 0.859355,0 1.556,0.69664 1.556,1.556 z"
+ id="circle237"
+ r="1.556"
+ cx="10"
+ cy="102" />
+ </g>
+ <g
+ id="g239"
+ transform="translate(116,-32)">
+ <circle
+ style="fill:url(#linearGradient3428)"
+ sodipodi:ry="2"
+ sodipodi:rx="2"
+ sodipodi:cy="94"
+ sodipodi:cx="10"
+ d="m 12,94 c 0,1.104569 -0.895431,2 -2,2 -1.1045695,0 -2,-0.895431 -2,-2 0,-1.104569 0.8954305,-2 2,-2 1.104569,0 2,0.895431 2,2 z"
+ id="circle241"
+ r="2"
+ cx="10"
+ cy="94" />
+ <circle
+ style="fill:url(#linearGradient3430)"
+ sodipodi:ry="1.556"
+ sodipodi:rx="1.556"
+ sodipodi:cy="94"
+ sodipodi:cx="10"
+ d="m 11.556,94 c 0,0.859355 -0.696645,1.556 -1.556,1.556 -0.8593551,0 -1.556,-0.696645 -1.556,-1.556 0,-0.859355 0.6966449,-1.556 1.556,-1.556 0.859355,0 1.556,0.696645 1.556,1.556 z"
+ id="circle243"
+ r="1.556"
+ cx="10"
+ cy="94" />
+ </g>
+ <g
+ id="g245"
+ transform="translate(116,-32)">
+ <circle
+ style="fill:url(#linearGradient3432)"
+ sodipodi:ry="2"
+ sodipodi:rx="2"
+ sodipodi:cy="86"
+ sodipodi:cx="10"
+ d="m 12,86 c 0,1.104569 -0.895431,2 -2,2 -1.1045695,0 -2,-0.895431 -2,-2 0,-1.104569 0.8954305,-2 2,-2 1.104569,0 2,0.895431 2,2 z"
+ id="circle247"
+ r="2"
+ cx="10"
+ cy="86" />
+ <circle
+ style="fill:url(#linearGradient3434)"
+ sodipodi:ry="1.556"
+ sodipodi:rx="1.556"
+ sodipodi:cy="86"
+ sodipodi:cx="10"
+ d="m 11.556,86 c 0,0.859355 -0.696645,1.556 -1.556,1.556 -0.8593551,0 -1.556,-0.696645 -1.556,-1.556 0,-0.859355 0.6966449,-1.556 1.556,-1.556 0.859355,0 1.556,0.696645 1.556,1.556 z"
+ id="circle249"
+ r="1.556"
+ cx="10"
+ cy="86" />
+ </g>
+ <g
+ id="g251"
+ transform="translate(116,-32)">
+ <circle
+ style="fill:url(#linearGradient3436)"
+ sodipodi:ry="2"
+ sodipodi:rx="2"
+ sodipodi:cy="78"
+ sodipodi:cx="10"
+ d="m 12,78 c 0,1.104569 -0.895431,2 -2,2 -1.1045695,0 -2,-0.895431 -2,-2 0,-1.104569 0.8954305,-2 2,-2 1.104569,0 2,0.895431 2,2 z"
+ id="circle253"
+ r="2"
+ cx="10"
+ cy="78" />
+ <circle
+ style="fill:url(#linearGradient3438)"
+ sodipodi:ry="1.556"
+ sodipodi:rx="1.556"
+ sodipodi:cy="78"
+ sodipodi:cx="10"
+ d="m 11.556,78 c 0,0.859355 -0.696645,1.556 -1.556,1.556 -0.8593551,0 -1.556,-0.696645 -1.556,-1.556 0,-0.859355 0.6966449,-1.556 1.556,-1.556 0.859355,0 1.556,0.696645 1.556,1.556 z"
+ id="circle255"
+ r="1.556"
+ cx="10"
+ cy="78" />
+ </g>
+ <g
+ id="g257"
+ transform="translate(116,-32)">
+ <circle
+ style="fill:url(#linearGradient3440)"
+ sodipodi:ry="2"
+ sodipodi:rx="2"
+ sodipodi:cy="70"
+ sodipodi:cx="10"
+ d="m 12,70 c 0,1.104569 -0.895431,2 -2,2 -1.1045695,0 -2,-0.895431 -2,-2 0,-1.104569 0.8954305,-2 2,-2 1.104569,0 2,0.895431 2,2 z"
+ id="circle259"
+ r="2"
+ cx="10"
+ cy="70" />
+ <circle
+ style="fill:url(#linearGradient3442)"
+ sodipodi:ry="1.556"
+ sodipodi:rx="1.556"
+ sodipodi:cy="70"
+ sodipodi:cx="10"
+ d="m 11.556,70 c 0,0.859355 -0.696645,1.556 -1.556,1.556 -0.8593551,0 -1.556,-0.696645 -1.556,-1.556 0,-0.859355 0.6966449,-1.556 1.556,-1.556 0.859355,0 1.556,0.696645 1.556,1.556 z"
+ id="circle261"
+ r="1.556"
+ cx="10"
+ cy="70" />
+ </g>
+ <g
+ id="g263"
+ transform="translate(116,-32)">
+ <circle
+ style="fill:url(#linearGradient3444)"
+ sodipodi:ry="2"
+ sodipodi:rx="2"
+ sodipodi:cy="62"
+ sodipodi:cx="10"
+ d="m 12,62 c 0,1.104569 -0.895431,2 -2,2 -1.1045695,0 -2,-0.895431 -2,-2 0,-1.104569 0.8954305,-2 2,-2 1.104569,0 2,0.895431 2,2 z"
+ id="circle265"
+ r="2"
+ cx="10"
+ cy="62" />
+ <circle
+ style="fill:url(#linearGradient3446)"
+ sodipodi:ry="1.556"
+ sodipodi:rx="1.556"
+ sodipodi:cy="62"
+ sodipodi:cx="10"
+ d="m 11.556,62 c 0,0.859355 -0.696645,1.556 -1.556,1.556 -0.8593551,0 -1.556,-0.696645 -1.556,-1.556 0,-0.859355 0.6966449,-1.556 1.556,-1.556 0.859355,0 1.556,0.696645 1.556,1.556 z"
+ id="circle267"
+ r="1.556"
+ cx="10"
+ cy="62" />
+ </g>
+ <g
+ id="g269"
+ transform="translate(116,-32)">
+ <circle
+ style="fill:url(#linearGradient3448)"
+ sodipodi:ry="2"
+ sodipodi:rx="2"
+ sodipodi:cy="54"
+ sodipodi:cx="10"
+ d="m 12,54 c 0,1.104569 -0.895431,2 -2,2 -1.1045695,0 -2,-0.895431 -2,-2 0,-1.104569 0.8954305,-2 2,-2 1.104569,0 2,0.895431 2,2 z"
+ id="circle271"
+ r="2"
+ cx="10"
+ cy="54" />
+ <circle
+ style="fill:url(#linearGradient3450)"
+ sodipodi:ry="1.556"
+ sodipodi:rx="1.556"
+ sodipodi:cy="54"
+ sodipodi:cx="10"
+ d="m 11.556,54 c 0,0.859355 -0.696645,1.556 -1.556,1.556 -0.8593551,0 -1.556,-0.696645 -1.556,-1.556 0,-0.859355 0.6966449,-1.556 1.556,-1.556 0.859355,0 1.556,0.696645 1.556,1.556 z"
+ id="circle273"
+ r="1.556"
+ cx="10"
+ cy="54" />
+ </g>
+ <g
+ id="g275"
+ transform="translate(116,-32)">
+ <circle
+ style="fill:url(#linearGradient3452)"
+ sodipodi:ry="2"
+ sodipodi:rx="2"
+ sodipodi:cy="46"
+ sodipodi:cx="10"
+ d="m 12,46 c 0,1.104569 -0.895431,2 -2,2 -1.1045695,0 -2,-0.895431 -2,-2 0,-1.104569 0.8954305,-2 2,-2 1.104569,0 2,0.895431 2,2 z"
+ id="circle277"
+ r="2"
+ cx="10"
+ cy="46" />
+ <circle
+ style="fill:url(#linearGradient3454)"
+ sodipodi:ry="1.556"
+ sodipodi:rx="1.556"
+ sodipodi:cy="46"
+ sodipodi:cx="10"
+ d="m 11.556,46 c 0,0.859355 -0.696645,1.556 -1.556,1.556 -0.8593551,0 -1.556,-0.696645 -1.556,-1.556 0,-0.859355 0.6966449,-1.556 1.556,-1.556 0.859355,0 1.556,0.696645 1.556,1.556 z"
+ id="circle279"
+ r="1.556"
+ cx="10"
+ cy="46" />
+ </g>
+ <g
+ id="g281"
+ transform="translate(116,-32)">
+ <circle
+ style="fill:url(#linearGradient3456)"
+ sodipodi:ry="2"
+ sodipodi:rx="2"
+ sodipodi:cy="38"
+ sodipodi:cx="10"
+ d="m 12,38 c 0,1.104569 -0.895431,2 -2,2 -1.1045695,0 -2,-0.895431 -2,-2 0,-1.104569 0.8954305,-2 2,-2 1.104569,0 2,0.895431 2,2 z"
+ id="circle283"
+ r="2"
+ cx="10"
+ cy="38" />
+ <circle
+ style="fill:url(#linearGradient3458)"
+ sodipodi:ry="1.556"
+ sodipodi:rx="1.556"
+ sodipodi:cy="38"
+ sodipodi:cx="10"
+ d="m 11.556,38 c 0,0.859355 -0.696645,1.556 -1.556,1.556 -0.8593551,0 -1.556,-0.696645 -1.556,-1.556 0,-0.859355 0.6966449,-1.556 1.556,-1.556 0.859355,0 1.556,0.696645 1.556,1.556 z"
+ id="circle285"
+ r="1.556"
+ cx="10"
+ cy="38" />
+ </g>
+ <g
+ id="g287"
+ transform="translate(116,-32)">
+ <circle
+ style="fill:url(#linearGradient3460)"
+ sodipodi:ry="2"
+ sodipodi:rx="2"
+ sodipodi:cy="30"
+ sodipodi:cx="10"
+ d="m 12,30 c 0,1.104569 -0.895431,2 -2,2 -1.1045695,0 -2,-0.895431 -2,-2 0,-1.104569 0.8954305,-2 2,-2 1.104569,0 2,0.895431 2,2 z"
+ id="circle289"
+ r="2"
+ cx="10"
+ cy="30" />
+ <circle
+ style="fill:url(#linearGradient3462)"
+ sodipodi:ry="1.556"
+ sodipodi:rx="1.556"
+ sodipodi:cy="30"
+ sodipodi:cx="10"
+ d="m 11.556,30 c 0,0.859355 -0.696645,1.556 -1.556,1.556 -0.8593551,0 -1.556,-0.696645 -1.556,-1.556 0,-0.859355 0.6966449,-1.556 1.556,-1.556 0.859355,0 1.556,0.696645 1.556,1.556 z"
+ id="circle291"
+ r="1.556"
+ cx="10"
+ cy="30" />
+ </g>
+ <g
+ id="g293"
+ transform="translate(116,-32)">
+ <circle
+ style="fill:url(#linearGradient3464)"
+ sodipodi:ry="2"
+ sodipodi:rx="2"
+ sodipodi:cy="22"
+ sodipodi:cx="10"
+ d="m 12,22 c 0,1.104569 -0.895431,2 -2,2 -1.1045695,0 -2,-0.895431 -2,-2 0,-1.104569 0.8954305,-2 2,-2 1.104569,0 2,0.895431 2,2 z"
+ id="circle295"
+ r="2"
+ cx="10"
+ cy="22" />
+ <circle
+ style="fill:url(#linearGradient3466)"
+ sodipodi:ry="1.556"
+ sodipodi:rx="1.556"
+ sodipodi:cy="22"
+ sodipodi:cx="10"
+ d="m 11.556,22 c 0,0.859355 -0.696645,1.556 -1.556,1.556 -0.8593551,0 -1.556,-0.696645 -1.556,-1.556 0,-0.859355 0.6966449,-1.556 1.556,-1.556 0.859355,0 1.556,0.696645 1.556,1.556 z"
+ id="circle297"
+ r="1.556"
+ cx="10"
+ cy="22" />
+ </g>
+ <g
+ id="g299"
+ transform="translate(116,-32)">
+ <circle
+ style="fill:url(#linearGradient3468)"
+ sodipodi:ry="2"
+ sodipodi:rx="2"
+ sodipodi:cy="14"
+ sodipodi:cx="10"
+ d="m 12,14 c 0,1.104569 -0.895431,2 -2,2 -1.1045695,0 -2,-0.895431 -2,-2 0,-1.104569 0.8954305,-2 2,-2 1.104569,0 2,0.895431 2,2 z"
+ id="circle301"
+ r="2"
+ cx="10"
+ cy="14" />
+ <circle
+ style="fill:url(#linearGradient3470)"
+ sodipodi:ry="1.556"
+ sodipodi:rx="1.556"
+ sodipodi:cy="14"
+ sodipodi:cx="10"
+ d="m 11.556,14 c 0,0.859355 -0.696645,1.556 -1.556,1.556 -0.8593551,0 -1.556,-0.696645 -1.556,-1.556 0,-0.859355 0.6966449,-1.556 1.556,-1.556 0.859355,0 1.556,0.696645 1.556,1.556 z"
+ id="circle303"
+ r="1.556"
+ cx="10"
+ cy="14" />
+ </g>
+ </g>
+ <g
+ style="opacity:0.38462004;stroke:#000000;stroke-linecap:round;stroke-linejoin:round"
+ id="g305"
+ transform="matrix(-0.59030942,0,0,-0.63192583,27.618336,27.775348)">
+ <path
+ inkscape:connector-curvature="0"
+ id="path307"
+ d="M 16.728,8.2961 20.5,12.5 v 10"
+ display="block"
+ style="color:#000000;fill:none;display:block" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path309"
+ d="m 24.5,22.5 v -12 l -3,-3"
+ display="block"
+ style="color:#000000;fill:none;display:block" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path311"
+ d="m 28.5,22.5 v -14 l -1,-1"
+ display="block"
+ style="color:#000000;fill:none;display:block" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path313"
+ d="M 32.5,22.5 V 7.5"
+ display="block"
+ style="color:#000000;fill:none;display:block" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path315"
+ display="block"
+ d="m 20.5,22.5 v 2"
+ style="color:#000000;fill:none;stroke-width:2;display:block" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path317"
+ display="block"
+ d="m 24.5,22.5 v 2"
+ style="color:#000000;fill:none;stroke-width:2;display:block" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path319"
+ display="block"
+ d="m 28.5,22.5 v 2"
+ style="color:#000000;fill:none;stroke-width:2;display:block" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path321"
+ display="block"
+ d="m 32.5,22.5 v 2"
+ style="color:#000000;fill:none;stroke-width:2;display:block" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path323"
+ display="block"
+ d="m 32.5,31.5 v 2"
+ style="color:#000000;fill:none;stroke-width:2;display:block" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path325"
+ display="block"
+ d="m 28.5,31.5 v 2"
+ style="color:#000000;fill:none;stroke-width:2;display:block" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path327"
+ display="block"
+ d="m 24.5,31.5 v 2"
+ style="color:#000000;fill:none;stroke-width:2;display:block" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path329"
+ display="block"
+ d="m 20.5,31.5 v 2"
+ style="color:#000000;fill:none;stroke-width:2;display:block" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path331"
+ d="m 20.5,33.5 v 4 l -3,3 h -7"
+ display="block"
+ style="color:#000000;fill:none;display:block" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path333"
+ d="m 24.5,33.5 v 7"
+ display="block"
+ style="color:#000000;fill:none;display:block" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path335"
+ d="m 28.5,33.5 v 4 l 3,3 h 6"
+ display="block"
+ style="color:#000000;fill:none;display:block" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path337"
+ d="m 32.5,33.5 2,2 h 3"
+ display="block"
+ style="color:#000000;fill:none;display:block" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path339"
+ d="m 10.888,13.663 c 1.6249,-1.4406 3.4028,-3.3721 3.4028,-3.3721 0.01885,0.01796 1.7133,1.7863 3.2087,3.2089 v 22 l -2,2 h -5 z"
+ display="block"
+ style="color:#000000;display:block" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path341"
+ d="m 37.5,32.5 h -1 v -12 l -1,-1 v -12 h 2 v 25 z"
+ display="block"
+ style="color:#000000;display:block" />
+ </g>
+ <path
+ style="fill:url(#linearGradient3472)"
+ inkscape:connector-curvature="0"
+ id="path345"
+ d="m 16.101745,24.380687 c 0,0 2.504939,-2.051324 3.661334,-3.189317 1.156394,-1.137993 3.240845,-3.603109 3.240845,-3.603109 0,0 -2.890986,1.82661 -5.046266,1.82661 0,2.120808 -1.856113,4.965816 -1.856113,4.965816 z" />
+ </g>
+ <g
+ transform="matrix(0.63117684,-0.08174752,0.08600797,0.59988584,-47.68986,-33.098749)"
+ id="g616-9">
+ <path
+ style="fill:url(#be-2-3);stroke:#000000;stroke-width:0.55532998;stroke-linejoin:round"
+ d="m 85.365,96.011 c 0.43282,-0.32935 1.5837,0.20459 2.6183,1.2232 1.0321,1.0162 1.5508,2.1286 1.225,2.5608 -0.0012,0.0017 0.02544,0.02217 0.02417,0.0238 l 15.708,-15.618 c 0.39874,-0.3965 -0.14267,-1.5725 -1.2101,-2.6235 -1.0674,-1.0509 -2.2586,-1.5808 -2.6574,-1.1843 l -15.705,15.618 z"
+ id="path618-9" />
+ <path
+ style="opacity:0.8;fill:#ff9de8;stroke:#dd78c5;stroke-width:0.55532998;stroke-linejoin:round"
+ d="m 99.153,82.3 c 0.43282,-0.32935 1.5837,0.20459 2.6183,1.2232 1.0321,1.0162 1.5508,2.1285 1.225,2.5608 -0.001,0.0017 0.0254,0.02217 0.0242,0.0238 l 1.9167,-1.9059 0.0391,-0.0389 c 0.001,-0.0017 -0.0254,-0.02215 -0.0242,-0.0238 0.32575,-0.43223 -0.19292,-1.5446 -1.225,-2.5608 -1.0346,-1.0186 -2.1854,-1.5525 -2.6183,-1.2232 l -0.04,0.039 -1.917,1.906 z"
+ id="path620-1" />
+ <path
+ style="opacity:0.6"
+ d="m 85.365,96.011 c 0.43282,-0.32935 1.5837,0.20459 2.6183,1.2232 1.0321,1.0162 1.5508,2.1286 1.225,2.5608 -0.0012,0.0017 0.02544,0.02217 0.02417,0.0238 l 10.816,-10.755 0.0391,-0.0389 c 0.001,-0.0017 -0.0254,-0.02216 -0.0242,-0.0238 0.32575,-0.43223 -0.19293,-1.5446 -1.225,-2.5608 -1.0346,-1.0186 -2.1854,-1.5525 -2.6183,-1.2232 l -0.03911,0.0389 -10.816,10.755 z"
+ id="path622-4" />
+ <path
+ style="fill:url(#bf-6-0)"
+ d="m 97.005,84.436 c 0.43282,-0.32935 1.5837,0.20459 2.6183,1.2232 1.0321,1.0162 1.5508,2.1286 1.225,2.5608 -10e-4,0.0017 0.0254,0.02217 0.0242,0.0238 l 0.19558,-0.19448 c 0.001,-0.0017 -0.0254,-0.02215 -0.0242,-0.0238 0.32575,-0.43222 -0.19292,-1.5446 -1.225,-2.5608 -1.0346,-1.0186 -2.1854,-1.5525 -2.6183,-1.2232 l -0.197,0.194 z"
+ id="path624-9" />
+ <path
+ style="fill:url(#bg-7-0)"
+ d="m 96.826,84.614 c 0.43282,-0.32935 1.5837,0.20459 2.6183,1.2232 1.0321,1.0162 1.5508,2.1285 1.225,2.5608 -0.001,0.0017 0.0254,0.02217 0.0242,0.0238 l 0.19558,-0.19448 c 10e-4,-0.0017 -0.0254,-0.02215 -0.0242,-0.0238 0.32575,-0.43222 -0.19292,-1.5446 -1.225,-2.5608 -1.0346,-1.0186 -2.1854,-1.5525 -2.6183,-1.2232 l -0.196,0.194 z"
+ id="path626-1" />
+ <path
+ style="fill:url(#bh-2-6)"
+ d="m 97.628,83.817 c 0.43282,-0.32935 1.5837,0.20459 2.6183,1.2232 1.0321,1.0162 1.5508,2.1286 1.225,2.5608 -0.001,0.0017 0.0254,0.02217 0.0242,0.0238 l 0.19558,-0.19448 c 0.001,-0.0017 -0.0254,-0.02215 -0.0242,-0.0238 0.32575,-0.43222 -0.19292,-1.5446 -1.225,-2.5608 -1.0346,-1.0186 -2.1854,-1.5525 -2.6183,-1.2232 l -0.204,0.194 z"
+ id="path628-0" />
+ <path
+ style="fill:url(#bi-4-7)"
+ d="m 97.448,83.995 c 0.43282,-0.32935 1.5837,0.20459 2.6183,1.2232 1.0321,1.0162 1.5508,2.1286 1.225,2.5608 -0.001,0.0017 0.0254,0.02217 0.0242,0.0238 l 0.19558,-0.19448 c 10e-4,-0.0017 -0.0254,-0.02215 -0.0242,-0.0238 0.32575,-0.43222 -0.19292,-1.5446 -1.225,-2.5608 -1.0346,-1.0186 -2.1854,-1.5525 -2.6183,-1.2232 l -0.204,0.194 z"
+ id="path630-7" />
+ <path
+ style="fill:url(#bj-0-7)"
+ d="m 98.254,83.194 c 0.43282,-0.32935 1.5837,0.20459 2.6183,1.2232 1.0321,1.0162 1.5508,2.1286 1.225,2.5608 -0.001,0.0017 0.0254,0.02217 0.0242,0.0238 l 0.19558,-0.19448 c 10e-4,-0.0017 -0.0254,-0.02216 -0.0242,-0.0238 0.32575,-0.43223 -0.19292,-1.5446 -1.225,-2.5608 -1.0346,-1.0186 -2.1854,-1.5525 -2.6183,-1.2232 l -0.198,0.194 z"
+ id="path632-5" />
+ <path
+ style="fill:url(#bk-6-9)"
+ d="m 98.074,83.372 c 0.43282,-0.32935 1.5837,0.20459 2.6183,1.2232 1.0321,1.0162 1.5508,2.1286 1.225,2.5608 -10e-4,0.0017 0.0254,0.02217 0.0242,0.0238 l 0.19558,-0.19448 c 0.001,-0.0017 -0.0254,-0.02216 -0.0242,-0.0238 0.32575,-0.43223 -0.19292,-1.5446 -1.225,-2.5608 -1.0346,-1.0186 -2.1854,-1.5525 -2.6183,-1.2232 l -0.198,0.194 z"
+ id="path634-8" />
+ <path
+ style="fill:url(#bl-2-6);fill-rule:evenodd;stroke:url(#bm-0-2);stroke-width:0.55532998"
+ d="m 82.185,102.91 7.0294,-3.0074 c 0.32575,-0.43222 -0.20033,-1.6548 -1.2324,-2.671 -1.0346,-1.0186 -2.2398,-1.5494 -2.6726,-1.22 l -3.124,6.898 z"
+ id="path636-7" />
+ <path
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.55532998"
+ d="m 83.046,101.04 -0.86146,1.8612 1.9008,-0.83782 c -0.14196,-0.17111 -0.2723,-0.3455 -0.44432,-0.51486 -0.19804,-0.19498 -0.39436,-0.35167 -0.59506,-0.50847 z"
+ id="path638-0" />
+ </g>
+</svg>
diff --git a/resources/linux/mime/mime/packages/kicad.xml b/resources/linux/mime/mime/packages/kicad.xml
new file mode 100644
index 0000000..3b945dd
--- /dev/null
+++ b/resources/linux/mime/mime/packages/kicad.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
+ <mime-type type="application/x-kicad-project">
+ <sub-class-of type="text/plain"/>
+ <comment>KiCad Project</comment>
+ <comment xml:lang="fr">Projet KiCad</comment>
+ <glob pattern="*.pro"/>
+ </mime-type>
+ <mime-type type="application/x-kicad-schematic">
+ <sub-class-of type="text/plain"/>
+ <comment>KiCad Schematic</comment>
+ <comment xml:lang="fr">Schéma électronique KiCad</comment>
+ <glob pattern="*.sch"/>
+ </mime-type>
+ <mime-type type="application/x-kicad-pcbnew">
+ <sub-class-of type="text/plain"/>
+ <comment>KiCad PCB</comment>
+ <comment xml:lang="fr">Circuit imprimé KiCad</comment>
+ <glob pattern="*.brd"/>
+ </mime-type>
+</mime-info>
diff --git a/resources/linux/mime/mimelnk/application/x-kicad-pcb.desktop b/resources/linux/mime/mimelnk/application/x-kicad-pcb.desktop
new file mode 100644
index 0000000..7a5afb8
--- /dev/null
+++ b/resources/linux/mime/mimelnk/application/x-kicad-pcb.desktop
@@ -0,0 +1,8 @@
+[Desktop Entry]
+Type=MimeType
+MimeType=application/x-kicad-pcb
+Icon=pcbnew
+Patterns=*.kicad_pcb
+
+Comment=KiCad Printed Circuit Board
+#Comment[fr]=Schéma électronique KiCad
diff --git a/resources/linux/mime/mimelnk/application/x-kicad-project.desktop b/resources/linux/mime/mimelnk/application/x-kicad-project.desktop
new file mode 100644
index 0000000..bdae413
--- /dev/null
+++ b/resources/linux/mime/mimelnk/application/x-kicad-project.desktop
@@ -0,0 +1,7 @@
+[Desktop Entry]
+Type=MimeType
+MimeType=application/x-kicad-project
+Icon=kicad
+Patterns=*.pro
+Comment=KiCad Project
+Comment[fr]=Projet KiCad
diff --git a/resources/linux/mime/mimelnk/application/x-kicad-schematic.desktop b/resources/linux/mime/mimelnk/application/x-kicad-schematic.desktop
new file mode 100644
index 0000000..f34abc3
--- /dev/null
+++ b/resources/linux/mime/mimelnk/application/x-kicad-schematic.desktop
@@ -0,0 +1,7 @@
+[Desktop Entry]
+Type=MimeType
+MimeType=application/x-kicad-schematic
+Icon=eeschema
+Patterns=*.sch
+Comment=KiCad Schematic
+Comment[fr]=Schéma électronique KiCad
diff --git a/rules b/rules
new file mode 100644
index 0000000..13dfb51
--- /dev/null
+++ b/rules
@@ -0,0 +1,27 @@
+# This is the Bazaar "rules" file for KiCad Developers.
+# http://doc.bazaar.canonical.com/beta/en/user-reference/eol-help.html
+
+# To put it into use, you must _copy_ it to your BZR_HOME directory, where BZR_HOME
+# varies by platform. In that target directory, described in more detail below,
+# simply make sure this file has the name "rules", but without quotes.
+
+# platform BZR_RULES dir and rules filename
+#--------- --------------------------------
+# linux ~/.bazaar/rules
+# WinXP C:\Documents and Settings\<username>\Application Data\Bazaar\2.0\rules
+# Win7 C:\Users\<username>\AppData\Roaming\bazaar\2.0\rules
+
+
+# the actual rules:
+
+[name *.png]
+eol = exact
+
+[name *.pdf]
+eol = exact
+
+[name *.sh]
+eol = lf
+
+[name *]
+eol = native
diff --git a/scripting/build_tools/extract_docstrings.py b/scripting/build_tools/extract_docstrings.py
new file mode 100644
index 0000000..103654e
--- /dev/null
+++ b/scripting/build_tools/extract_docstrings.py
@@ -0,0 +1,405 @@
+#!/usr/bin/env python
+"""Doxygen XML to SWIG docstring converter.
+
+Converts Doxygen generated XML files into a file containing docstrings
+that can be used by SWIG >1.3.23
+
+Usage:
+
+ extract-docstrings.py input_py_wrapper.py input_xml_dir output_directory
+
+input_py_wrapper.py is a swig generated file, with/without docstrings,
+ so we can get to know which classes are inspected by swig
+
+input_xml_dir is your doxygen generated XML directory
+
+output_directory is the directory where output will be written
+
+"""
+
+# This code is implemented using Mark Pilgrim's code as a guideline:
+# http://www.faqs.org/docs/diveintopython/kgp_divein.html
+# Based in doxy2swig.py
+# Author: Prabhu Ramachandran
+# License: BSD style
+
+
+from xml.dom import minidom
+import re
+import textwrap
+import sys
+import types
+import os.path
+
+
+def my_open_read(source):
+ if hasattr(source, "read"):
+ return source
+ else:
+ return open(source)
+
+def my_open_write(dest):
+ if hasattr(dest, "write"):
+ return dest
+ else:
+ return open(dest, 'w')
+
+
+class Doxy2SWIG:
+ """Converts Doxygen generated XML files into a file containing
+ docstrings that can be used by SWIG-1.3.x that have support for
+ feature("docstring"). Once the data is parsed it is stored in
+ self.pieces.
+
+ """
+
+ def __init__(self, src):
+ """Initialize the instance given a source object (file or
+ filename).
+
+ """
+ f = my_open_read(src)
+ self.my_dir = os.path.dirname(f.name)
+ self.xmldoc = minidom.parse(f).documentElement
+ f.close()
+
+ self.pieces = []
+ self.pieces.append('\n// File: %s\n'%\
+ os.path.basename(f.name))
+
+ self.space_re = re.compile(r'\s+')
+ self.lead_spc = re.compile(r'^(%feature\S+\s+\S+\s*?)"\s+(\S)')
+ self.multi = 0
+ self.ignores = ('inheritancegraph', 'param', 'listofallmembers',
+ 'innerclass', 'name', 'declname', 'incdepgraph',
+ 'invincdepgraph', 'programlisting', 'type',
+ 'references', 'referencedby', 'location',
+ 'collaborationgraph', 'reimplements',
+ 'reimplementedby', 'derivedcompoundref',
+ 'basecompoundref')
+ #self.generics = []
+
+ def generate(self):
+ """Parses the file set in the initialization. The resulting
+ data is stored in `self.pieces`.
+
+ """
+ self.parse(self.xmldoc)
+
+ def parse(self, node):
+ """Parse a given node. This function in turn calls the
+ `parse_<nodeType>` functions which handle the respective
+ nodes.
+
+ """
+ pm = getattr(self, "parse_%s"%node.__class__.__name__)
+ pm(node)
+
+ def parse_Document(self, node):
+ self.parse(node.documentElement)
+
+ def parse_Text(self, node):
+ txt = node.data
+ txt = txt.replace('\\', r'\\\\')
+ txt = txt.replace('"', r'\"')
+ # ignore pure whitespace
+ m = self.space_re.match(txt)
+ if m and len(m.group()) == len(txt):
+ pass
+ else:
+ self.add_text(textwrap.fill(txt))
+
+ def parse_Element(self, node):
+ """Parse an `ELEMENT_NODE`. This calls specific
+ `do_<tagName>` handers for different elements. If no handler
+ is available the `generic_parse` method is called. All
+ tagNames specified in `self.ignores` are simply ignored.
+
+ """
+ name = node.tagName
+ ignores = self.ignores
+ if name in ignores:
+ return
+ attr = "do_%s" % name
+ if hasattr(self, attr):
+ handlerMethod = getattr(self, attr)
+ handlerMethod(node)
+ else:
+ self.generic_parse(node)
+ #if name not in self.generics: self.generics.append(name)
+
+ def add_text(self, value):
+ """Adds text corresponding to `value` into `self.pieces`."""
+ if type(value) in (types.ListType, types.TupleType):
+ self.pieces.extend(value)
+ else:
+ self.pieces.append(value)
+
+ def get_specific_nodes(self, node, names):
+ """Given a node and a sequence of strings in `names`, return a
+ dictionary containing the names as keys and child
+ `ELEMENT_NODEs`, that have a `tagName` equal to the name.
+
+ """
+ nodes = [(x.tagName, x) for x in node.childNodes \
+ if x.nodeType == x.ELEMENT_NODE and \
+ x.tagName in names]
+ return dict(nodes)
+
+ def generic_parse(self, node, pad=0):
+ """A Generic parser for arbitrary tags in a node.
+
+ Parameters:
+
+ - node: A node in the DOM.
+ - pad: `int` (default: 0)
+
+ If 0 the node data is not padded with newlines. If 1 it
+ appends a newline after parsing the childNodes. If 2 it
+ pads before and after the nodes are processed. Defaults to
+ 0.
+
+ """
+ npiece = 0
+ if pad:
+ npiece = len(self.pieces)
+ if pad == 2:
+ self.add_text('\n')
+ for n in node.childNodes:
+ self.parse(n)
+ if pad:
+ if len(self.pieces) > npiece:
+ self.add_text('\n')
+
+ def space_parse(self, node):
+ self.add_text(' ')
+ self.generic_parse(node)
+
+ do_ref = space_parse
+ do_emphasis = space_parse
+ do_bold = space_parse
+ do_computeroutput = space_parse
+ do_formula = space_parse
+
+ def do_compoundname(self, node):
+ self.add_text('\n\n')
+ data = node.firstChild.data
+ self.add_text('%%feature("docstring") %s "\n'%data)
+
+ def do_compounddef(self, node):
+ kind = node.attributes['kind'].value
+ if kind in ('class', 'struct'):
+ prot = node.attributes['prot'].value
+ if prot <> 'public':
+ return
+ names = ('compoundname', 'briefdescription',
+ 'detaileddescription', 'includes')
+ first = self.get_specific_nodes(node, names)
+ for n in names:
+ if first.has_key(n):
+ self.parse(first[n])
+ self.add_text(['";','\n'])
+ for n in node.childNodes:
+ if n not in first.values():
+ self.parse(n)
+ elif kind in ('file', 'namespace'):
+ nodes = node.getElementsByTagName('sectiondef')
+ for n in nodes:
+ self.parse(n)
+
+ def do_includes(self, node):
+ self.add_text('C++ includes: ')
+ self.generic_parse(node, pad=1)
+
+ def do_parameterlist(self, node):
+ self.add_text(['\n', '\n', 'Parameters:', '\n'])
+ self.generic_parse(node, pad=1)
+
+ def do_para(self, node):
+ self.add_text('\n')
+ self.generic_parse(node, pad=1)
+
+ def do_parametername(self, node):
+ self.add_text('\n')
+ try:
+ self.add_text("%s: "%node.firstChild.data)
+ except AttributeError:
+ self.add_text("???: ")
+
+ def do_parameterdefinition(self, node):
+ self.generic_parse(node, pad=1)
+
+ def do_detaileddescription(self, node):
+ self.generic_parse(node, pad=1)
+
+ def do_briefdescription(self, node):
+ self.generic_parse(node, pad=1)
+
+ def do_memberdef(self, node):
+ prot = node.attributes['prot'].value
+ id = node.attributes['id'].value
+ kind = node.attributes['kind'].value
+ tmp = node.parentNode.parentNode.parentNode
+ compdef = tmp.getElementsByTagName('compounddef')[0]
+ cdef_kind = compdef.attributes['kind'].value
+
+ if prot == 'public':
+ first = self.get_specific_nodes(node, ('definition', 'name'))
+ name = first['name'].firstChild.data
+ if name[:8] == 'operator': # Don't handle operators yet.
+ return
+
+ defn = first['definition'].firstChild.data
+ self.add_text('\n')
+ self.add_text('%feature("docstring") ')
+
+ anc = node.parentNode.parentNode
+ if cdef_kind in ('file', 'namespace'):
+ ns_node = anc.getElementsByTagName('innernamespace')
+ if not ns_node and cdef_kind == 'namespace':
+ ns_node = anc.getElementsByTagName('compoundname')
+ if ns_node:
+ ns = ns_node[0].firstChild.data
+ self.add_text(' %s::%s "\n%s'%(ns, name, defn))
+ else:
+ self.add_text(' %s "\n%s'%(name, defn))
+ elif cdef_kind in ('class', 'struct'):
+ # Get the full function name.
+ anc_node = anc.getElementsByTagName('compoundname')
+ cname = anc_node[0].firstChild.data
+ self.add_text(' %s::%s "\n%s'%(cname, name, defn))
+
+
+ for n in node.childNodes:
+ if n not in first.values():
+ self.parse(n)
+ self.add_text(['";', '\n'])
+
+ def do_definition(self, node):
+ data = node.firstChild.data
+ self.add_text('%s "\n%s'%(data, data))
+
+ def do_sectiondef(self, node):
+ kind = node.attributes['kind'].value
+ if kind in ('public-func', 'func'):
+ self.generic_parse(node)
+
+ def do_simplesect(self, node):
+ kind = node.attributes['kind'].value
+ if kind in ('date', 'rcs', 'version'):
+ pass
+ elif kind == 'warning':
+ self.add_text(['\n', 'WARNING: '])
+ self.generic_parse(node)
+ elif kind == 'see':
+ self.add_text('\n')
+ self.add_text('See: ')
+ self.generic_parse(node)
+ else:
+ self.generic_parse(node)
+
+ def do_argsstring(self, node):
+ self.generic_parse(node, pad=1)
+
+ def do_member(self, node):
+ kind = node.attributes['kind'].value
+ refid = node.attributes['refid'].value
+ if kind == 'function' and refid[:9] == 'namespace':
+ self.generic_parse(node)
+
+ def do_doxygenindex(self, node):
+ self.multi = 1
+ comps = node.getElementsByTagName('compound')
+ for c in comps:
+ refid = c.attributes['refid'].value
+ fname = refid + '.xml'
+ if not os.path.exists(fname):
+ fname = os.path.join(self.my_dir, fname)
+ print "parsing file: %s"%fname
+ p = Doxy2SWIG(fname)
+ p.generate()
+ self.pieces.extend(self.clean_pieces(p.pieces))
+
+ def write(self, fname):
+ o = my_open_write(fname)
+ if self.multi:
+ o.write("".join(self.pieces))
+ else:
+ o.write("".join(self.clean_pieces(self.pieces)))
+ o.close()
+
+ def clean_pieces(self, pieces):
+ """Cleans the list of strings given as `pieces`. It replaces
+ multiple newlines by a maximum of 2 and returns a new list.
+ It also wraps the paragraphs nicely.
+
+ """
+ ret = []
+ count = 0
+ for i in pieces:
+ if i == '\n':
+ count = count + 1
+ else:
+ if i == '";':
+ if count:
+ ret.append('\n')
+ elif count > 2:
+ ret.append('\n\n')
+ elif count:
+ ret.append('\n'*count)
+ count = 0
+ ret.append(i)
+
+ _data = "".join(ret)
+ ret = []
+ for i in _data.split('\n\n'):
+ if i == 'Parameters:':
+ ret.extend(['Parameters:\n-----------', '\n\n'])
+ elif i.find('// File:') > -1: # leave comments alone.
+ ret.extend([i, '\n'])
+ else:
+ _tmp = textwrap.fill(i.strip())
+ _tmp = self.lead_spc.sub(r'\1"\2', _tmp)
+ ret.extend([_tmp, '\n\n'])
+ return ret
+
+
+def get_python_classes(input_py):
+ with open(input_py) as f:
+ data = f.read()
+ classes_supers = re.findall(r'class[ ]+([\w_]+)(\([\w_, ]+\))?:',data)
+ classes = (classname for classname,superclass in classes_supers)
+ return classes
+ return []
+
+def main(input_py, input_xml, output_dir):
+
+ classes = get_python_classes(input_py)
+
+ with file("%s/docstrings.i"%output_dir,'w') as f_index:
+
+ for classname in classes:
+
+
+ class_file = "%s/class%s.xml"%(input_xml,classname.replace("_","__"))
+ swig_file = "%s/%s.i"%(output_dir,classname.lower())
+
+ if os.path.isfile(class_file):
+ print "processing:",class_file," ->",swig_file
+ p = Doxy2SWIG(class_file)
+ p.generate()
+ p.write(swig_file)
+ f_index.write('%%include "%s.i"\n'% classname.lower())
+ #else:
+ # print "ignoring class %s, as %s does not exist" %(classname,class_file)
+
+
+
+
+
+if __name__ == '__main__':
+ print sys.argv
+ if len(sys.argv) != 4:
+ print __doc__
+ sys.exit(1)
+ main(sys.argv[1], sys.argv[2],sys.argv[3])
diff --git a/scripting/build_tools/fix_swig_imports.py b/scripting/build_tools/fix_swig_imports.py
new file mode 100644
index 0000000..bcf9dbd
--- /dev/null
+++ b/scripting/build_tools/fix_swig_imports.py
@@ -0,0 +1,77 @@
+#!/usr/bin/env python
+
+# the purpose of this script is rewriting the swig_import_helper
+# call so it will not load _xxxxx.so/dso from inside a kicad executable
+# because the kicad executable itself sill provide an _xxxxx module
+# that's linked inside itself.
+#
+# for the normal module import it should work the same way with this
+# fix in the swig_import_helper
+#
+
+from sys import argv,exit
+
+if len(argv)<2:
+ print "usage:"
+ print " fix_swig_imports.py file.py"
+ print ""
+ print " will fix the swig import code for working inside KiCad"
+ print " where it happended that the external _pcbnew.so/dll was"
+ print " loaded too -and the internal _pcbnew module was to be used"
+ exit(1)
+
+
+filename = argv[1]
+
+f = open(filename,"rb")
+lines = f.readlines()
+f.close()
+
+
+doneOk = False
+
+if (len(lines)<4000):
+ print "still building"
+ exit(0)
+
+txt = ""
+
+for l in lines:
+ if l.startswith("if _swig_python_version_info >= (2, 7, 0):"): # ok with swig version >= 3.0.10
+ l = l.replace("_swig_python_version_info >= (2, 7, 0)","False")
+ doneOk = True
+ elif l.startswith("elif _swig_python_version_info >= (2, 6, 0):"): # needed with swig version >= 3.0.10
+ l = l.replace("_swig_python_version_info >= (2, 6, 0)","False")
+ doneOk = True
+ if l.startswith("if version_info >= (2, 7, 0):"): # ok with swig version >= 3.0.9
+ l = l.replace("version_info >= (2, 7, 0)","False")
+ doneOk = True
+ elif l.startswith("elif version_info >= (2, 6, 0):"): # needed with swig version >= 3.0.9
+ l = l.replace("version_info >= (2, 6, 0)","False")
+ doneOk = True
+ elif l.startswith("if version_info >= (2,6,0):"): # ok with swig version <= 3.0.2
+ l = l.replace("version_info >= (2,6,0)","False")
+ doneOk = True
+ elif l.startswith("if version_info >= (2, 6, 0):"): # needed with swig version 3.0.3
+ l = l.replace("version_info >= (2, 6, 0)","False")
+ doneOk = True
+ elif l.startswith("if False:"): # it was already patched?
+ doneOk = True
+ txt = txt + l
+
+f = open(filename,"wb")
+f.write(txt)
+f.close()
+
+if doneOk:
+ print "swig_import_helper fixed for",filename
+else:
+ print "Error: the swig import helper was not fixed, check",filename
+ print " and fix this script: fix_swig_imports.py"
+ exit(2)
+
+
+exit(0)
+
+
+
diff --git a/scripting/dlist.i b/scripting/dlist.i
new file mode 100644
index 0000000..9ea7712
--- /dev/null
+++ b/scripting/dlist.i
@@ -0,0 +1,67 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2012 NBEE Embedded Systems, Miguel Angel Ajo <miguelangel@nbee.es>
+ * Copyright (C) 1992-2012 KiCad Developers, see AUTHORS.txt for contributors.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/* DLIST python iteration code, to allow standard iteration over DLIST */
+
+%extend DLIST
+{
+ %pythoncode
+ %{
+ class DLISTIter:
+ def __init__(self,aList):
+ self.last = aList # last item is the start of list
+
+ def next(self): # get the next item
+
+ item = self.last
+ try:
+ item = item.Get()
+ except:
+ pass
+
+ if item is None: # if the item is None, then finish the iteration
+ raise StopIteration
+ else:
+ ret = None
+
+ # first item in list has "Get" as a DLIST
+ try:
+ ret = self.last.Get()
+ except:
+ ret = self.last # next items do not..
+
+ self.last = self.last.Next()
+
+ # when the iterated object can be casted down in inheritance, just do it..
+
+ if 'Cast' in dir(ret):
+ ret = ret.Cast()
+
+ return ret
+
+ def __iter__(self):
+ return self.DLISTIter(self)
+
+ %}
+}
diff --git a/scripting/kicad.i b/scripting/kicad.i
new file mode 100644
index 0000000..073c112
--- /dev/null
+++ b/scripting/kicad.i
@@ -0,0 +1,160 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2012 NBEE Embedded Systems, Miguel Angel Ajo <miguelangel@nbee.es>
+ * Copyright (C) 1992-2012 KiCad Developers, see AUTHORS.txt for contributors.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * @file kicad.i
+ * @brief General wrappers for kicad / wx structures and classes
+ */
+
+%include <std_vector.i>
+%include <std_basic_string.i>
+%include <std_string.i>
+%include <std_map.i>
+
+/* ignore some constructors of EDA_ITEM that will make the build fail */
+
+%nodefaultctor EDA_ITEM;
+%ignore EDA_ITEM::EDA_ITEM( EDA_ITEM* parent, KICAD_T idType );
+%ignore EDA_ITEM::EDA_ITEM( KICAD_T idType );
+%ignore EDA_ITEM::EDA_ITEM( const EDA_ITEM& base );
+
+/* swig tries to wrap SetBack/SetNext on derived classes, but this method is
+ private for most childs, so if we don't ignore it won't compile */
+
+%ignore EDA_ITEM::SetBack;
+%ignore EDA_ITEM::SetNext;
+
+/* ignore other functions that cause trouble */
+
+%ignore InitKiCadAbout;
+%ignore GetCommandOptions;
+
+%rename(getWxRect) operator wxRect;
+%ignore operator <<;
+%ignore operator=;
+
+
+/* headers/imports that must be included in the _wrapper.cpp at top */
+
+%{
+ #include <cstddef>
+ #include <dlist.h>
+ #include <base_struct.h>
+ #include <class_eda_rect.h>
+ #include <common.h>
+ #include <wx_python_helpers.h>
+ #include <cstddef>
+ #include <vector>
+ #include <bitset>
+
+ #include <class_title_block.h>
+ #include <class_colors_design_settings.h>
+ #include <class_marker_base.h>
+ #include <eda_text.h>
+ #include <convert_from_iu.h>
+ #include <convert_to_biu.h>
+%}
+
+/* all the wx wrappers for wxString, wxPoint, wxRect, wxChar .. */
+%include <wx.i>
+
+/* exception handling */
+
+/* the IO_ERROR exception handler, not working yet... */
+/*
+%exception
+{
+ try {
+ $function
+ }
+ catch (IO_ERROR e) {
+ PyErr_SetString(PyExc_IOError,"IO error");
+ return NULL;
+ }
+}
+*/
+
+/* header files that must be wrapped */
+
+%include <dlist.h>
+%include <base_struct.h>
+%include <class_eda_rect.h>
+%include <common.h>
+%include <class_title_block.h>
+%include <class_colors_design_settings.h>
+%include <class_marker_base.h>
+%include <eda_text.h>
+%include <convert_from_iu.h>
+%include <convert_to_biu.h>
+%include <fpid.h>
+
+/* special iteration wrapper for DLIST objects */
+%include "dlist.i"
+
+/* std template mappings */
+%template(intVector) std::vector<int>;
+%template(str_utf8_Map) std::map< std::string,UTF8 >;
+
+// wrapper of BASE_SEQ (see typedef std::vector<LAYER_ID> BASE_SEQ;)
+%template(base_seqVect) std::vector<enum LAYER_ID>;
+
+// TODO: wrapper of BASE_SET (see std::bitset<LAYER_ID_COUNT> BASE_SET;)
+
+
+/* KiCad plugin handling */
+%include "kicadplugins.i"
+
+// map CPolyLine and classes used in CPolyLine:
+#include <../polygon/PolyLine.h>
+%include <../polygon/PolyLine.h>
+
+// ignore warning relative to operator = and operator ++:
+#pragma SWIG nowarn=362,383
+
+// Rename operators defined in utf8.h
+%rename(utf8_to_charptr) operator char* () const;
+%rename(utf8_to_wxstring) operator wxString () const;
+
+#include <utf8.h>
+%include <utf8.h>
+
+%extend UTF8
+{
+ const char* Cast_to_CChar() { return (self->c_str()); }
+
+ %pythoncode
+ %{
+
+ # Get the char buffer of the UTF8 string
+ def GetChars(self):
+ return self.Cast_to_CChar()
+
+ # Convert the UTF8 string to a python string
+ # Same as GetChars(), but more easy to use in print command
+ def __str__(self):
+ return self.GetChars()
+
+ %}
+}
+
diff --git a/scripting/kicadplugins.i b/scripting/kicadplugins.i
new file mode 100644
index 0000000..9a310fa
--- /dev/null
+++ b/scripting/kicadplugins.i
@@ -0,0 +1,271 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2012 NBEE Embedded Systems, Miguel Angel Ajo <miguelangel@nbee.es>
+ * Copyright (C) 1992-2012 KiCad Developers, see AUTHORS.txt for contributors.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+ /**
+ * This file builds the base classes for all kind of python plugins that
+ * can be included into kicad.
+ * they provide generic code to all the classes:
+ *
+ * KiCadPlugin
+ * /|\
+ * |
+ * |\-FilePlugin
+ * |\-FootprintWizardPlugin
+ * |\-ActionPlugin
+ *
+ * It defines the LoadPlugins() function that loads all the plugins
+ * available in the system
+ *
+ */
+
+/*
+ * Remark:
+ * Avoid using the print function in python wizards
+ *
+ * Be aware print messages create IO exceptions, because the wizard
+ * is run from Pcbnew. And if pcbnew is not run from a console, there is
+ * no io channel to read the output of print function.
+ * When the io buffer is full, a IO exception is thrown.
+ */
+
+%pythoncode
+{
+
+KICAD_PLUGINS={}
+
+def ReloadPlugin(name):
+ if not KICAD_PLUGINS.has_key(name):
+ return False
+
+ KICAD_PLUGINS[name]["object"].deregister()
+ mod = reload(KICAD_PLUGINS[name]["module"])
+ KICAD_PLUGINS[name]["object"]= mod.register()
+
+
+def ReloadPlugins():
+ import os.path
+ for k in KICAD_PLUGINS.keys():
+ plugin = KICAD_PLUGINS[k]
+
+ filename = plugin["filename"]
+ mtime = plugin["modification_time"]
+ now_mtime = os.path.getmtime(filename)
+
+ if mtime!=now_mtime:
+ # /* print filename, " is modified, reloading" */
+ KICAD_PLUGINS[k]["modification_time"]=now_mtime
+ ReloadPlugin(k)
+
+
+def LoadPlugins(plugpath):
+ import os
+ import sys
+
+ kicad_path = os.environ.get('KICAD_PATH')
+ plugin_directories=[]
+
+ if plugpath:
+ plugin_directories.append(plugpath)
+
+ if kicad_path:
+ plugin_directories.append(os.path.join(kicad_path, 'scripting', 'plugins'))
+
+ if sys.platform.startswith('linux'):
+ plugin_directories.append(os.environ['HOME']+'/.kicad_plugins/')
+ plugin_directories.append(os.environ['HOME']+'/.kicad/scripting/plugins/')
+
+ for plugins_dir in plugin_directories:
+ if not os.path.isdir(plugins_dir):
+ continue
+
+ sys.path.append(plugins_dir)
+
+ for module in os.listdir(plugins_dir):
+ if os.path.isdir(plugins_dir+module):
+ __import__(module, locals(), globals())
+
+ if module == '__init__.py' or module[-3:] != '.py':
+ continue
+
+ mod = __import__(module[:-3], locals(), globals())
+
+ module_filename = plugins_dir+"/"+module
+ mtime = os.path.getmtime(module_filename)
+ if hasattr(mod,'register'):
+ KICAD_PLUGINS[module]={"filename":module_filename,
+ "modification_time":mtime,
+ "object":mod.register(),
+ "module":mod}
+
+
+
+class KiCadPlugin:
+ def __init__(self):
+ pass
+
+ def register(self):
+ if isinstance(self,FilePlugin):
+ pass # register to file plugins in C++
+ if isinstance(self,FootprintWizardPlugin):
+ PYTHON_FOOTPRINT_WIZARDS.register_wizard(self)
+ return
+
+ if isinstance(self,ActionPlugin):
+ pass # register to action plugins in C++
+
+ return
+
+ def deregister(self):
+ if isinstance(self,FilePlugin):
+ pass # register to file plugins in C++
+ if isinstance(self,FootprintWizardPlugin):
+ PYTHON_FOOTPRINT_WIZARDS.deregister_wizard(self)
+ return
+
+ if isinstance(self,ActionPlugin):
+ pass # register to action plugins in C++
+
+ return
+
+
+
+
+class FilePlugin(KiCadPlugin):
+ def __init__(self):
+ KiCadPlugin.__init__(self)
+
+
+from math import ceil, floor, sqrt
+
+class FootprintWizardPlugin(KiCadPlugin):
+ def __init__(self):
+ KiCadPlugin.__init__(self)
+ self.defaults()
+
+ def defaults(self):
+ self.module = None
+ self.parameters = {}
+ self.parameter_errors={}
+ self.name = "Undefined Footprint Wizard plugin"
+ self.description = ""
+ self.image = ""
+ self.buildmessages = ""
+
+ def GetName(self):
+ return self.name
+
+ def GetImage(self):
+ return self.image
+
+ def GetDescription(self):
+ return self.description
+
+
+ def GetNumParameterPages(self):
+ return len(self.parameters)
+
+ def GetParameterPageName(self,page_n):
+ return self.page_order[page_n]
+
+ def GetParameterNames(self,page_n):
+ name = self.GetParameterPageName(page_n)
+ return self.parameter_order[name]
+
+ def GetParameterValues(self,page_n):
+ name = self.GetParameterPageName(page_n)
+ names = self.GetParameterNames(page_n)
+ values = [self.parameters[name][n] for n in names]
+ return map(lambda x: str(x), values) # list elements as strings
+
+ def GetParameterErrors(self,page_n):
+ self.CheckParameters()
+ name = self.GetParameterPageName(page_n)
+ names = self.GetParameterNames(page_n)
+ values = [self.parameter_errors[name][n] for n in names]
+ return map(lambda x: str(x), values) # list elements as strings
+
+ def CheckParameters(self):
+ return ""
+
+ def ConvertValue(self,v):
+ try:
+ v = float(v)
+ except:
+ pass
+ if type(v) is float:
+ if ceil(v) == floor(v):
+ v = int(v)
+ return v
+
+
+ def SetParameterValues(self,page_n,values):
+ name = self.GetParameterPageName(page_n)
+ keys = self.GetParameterNames(page_n)
+ for n, key in enumerate(keys):
+ val = self.ConvertValue(values[n])
+ self.parameters[name][key] = val
+
+
+ def ClearErrors(self):
+ errs={}
+
+ for page in self.parameters.keys():
+ page_dict = self.parameters[page]
+ page_params = {}
+ for param in page_dict.keys():
+ page_params[param]=""
+
+ errs[page]=page_params
+
+ self.parameter_errors = errs
+
+
+ def GetFootprint( self ):
+ self.BuildFootprint()
+ return self.module
+
+ def BuildFootprint(self):
+ return
+
+ def GetBuildMessages( self ):
+ return self.buildmessages
+
+ def Show(self):
+ print "Footprint Wizard Name: ",self.GetName()
+ print "Footprint Wizard Description: ",self.GetDescription()
+ n_pages = self.GetNumParameterPages()
+ print " setup pages: ",n_pages
+ for page in range(0,n_pages):
+ name = self.GetParameterPageName(page)
+ values = self.GetParameterValues(page)
+ names = self.GetParameterNames(page)
+ print "page %d) %s"%(page,name)
+ for n in range (0,len(values)):
+ print "\t%s\t:\t%s"%(names[n],values[n])
+
+class ActionPlugin(KiCadPlugin):
+ def __init__(self):
+ KiCadPlugin.__init__(self)
+
+}
diff --git a/scripting/python_scripting.cpp b/scripting/python_scripting.cpp
new file mode 100644
index 0000000..c74abf5
--- /dev/null
+++ b/scripting/python_scripting.cpp
@@ -0,0 +1,364 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2012 NBEE Embedded Systems, Miguel Angel Ajo <miguelangel@nbee.es>
+ * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * @file python_scripting.cpp
+ * @brief methods to add scripting capabilities inside pcbnew
+ */
+
+#include <python_scripting.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <fctsys.h>
+#include <wxstruct.h>
+#include <common.h>
+#include <colors.h>
+#include <macros.h>
+
+/* init functions defined by swig */
+
+extern "C" void init_kicad( void );
+
+extern "C" void init_pcbnew( void );
+
+#define EXTRA_PYTHON_MODULES 10 // this is the number of python
+ // modules that we want to add into the list
+
+
+/* python inittab that links module names to module init functions
+ * we will rebuild it to include the original python modules plus
+ * our own ones
+ */
+
+struct _inittab* SwigImportInittab;
+static int SwigNumModules = 0;
+
+static bool wxPythonLoaded = false; // true if the wxPython scripting layer was successfully loaded
+
+bool IsWxPythonLoaded()
+{
+ return wxPythonLoaded;
+}
+
+
+/* Add a name + initfuction to our SwigImportInittab */
+
+static void swigAddModule( const char* name, void (* initfunc)() )
+{
+ SwigImportInittab[SwigNumModules].name = (char*) name;
+ SwigImportInittab[SwigNumModules].initfunc = initfunc;
+ SwigNumModules++;
+ SwigImportInittab[SwigNumModules].name = (char*) 0;
+ SwigImportInittab[SwigNumModules].initfunc = 0;
+}
+
+
+/* Add the builtin python modules */
+
+static void swigAddBuiltin()
+{
+ int i = 0;
+
+ /* discover the length of the pyimport inittab */
+ while( PyImport_Inittab[i].name )
+ i++;
+
+ /* allocate memory for the python module table */
+ SwigImportInittab = (struct _inittab*) malloc(
+ sizeof(struct _inittab) * (i + EXTRA_PYTHON_MODULES) );
+
+ /* copy all pre-existing python modules into our newly created table */
+ i = 0;
+
+ while( PyImport_Inittab[i].name )
+ {
+ swigAddModule( PyImport_Inittab[i].name, PyImport_Inittab[i].initfunc );
+ i++;
+ }
+}
+
+
+/* Function swigAddModules
+ * adds the internal modules we offer to the python scripting, so they will be
+ * available to the scripts we run.
+ *
+ */
+
+static void swigAddModules()
+{
+ swigAddModule( "_pcbnew", init_pcbnew );
+
+ // finally it seems better to include all in just one module
+ // but in case we needed to include any other modules,
+ // it must be done like this:
+ // swigAddModule( "_kicad", init_kicad );
+}
+
+
+/* Function swigSwitchPythonBuiltin
+ * switches python module table to our built one .
+ *
+ */
+
+static void swigSwitchPythonBuiltin()
+{
+ PyImport_Inittab = SwigImportInittab;
+}
+
+
+/* Function pcbnewInitPythonScripting
+ * Initializes all the python environment and publish our interface inside it
+ * initializes all the wxpython interface, and returns the python thread control structure
+ *
+ */
+
+PyThreadState* g_PythonMainTState;
+
+bool pcbnewInitPythonScripting( const char * aUserPluginsPath )
+{
+ swigAddBuiltin(); // add builtin functions
+ swigAddModules(); // add our own modules
+ swigSwitchPythonBuiltin(); // switch the python builtin modules to our new list
+
+ Py_Initialize();
+
+#ifdef KICAD_SCRIPTING_WXPYTHON
+ PyEval_InitThreads();
+
+#ifndef __WINDOWS__ // import wxversion.py currently not working under winbuilder, and not useful.
+ char cmd[1024];
+ // Make sure that that the correct version of wxPython is loaded. In systems where there
+ // are different versions of wxPython installed this can lead to select wrong wxPython
+ // version being selected.
+ snprintf( cmd, sizeof(cmd), "import wxversion; wxversion.select('%s')", WXPYTHON_VERSION );
+
+ int retv = PyRun_SimpleString( cmd );
+
+ if( retv != 0 )
+ {
+ wxLogError( wxT( "Python error %d occurred running string `%s`" ), retv, cmd );
+ PyErr_Print();
+ Py_Finalize();
+ return false;
+ }
+#endif // ifndef __WINDOWS__
+
+ // Load the wxPython core API. Imports the wx._core_ module and sets a
+ // local pointer to a function table located there. The pointer is used
+ // internally by the rest of the API functions.
+ if( !wxPyCoreAPI_IMPORT() )
+ {
+ wxLogError( wxT( "***** Error importing the wxPython API! *****" ) );
+ PyErr_Print();
+ Py_Finalize();
+ return false;
+ }
+
+ wxPythonLoaded = true;
+
+ // Save the current Python thread state and release the
+ // Global Interpreter Lock.
+
+ g_PythonMainTState = wxPyBeginAllowThreads();
+#endif // ifdef KICAD_SCRIPTING_WXPYTHON
+
+ // load pcbnew inside python, and load all the user plugins, TODO: add system wide plugins
+ {
+ char cmd[1024];
+ PyLOCK lock;
+ snprintf( cmd, sizeof(cmd), "import sys, traceback\n"
+ "sys.path.append(\".\")\n"
+ "import pcbnew\n"
+ "pcbnew.LoadPlugins(\"%s\")", aUserPluginsPath );
+ PyRun_SimpleString( cmd );
+ }
+
+ return true;
+}
+
+
+void pcbnewFinishPythonScripting()
+{
+#ifdef KICAD_SCRIPTING_WXPYTHON
+ wxPyEndAllowThreads( g_PythonMainTState );
+#endif
+ Py_Finalize();
+}
+
+
+#if defined( KICAD_SCRIPTING_WXPYTHON )
+
+void RedirectStdio()
+{
+ // This is a helpful little tidbit to help debugging and such. It
+ // redirects Python's stdout and stderr to a window that will popup
+ // only on demand when something is printed, like a traceback.
+ const char* python_redirect =
+ "import sys\n"
+ "import wx\n"
+ "output = wx.PyOnDemandOutputWindow()\n"
+ "sys.stderr = output\n";
+
+ PyLOCK lock;
+
+ PyRun_SimpleString( python_redirect );
+}
+
+
+wxWindow* CreatePythonShellWindow( wxWindow* parent )
+{
+ const char* pycrust_panel =
+ "import wx\n"
+ "from wx.py import shell, version\n"
+ "\n"
+ "intro = \"PyCrust %s - KiCAD Python Shell\" % version.VERSION\n"
+ "\n"
+ "def makeWindow(parent):\n"
+ " pycrust = shell.Shell(parent, -1, introText=intro)\n"
+ " return pycrust\n"
+ "\n";
+
+
+ wxWindow* window = NULL;
+ PyObject* result;
+
+ // As always, first grab the GIL
+ PyLOCK lock;
+
+ // Now make a dictionary to serve as the global namespace when the code is
+ // executed. Put a reference to the builtins module in it.
+
+ PyObject* globals = PyDict_New();
+ PyObject* builtins = PyImport_ImportModule( "__builtin__" );
+
+ PyDict_SetItemString( globals, "__builtins__", builtins );
+ Py_DECREF( builtins );
+
+ // Execute the code to make the makeWindow function we defined above
+ result = PyRun_String( pycrust_panel, Py_file_input, globals, globals );
+
+ // Was there an exception?
+ if( !result )
+ {
+ PyErr_Print();
+ return NULL;
+ }
+
+ Py_DECREF( result );
+
+ // Now there should be an object named 'makeWindow' in the dictionary that
+ // we can grab a pointer to:
+ PyObject* func = PyDict_GetItemString( globals, "makeWindow" );
+ wxASSERT( PyCallable_Check( func ) );
+
+ // Now build an argument tuple and call the Python function. Notice the
+ // use of another wxPython API to take a wxWindows object and build a
+ // wxPython object that wraps it.
+
+ PyObject* arg = wxPyMake_wxObject( parent, false );
+ wxASSERT( arg != NULL );
+
+ PyObject* tuple = PyTuple_New( 1 );
+ PyTuple_SET_ITEM( tuple, 0, arg );
+
+ result = PyEval_CallObject( func, tuple );
+
+ // Was there an exception?
+ if( !result )
+ PyErr_Print();
+ else
+ {
+ // Otherwise, get the returned window out of Python-land and
+ // into C++-ville...
+ bool success = wxPyConvertSwigPtr( result, (void**) &window, _T( "wxWindow" ) );
+ (void) success;
+
+ wxASSERT_MSG( success, _T( "Returned object was not a wxWindow!" ) );
+ Py_DECREF( result );
+ }
+
+ // Release the python objects we still have
+ Py_DECREF( globals );
+ Py_DECREF( tuple );
+
+ return window;
+}
+
+
+#endif
+
+wxArrayString PyArrayStringToWx( PyObject* aArrayString )
+{
+ wxArrayString ret;
+
+ int list_size = PyList_Size( aArrayString );
+
+ for( int n = 0; n<list_size; n++ )
+ {
+ PyObject* element = PyList_GetItem( aArrayString, n );
+
+ ret.Add( FROM_UTF8( PyString_AsString( element ) ), 1 );
+ }
+
+ return ret;
+}
+
+
+wxString PyErrStringWithTraceback()
+{
+ wxString err;
+
+ if( !PyErr_Occurred() )
+ return err;
+
+ PyObject* type;
+ PyObject* value;
+ PyObject* traceback;
+
+ PyErr_Fetch( &type, &value, &traceback );
+
+ PyObject* tracebackModuleString = PyString_FromString( (char*) "traceback" );
+ PyObject* tracebackModule = PyImport_Import( tracebackModuleString );
+
+
+ PyObject* formatException = PyObject_GetAttrString( tracebackModule,
+ (char*) "format_exception" );
+ PyObject* args = Py_BuildValue( "(O,O,O)", type, value, traceback );
+
+ PyObject* result = PyObject_CallObject( formatException, args );
+
+ Py_DECREF( args );
+
+ wxArrayString res = PyArrayStringToWx( result );
+
+ for( unsigned i = 0; i<res.Count(); i++ )
+ {
+ err += res[i] + wxT( "\n" );
+ }
+
+ PyErr_Clear();
+
+ return err;
+}
diff --git a/scripting/python_scripting.h b/scripting/python_scripting.h
new file mode 100644
index 0000000..8d0ae17
--- /dev/null
+++ b/scripting/python_scripting.h
@@ -0,0 +1,62 @@
+#ifndef __PYTHON_SCRIPTING_H
+#define __PYTHON_SCRIPTING_H
+
+// undefs explained here: https://bugzilla.redhat.com/show_bug.cgi?id=427617
+
+#ifdef _POSIX_C_SOURCE
+ #undef _POSIX_C_SOURCE
+#endif
+#ifdef _XOPEN_SOURCE
+ #undef _XOPEN_SOURCE
+#endif
+
+#include <Python.h>
+#ifndef NO_WXPYTHON_EXTENSION_HEADERS
+#ifdef KICAD_SCRIPTING_WXPYTHON
+ #include <wx/wxPython/wxPython.h>
+#endif
+#endif
+
+#include <wx/string.h>
+#include <wx/arrstr.h>
+
+/* Function pcbnewInitPythonScripting
+ * Initializes the Python engine inside pcbnew
+ */
+
+bool pcbnewInitPythonScripting( const char * aUserPluginsPath );
+void pcbnewFinishPythonScripting();
+
+
+#ifdef KICAD_SCRIPTING_WXPYTHON
+
+void RedirectStdio();
+wxWindow* CreatePythonShellWindow( wxWindow* parent );
+
+class PyLOCK
+{
+ wxPyBlock_t b;
+public:
+
+ // @todo, find out why these are wxPython specific. We need the GIL regardless.
+ // Should never assume python will only have one thread calling it.
+ PyLOCK() { b = wxPyBeginBlockThreads(); }
+ ~PyLOCK() { wxPyEndBlockThreads( b ); }
+};
+
+
+#else
+class PyLOCK
+{
+ PyGILState_STATE gil_state;
+public:
+ PyLOCK() { gil_state = PyGILState_Ensure(); }
+ ~PyLOCK() { PyGILState_Release( gil_state ); }
+};
+
+#endif
+
+wxArrayString PyArrayStringToWx( PyObject* arr );
+wxString PyErrStringWithTraceback();
+
+#endif // __PYTHON_SCRIPTING_H
diff --git a/scripting/wx.i b/scripting/wx.i
new file mode 100644
index 0000000..aa03a3f
--- /dev/null
+++ b/scripting/wx.i
@@ -0,0 +1,301 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2012 Miguel Angel Ajo <miguelangel@nbee.es>
+ * Copyright (C) 1992-2012 KiCad Developers, see AUTHORS.txt for contributors.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * @file wx.i
+ * @brief wx wrappers for basic things, wxString, wxPoint, wxRect, etc..
+ * all the wx objects are very complex, and we don't want to pull
+ * and swig all depending objects, so we just define the methods
+ * we want to wrap.
+ */
+
+%{
+#include <wx_python_helpers.h>
+%}
+
+// encoding setup, ascii by default ///////////////////////////////////////////
+
+void wxSetDefaultPyEncoding(const char* encoding);
+const char* wxGetDefaultPyEncoding();
+
+
+// wxRect class wrapper ///////////////////////////////////////////////////////
+
+class wxRect
+{
+public:
+ wxRect() : x(0), y(0), width(0), height(0) { }
+ wxRect(int xx, int yy, int ww, int hh): x(xx), y(yy), width(ww), height(hh) { }
+ wxRect(const wxPoint& topLeft, const wxPoint& bottomRight);
+ wxRect(const wxPoint& pt, const wxSize& size)
+ : x(pt.x), y(pt.y), width(size.x), height(size.y) { }
+ wxRect(const wxSize& size): x(0), y(0), width(size.x), height(size.y) { }
+
+ int GetX() const { return x; }
+ void SetX(int xx) { x = xx; }
+
+ int GetY() const { return y; }
+ void SetY(int yy) { y = yy; }
+
+ int GetWidth() const { return width; }
+ void SetWidth(int w) { width = w; }
+
+ int GetHeight() const { return height; }
+ void SetHeight(int h) { height = h; }
+
+ wxPoint GetPosition() const { return wxPoint(x, y); }
+ void SetPosition( const wxPoint &p ) { x = p.x; y = p.y; }
+
+ int x, y, width, height;
+
+ %extend
+ {
+ /* extend the wxRect object so it can be converted into a tuple */
+ PyObject* Get()
+ {
+ PyObject* res = PyTuple_New(4);
+ PyTuple_SET_ITEM(res, 0, PyInt_FromLong(self->x));
+ PyTuple_SET_ITEM(res, 1, PyInt_FromLong(self->y));
+ PyTuple_SET_ITEM(res, 2, PyInt_FromLong(self->width));
+ PyTuple_SET_ITEM(res, 3, PyInt_FromLong(self->height));
+ return res;
+ }
+ }
+
+
+ %pythoncode
+ {
+
+ def __eq__(self,other):
+ return self.x==other.x and self.y==other.y and self.width==other.width and self.height==other.height
+ def __str__(self): return str(self.Get())
+ def __repr__(self): return 'wxRect'+str(self.Get())
+ def __len__(self): return len(self.Get())
+ def __getitem__(self, index): return self.Get()[index]
+ def __setitem__(self, index, val):
+ if index == 0: self.SetX(val)
+ elif index == 1: self.SetY(val)
+ elif index == 2: self.SetWidth(val)
+ elif index == 3: self.SetHeight(val)
+ else: raise IndexError
+ def __nonzero__(self): return self.Get() != (0,0,0,0)
+ __safe_for_unpickling__ = True
+ }
+
+};
+
+// wxSize class wrapper ///////////////////////////////////////////////////////
+
+class wxSize
+{
+public:
+ int x,y;
+ wxSize(int xx, int yy) : x(xx), y(yy) { }
+ wxSize(double xx, double yy) : x(xx), y(yy) {}
+ %extend
+ {
+ PyObject* Get()
+ {
+ PyObject* res = PyTuple_New(2);
+ PyTuple_SET_ITEM(res, 0, PyInt_FromLong(self->x));
+ PyTuple_SET_ITEM(res, 1, PyInt_FromLong(self->y));
+ return res;
+ }
+ }
+
+ ~wxSize();
+
+ void SetWidth(int w);
+ void SetHeight(int h);
+ int GetWidth() const;
+ int GetHeight() const;
+
+
+ %pythoncode
+ {
+ def Scale(self,xscale,yscale):
+ return wxSize(self.x*xscale,self.y*yscale)
+ def __eq__(self,other):
+ return self.GetWidth()==other.GetWidth() and self.GetHeight()==other.GetHeight()
+ def __str__(self): return str(self.Get())
+ def __repr__(self): return 'wxSize'+str(self.Get())
+ def __len__(self): return len(self.Get())
+ def __getitem__(self, index): return self.Get()[index]
+ def __setitem__(self, index, val):
+ if index == 0: self.SetWidth(val)
+ elif index == 1: self.SetHeight(val)
+ else: raise IndexError
+ def __nonzero__(self): return self.Get() != (0,0)
+ __safe_for_unpickling__ = True
+
+ }
+};
+
+// wxPoint class wrapper to (xx,yy) tuple /////////////////////////////////////
+
+class wxPoint
+{
+public:
+ int x, y;
+ wxPoint(int xx, int yy);
+ wxPoint(double xx, double yy) : x(xx), y(yy) {}
+ ~wxPoint();
+ %extend {
+ wxPoint __add__(const wxPoint& pt) { return *self + pt; }
+ wxPoint __sub__(const wxPoint& pt) { return *self - pt; }
+
+ void Set(long x, long y) { self->x = x; self->y = y; }
+ PyObject* Get()
+ {
+ PyObject* tup = PyTuple_New(2);
+ PyTuple_SET_ITEM(tup, 0, PyInt_FromLong(self->x));
+ PyTuple_SET_ITEM(tup, 1, PyInt_FromLong(self->y));
+ return tup;
+ }
+ }
+
+ %pythoncode {
+ def __eq__(self,other): return (self.x==other.x and self.y==other.y)
+ def __ne__(self,other): return not (self==other)
+ def __str__(self): return str(self.Get())
+ def __repr__(self): return 'wxPoint'+str(self.Get())
+ def __len__(self): return len(self.Get())
+ def __getitem__(self, index): return self.Get()[index]
+ def __setitem__(self, index, val):
+ if index == 0:
+ self.x = val
+ elif index == 1:
+ self.y = val
+ else:
+ raise IndexError
+ def __nonzero__(self): return self.Get() != (0,0)
+
+ }
+};
+
+
+// wxChar typemaps ///////////////////////////////////////////////////////////
+
+/* they handle the conversion from/to strings */
+
+%typemap(in) wxChar { wxString str = Py2wxString($input); $1 = str[0]; }
+%typemap(out) wxChar { wxString str($1); $result = wx2PyString(str); }
+
+// wxString wrappers /////////////////////////////////////////////////////////
+
+%typemap(out) wxString&
+{
+%#if wxUSE_UNICODE
+ $result = PyUnicode_FromWideChar($1->c_str(), $1->Len());
+%#else
+ $result = PyString_FromStringAndSize($1->c_str(), $1->Len());
+%#endif
+}
+
+%apply wxString& { wxString* }
+
+%typemap(out) wxString
+{
+%#if wxUSE_UNICODE
+ $result = PyUnicode_FromWideChar($1.c_str(), $1.Len());
+%#else
+ $result = PyString_FromStringAndSize($1.c_str(), $1.Len());
+%#endif
+}
+
+%typemap(varout) wxString
+{
+%#if wxUSE_UNICODE
+ $result = PyUnicode_FromWideChar($1.c_str(), $1.Len());
+%#else
+ $result = PyString_FromStringAndSize($1.c_str(), $1.Len());
+%#endif
+}
+
+%typemap(in) wxString& (bool temp=false)
+{
+ $1 = newWxStringFromPy($input);
+ if ($1 == NULL) SWIG_fail;
+ temp = true;
+}
+
+%typemap(freearg) wxString&
+{
+ if (temp$argnum)
+ delete $1;
+}
+
+
+%typemap(in) wxString {
+ wxString* sptr = newWxStringFromPy($input);
+ if (sptr == NULL) SWIG_fail;
+ $1 = *sptr;
+ delete sptr;
+}
+
+%typemap(typecheck, precedence=SWIG_TYPECHECK_POINTER) wxString& {
+ $1 = PyString_Check($input) || PyUnicode_Check($input);
+}
+
+
+// wxArrayString wrappers //////////////////////////////////////////////////////
+%typemap(in) wxArrayString& (bool temp=false) {
+ if (!PySequence_Check($input))
+ {
+ PyErr_SetString(PyExc_TypeError, "Not a sequence of strings");
+ SWIG_fail;
+ }
+
+ $1 = new wxArrayString;
+ temp = true;
+ int last=PySequence_Length($input);
+ for (int i=0; i<last; i++)
+ {
+ PyObject* pyStr = PySequence_GetItem($input, i);
+ wxString* wxS = newWxStringFromPy(pyStr);
+ if (PyErr_Occurred())
+ SWIG_fail;
+ $1->Add(*wxS);
+ delete wxS;
+ Py_DECREF(pyStr);
+ }
+}
+
+%typemap(freearg) wxArrayString&
+{
+ if (temp$argnum)
+ delete $1;
+}
+
+%typemap(out) wxArrayString&
+{
+ $result = wxArrayString2PyList(*$1);
+}
+
+%typemap(out) wxArrayString
+{
+ $result = wxArrayString2PyList($1);
+}
+
+%template(wxPoint_Vector) std::vector<wxPoint>;
diff --git a/scripting/wx_python_helpers.cpp b/scripting/wx_python_helpers.cpp
new file mode 100644
index 0000000..587fe2a
--- /dev/null
+++ b/scripting/wx_python_helpers.cpp
@@ -0,0 +1,196 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2012 Miguel Angel Ajo <miguelangel@nbee.es>
+ * Copyright (C) 1992-2012 KiCad Developers, see AUTHORS.txt for contributors.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * @file wx_python_helpers.cpp
+ * @brief Python wrapping helpers for wx structures/objects
+ */
+
+#include <Python.h>
+#include <wx/intl.h>
+#include <wx/string.h>
+#include <wx/arrstr.h>
+
+
+#define WX_DEFAULTENCODING_SIZE 64
+
+static char wxPythonEncoding[WX_DEFAULTENCODING_SIZE] = "ascii";
+
+
+PyObject* wxArrayString2PyList( const wxArrayString& lst )
+{
+ PyObject* list = PyList_New( 0 );
+
+ for( size_t i = 0; i < lst.GetCount(); i++ )
+ {
+#if wxUSE_UNICODE
+ PyObject* pyStr = PyUnicode_FromWideChar( lst[i].c_str(),
+ lst[i].Len()
+ );
+#else
+ PyObject* pyStr = PyString_FromStringAndSize( lst[i].c_str(),
+ lst[i].Len()
+ );
+#endif
+ PyList_Append( list, pyStr );
+ Py_DECREF( pyStr );
+ }
+
+ return list;
+}
+
+
+wxString* newWxStringFromPy( PyObject* src )
+{
+ bool must_unref_str = false;
+
+ wxString* result = NULL;
+ PyObject* obj = src;
+
+#if wxUSE_UNICODE
+ bool must_unref_obj = false;
+ // Unicode string to python unicode string
+ PyObject* uni_str = src;
+
+ // if not an str or unicode, try to str(src)
+ if( !PyString_Check( src ) && !PyUnicode_Check( src ) )
+ {
+ obj = PyObject_Str( src );
+ must_unref_obj = true;
+
+ if( PyErr_Occurred() )
+ return NULL;
+ }
+
+ if( PyString_Check( obj ) )
+ {
+ uni_str = PyUnicode_FromEncodedObject( obj, wxPythonEncoding, "strict" );
+ must_unref_str = true;
+
+ if( PyErr_Occurred() )
+ return NULL;
+ }
+
+ result = new wxString();
+ size_t len = PyUnicode_GET_SIZE( uni_str );
+
+ if( len )
+ {
+ PyUnicode_AsWideChar( (PyUnicodeObject*) uni_str,
+ wxStringBuffer( *result, len ), len );
+ }
+
+ if( must_unref_str )
+ {
+ Py_DECREF( uni_str );
+ }
+
+ if( must_unref_obj )
+ {
+ Py_DECREF( obj );
+ }
+
+#else
+ // normal string (or object) to normal python string
+ PyObject* str = src;
+
+ if( PyUnicode_Check( src ) ) // if it's unicode convert to normal string
+ {
+ str = PyUnicode_AsEncodedString( src, wxPythonEncoding, "strict" );
+
+ if( PyErr_Occurred() )
+ return NULL;
+ }
+ else if( !PyString_Check( src ) ) // if it's not a string, str(obj)
+ {
+ str = PyObject_Str( src );
+ must_unref_str = true;
+
+ if( PyErr_Occurred() )
+ return NULL;
+ }
+
+ // get the string pointer and size
+ char* str_ptr;
+ Py_ssize_t str_size;
+ PyString_AsStringAndSize( str, &str_ptr, &str_size );
+
+ // build the wxString from our pointer / size
+ result = new wxString( str_ptr, str_size );
+
+ if( must_unref_str )
+ {
+ Py_DECREF( str );
+ }
+
+#endif
+
+ return result;
+}
+
+
+wxString Py2wxString( PyObject* src )
+{
+ wxString result;
+ wxString* resPtr = newWxStringFromPy( src );
+
+ // In case of exception clear it and return an empty string
+ if( resPtr==NULL )
+ {
+ PyErr_Clear();
+ return wxEmptyString;
+ }
+
+ result = *resPtr;
+
+ delete resPtr;
+
+ return result;
+}
+
+
+PyObject* wx2PyString( const wxString& src )
+{
+ PyObject* str;
+
+#if wxUSE_UNICODE
+ str = PyUnicode_FromWideChar( src.c_str(), src.Len() );
+#else
+ str = PyString_FromStringAndSize( src.c_str(), src.Len() );
+#endif
+ return str;
+}
+
+
+void wxSetDefaultPyEncoding( const char* encoding )
+{
+ strncpy( wxPythonEncoding, encoding, WX_DEFAULTENCODING_SIZE );
+ wxPythonEncoding[ WX_DEFAULTENCODING_SIZE - 1 ] = '\0';
+}
+
+
+const char* wxGetDefaultPyEncoding()
+{
+ return wxPythonEncoding;
+}
diff --git a/scripting/wx_python_helpers.h b/scripting/wx_python_helpers.h
new file mode 100644
index 0000000..2da11d6
--- /dev/null
+++ b/scripting/wx_python_helpers.h
@@ -0,0 +1,18 @@
+#ifndef __wx_helpers_h
+#define __wx_helpers_h
+
+#include <Python.h>
+#include <wx/intl.h>
+#include <wx/string.h>
+#include <wx/arrstr.h>
+
+
+PyObject* wxArrayString2PyList( const wxArrayString& lst );
+wxString* newWxStringFromPy( PyObject* source );
+wxString Py2wxString( PyObject* source );
+PyObject* wx2PyString( const wxString& src );
+
+void wxSetDefaultPyEncoding( const char* encoding );
+const char* wxGetDefaultPyEncoding();
+
+#endif
diff --git a/scripts/kicad-install.sh b/scripts/kicad-install.sh
new file mode 100755
index 0000000..a0f9781
--- /dev/null
+++ b/scripts/kicad-install.sh
@@ -0,0 +1,408 @@
+#!/bin/bash -e
+# Install KiCad from source onto either:
+# -> a Ubuntu/Debian/Mint or
+# -> a Red Hat
+# compatible linux system.
+#
+# The "install_prerequisites" step is the only "distro dependent" one. That step could be modified
+# for other linux distros.
+#
+# There are 3 package groups in a KiCad install:
+# 1) Compiled source code in the form of executable programs.
+# 2) User manuals and other documentation typically as *.pdf files.
+# 3) a) Schematic parts, b) layout footprints, and c) 3D models for footprints.
+#
+# To achieve 1) source is checked out from its repo and compiled by this script then executables
+# are installed using CMake.
+# To achieve 2) documentation is checked out from its repo and installed using CMake.
+# TO achieve 3a) and 3c) they are checked out from their repos and installed using CMake.
+# To achieve 3b) a global fp-lib-table is put into your home directory which points to
+# http://github.com/KiCad. No actual footprints are installed locally, internet access is used
+# during program operation to fetch footprints from github as if it was a remote drive in the cloud.
+# If you want to install those same KiCad footprints locally, you may run a separate script
+# named library-repos-install.sh found in this same directory. That script requires that "git" be on
+# your system whereas this script does not. The footprints require some means to download them and
+# bzr-git seems not up to the task. wget or curl would also work.
+
+
+# Since bash is invoked with -e by the first line of this script, all the steps in this script
+# must succeed otherwise bash will abort at the first non-zero error code. Therefore any script
+# functions must be crafted to anticipate numerous conditions, such that no command fails unless it
+# is a serious situation.
+
+
+# Set where the 3 source trees will go, use a full path
+WORKING_TREES=~/kicad_sources
+
+STABLE=5054 # a sensible mix of features and stability
+TESTING=last:1 # the most recent
+
+# Set this to STABLE or TESTING or other known revision number:
+REVISION=$TESTING
+
+# For info on revision syntax:
+# $ bzr help revisionspec
+
+
+# CMake Options
+OPTS="$OPTS -DBUILD_GITHUB_PLUGIN=ON" # needed by $STABLE revision
+
+# Python scripting, uncomment only one to enable:
+
+# Basic python scripting: gives access to wizards like footprint wizards (recommended)
+# be sure you have python 2.7 installed
+#OPTS="$OPTS -DKICAD_SCRIPTING=ON"
+
+# More advanced python scripting: gives access to wizards like footprint wizards and creates a python module
+# to edit board files (.kicad_pcb files) outside kicad, by python scripts
+#OPTS="$OPTS -DKICAD_SCRIPTING=ON -DKICAD_SCRIPTING_MODULES=ON"
+
+# Most advanced python scripting: you can execute python scripts inside Pcbnew to edit the current loaded board
+# mainly for advanced users
+#OPTS="$OPTS -DKICAD_SCRIPTING=ON -DKICAD_SCRIPTING_MODULES=ON -DKICAD_SCRIPTING_WXPYTHON=ON"
+
+# Use https under bazaar to retrieve repos because this does not require a
+# launchpad.net account. Whereas lp:<something> requires a launchpad account.
+# https results in read only access.
+REPOS=https://code.launchpad.net
+
+# This branch is a bzr/launchpad import of the Git repository
+# at https://github.com/KiCad/kicad-library.git.
+# It has schematic parts and 3D models in it.
+LIBS_REPO=$REPOS/~kicad-product-committers/kicad/library
+
+SRCS_REPO=$REPOS/~kicad-product-committers/kicad/product
+DOCS_REPO=$REPOS/~kicad-developers/kicad/doc
+
+
+usage()
+{
+ echo ""
+ echo " usage:"
+ echo ""
+ echo "./kicad-install.sh <cmd>"
+ echo " where <cmd> is one of:"
+ echo " --install-or-update (does full installation or update.)"
+ echo " --remove-sources (removes source trees for another attempt.)"
+ echo " --uninstall-libraries (removes KiCad supplied libraries.)"
+ echo " --uninstall-kicad (uninstalls all of KiCad but leaves source trees.)"
+ echo ""
+ echo "example:"
+ echo ' $ ./kicad-install.sh --install-or-update'
+}
+
+
+install_prerequisites()
+{
+ # Find a package manager, PM
+ PM=$( command -v yum || command -v apt-get )
+
+ # assume all these Debian, Mint, Ubuntu systems have same prerequisites
+ if [ "$(expr match "$PM" '.*\(apt-get\)')" == "apt-get" ]; then
+ #echo "debian compatible system"
+ prerequisite_list="
+ bzr
+ bzrtools
+ build-essential
+ cmake
+ cmake-curses-gui
+ debhelper
+ doxygen
+ grep
+ libbz2-dev
+ libcairo2-dev
+ libglew-dev
+ libssl-dev
+ libwxgtk3.0-dev
+ "
+
+ for p in ${prerequisite_list}
+ do
+ sudo apt-get install $p || exit 1
+ done
+
+ # Only install the scripting prerequisites if required.
+ if [ "$(expr match "$OPTS" '.*\(-DKICAD_SCRIPTING=ON\)')" == "-DKICAD_SCRIPTING=ON" ]; then
+ #echo "KICAD_SCRIPTING=ON"
+ scripting_prerequisites="
+ python-dev
+ python-wxgtk3.0-dev
+ swig
+ "
+
+ for sp in ${scripting_prerequisites}
+ do
+ sudo apt-get install $sp || exit 1
+ done
+ fi
+
+ # assume all yum systems have same prerequisites
+ elif [ "$(expr match "$PM" '.*\(yum\)')" == "yum" ]; then
+ #echo "red hat compatible system"
+ # Note: if you find this list not to be accurate, please submit a patch:
+ sudo yum groupinstall "Development Tools" || exit 1
+
+ prerequisite_list="
+ bzr
+ bzrtools
+ bzip2-libs
+ bzip2-devel
+ cmake
+ cmake-gui
+ doxygen
+ cairo-devel
+ glew-devel
+ grep
+ openssl-devel
+ wxGTK3-devel
+ "
+
+ for p in ${prerequisite_list}
+ do
+ sudo yum install $p || exit 1
+ done
+
+ echo "Checking wxGTK version. Maybe you have to symlink /usr/bin/wx-config-3.0 to /usr/bin/wx-config"
+ V=`wx-config --version | cut -f 1 -d '.'` || echo "Error running wx-config."
+ if [ $V -lt 3 ]
+ then
+ echo "Error: wx-config is reporting version prior to 3"
+ exit
+ else
+ echo "All ok"
+ fi
+ # Only install the scripting prerequisites if required.
+ if [ "$(expr match "$OPTS" '.*\(-DKICAD_SCRIPTING=ON\)')" == "-DKICAD_SCRIPTING=ON" ]; then
+ #echo "KICAD_SCRIPTING=ON"
+ scripting_prerequisites="
+ swig
+ wxPython
+ "
+
+ for sp in ${scripting_prerequisites}
+ do
+ sudo yum install $sp || exit 1
+ done
+ fi
+ else
+ echo
+ echo "Incompatible System. Neither 'yum' nor 'apt-get' found. Not possible to continue."
+ echo
+ exit 1
+ fi
+
+ # ensure bzr name and email are set. No message since bzr prints an excellent diagnostic.
+ bzr whoami || {
+ echo "WARNING: You have not set bzr whoami, so I will set a dummy."
+ export BZR_EMAIL="Kicad Build <nobody@foo>"
+ }
+}
+
+
+rm_build_dir()
+{
+ local dir="$1"
+
+ echo "removing directory $dir"
+
+ if [ -e "$dir/install_manifest.txt" ]; then
+ # this file is often created as root, so remove as root
+ sudo rm "$dir/install_manifest.txt" 2> /dev/null
+ fi
+
+ if [ -d "$dir" ]; then
+ rm -rf "$dir"
+ fi
+}
+
+
+cmake_uninstall()
+{
+ # assume caller set the CWD, and is only telling us about it in $1
+ local dir="$1"
+
+ cwd=`pwd`
+ if [ "$cwd" != "$dir" ]; then
+ echo "missing dir $dir"
+ elif [ ! -e install_manifest.txt ]; then
+ echo
+ echo "Missing file $dir/install_manifest.txt."
+ else
+ echo "uninstalling from $dir"
+ sudo make uninstall
+ sudo rm install_manifest.txt
+ fi
+}
+
+
+# Function set_env_var
+# sets an environment variable globally.
+set_env_var()
+{
+ local var=$1
+ local val=$2
+
+ if [ -d /etc/profile.d ]; then
+ if [ ! -e /etc/profile.d/kicad.sh ] || ! grep "$var" /etc/profile.d/kicad.sh >> /dev/null; then
+ echo
+ echo "Adding environment variable $var to file /etc/profile.d/kicad.sh"
+ echo "Please logout and back in after this script completes for environment"
+ echo "variable to get set into environment."
+ sudo sh -c "echo export $var=$val >> /etc/profile.d/kicad.sh"
+ fi
+
+ elif [ -e /etc/environment ]; then
+ if ! grep "$var" /etc/environment >> /dev/null; then
+ echo
+ echo "Adding environment variable $var to file /etc/environment"
+ echo "Please reboot after this script completes for environment variable to get set into environment."
+ sudo sh -c "echo $var=$val >> /etc/environment"
+ fi
+ fi
+}
+
+
+install_or_update()
+{
+ echo "step 1) installing pre-requisites"
+ install_prerequisites
+
+
+ echo "step 2) make $WORKING_TREES if it does not exist"
+ if [ ! -d "$WORKING_TREES" ]; then
+ sudo mkdir -p "$WORKING_TREES"
+ echo " mark $WORKING_TREES as owned by me"
+ sudo chown -R `whoami` "$WORKING_TREES"
+ fi
+ cd $WORKING_TREES
+
+
+ echo "step 3) checking out the source code from launchpad repo..."
+ if [ ! -d "$WORKING_TREES/kicad.bzr" ]; then
+ bzr checkout -r $REVISION $SRCS_REPO kicad.bzr
+ echo " source repo to local working tree."
+ else
+ cd kicad.bzr
+ bzr up -r $REVISION
+ echo " local source working tree updated."
+ cd ../
+ fi
+
+ echo "step 4) checking out the schematic parts and 3D library repo."
+ if [ ! -d "$WORKING_TREES/kicad-lib.bzr" ]; then
+ bzr checkout $LIBS_REPO kicad-lib.bzr
+ echo ' kicad-lib checked out.'
+ else
+ cd kicad-lib.bzr
+ bzr up
+ echo ' kicad-lib repo updated.'
+ cd ../
+ fi
+
+ echo "step 5) checking out the documentation from launchpad repo..."
+ if [ ! -d "$WORKING_TREES/kicad-doc.bzr" ]; then
+ bzr checkout $DOCS_REPO kicad-doc.bzr
+ echo " docs checked out."
+ else
+ cd kicad-doc.bzr
+ bzr up
+ echo " docs working tree updated."
+ cd ../
+ fi
+
+
+ echo "step 6) compiling source code..."
+ cd kicad.bzr
+ if [ ! -d "build" ]; then
+ mkdir build && cd build
+ cmake $OPTS ../ || exit 1
+ else
+ cd build
+ # Although a "make clean" is sometimes needed, more often than not it slows down the update
+ # more than it is worth. Do it manually if you need to in this directory.
+ # make clean
+ fi
+ make -j4 || exit 1
+ echo " kicad compiled."
+
+
+ echo "step 7) installing KiCad program files..."
+ sudo make install
+ echo " kicad program files installed."
+
+
+ echo "step 8) installing libraries..."
+ cd ../../kicad-lib.bzr
+ rm_build_dir build
+ mkdir build && cd build
+ cmake ../
+ sudo make install
+ echo " kicad-lib.bzr installed."
+
+
+ echo "step 9) as non-root, install global fp-lib-table if none already installed..."
+ # install ~/fp-lib-table
+ if [ ! -e ~/fp-lib-table ]; then
+ make install_github_fp-lib-table
+ echo " global fp-lib-table installed."
+ fi
+
+
+ echo "step 10) installing documentation..."
+ cd ../../kicad-doc.bzr
+ rm_build_dir build
+ mkdir build && cd build
+ cmake ../
+ sudo make install
+ echo " kicad-doc.bzr installed."
+
+ echo "step 11) check for environment variables..."
+ if [ -z "${KIGITHUB}" ]; then
+ set_env_var KIGITHUB https://github.com/KiCad
+ fi
+
+ echo
+ echo 'All KiCad "--install-or-update" steps completed, you are up to date.'
+ echo
+}
+
+
+if [ $# -eq 1 -a "$1" == "--remove-sources" ]; then
+ echo "deleting $WORKING_TREES"
+ rm_build_dir "$WORKING_TREES/kicad.bzr/build"
+ rm_build_dir "$WORKING_TREES/kicad-lib.bzr/build"
+ rm_build_dir "$WORKING_TREES/kicad-doc.bzr/build"
+ rm -rf "$WORKING_TREES"
+ exit
+fi
+
+
+if [ $# -eq 1 -a "$1" == "--install-or-update" ]; then
+ install_or_update
+ exit
+fi
+
+
+if [ $# -eq 1 -a "$1" == "--uninstall-libraries" ]; then
+ cd "$WORKING_TREES/kicad-lib.bzr/build"
+ cmake_uninstall "$WORKING_TREES/kicad-lib.bzr/build"
+ exit
+fi
+
+
+if [ $# -eq 1 -a "$1" == "--uninstall-kicad" ]; then
+ cd "$WORKING_TREES/kicad.bzr/build"
+ cmake_uninstall "$WORKING_TREES/kicad.bzr/build"
+
+ cd "$WORKING_TREES/kicad-lib.bzr/build"
+ cmake_uninstall "$WORKING_TREES/kicad-lib.bzr/build"
+
+ # this may fail since "uninstall" support is a recent feature of this repo:
+ cd "$WORKING_TREES/kicad-doc.bzr/build"
+ cmake_uninstall "$WORKING_TREES/kicad-doc.bzr/build"
+
+ exit
+fi
+
+
+usage
diff --git a/scripts/lib_convert.py b/scripts/lib_convert.py
new file mode 100755
index 0000000..18abc72
--- /dev/null
+++ b/scripts/lib_convert.py
@@ -0,0 +1,51 @@
+#!/usr/bin/python
+
+# Convert a footprint library from one format to another, e.g. legacy to pretty.
+
+# 1) Build target _pcbnew after enabling scripting in cmake.
+# $ make _pcbnew
+
+# 2) Changed dir to pcbnew
+# $ cd pcbnew
+# $ pwd
+# build/pcbnew
+
+# 3) Entered following command line, script takes to arguments: oldLibPath & newLibPath
+# $ PYTHONPATH=. <path_to>/lib_convert.py /usr/local/share/kicad/modules/smd_dil.mod /tmp/smd_dil.pretty
+
+# 4) inspect one footprint found in new librarypath /tmp/smd_dil.pretty
+# $ less /tmp/smd_dil.pretty/msoic-10.kicad_mod
+
+
+from __future__ import print_function
+from pcbnew import *
+import sys
+
+if len( sys.argv ) < 3 :
+ print( "usage: script srcLibraryPath dstLibraryPath" )
+ sys.exit(1)
+
+
+src_libpath = sys.argv[1]
+dst_libpath = sys.argv[2]
+
+
+src_type = IO_MGR.GuessPluginTypeFromLibPath( src_libpath );
+dst_type = IO_MGR.GuessPluginTypeFromLibPath( dst_libpath );
+
+src_plugin = IO_MGR.PluginFind( src_type )
+dst_plugin = IO_MGR.PluginFind( dst_type )
+
+try:
+ dst_plugin.FootprintLibDelete( dst_libpath )
+except:
+ None # ignore, new may not exist if first run
+
+dst_plugin.FootprintLibCreate( dst_libpath )
+
+list_of_parts = src_plugin.FootprintEnumerate( src_libpath )
+
+for part_id in list_of_parts:
+ module = src_plugin.FootprintLoad( src_libpath, part_id )
+ dst_plugin.FootprintSave( dst_libpath, module )
+
diff --git a/scripts/library-repos-install.bat b/scripts/library-repos-install.bat
new file mode 100644
index 0000000..09027b5
--- /dev/null
+++ b/scripts/library-repos-install.bat
@@ -0,0 +1,89 @@
+REM This file was created using <kicad_src>/scripts/library-repos-install.sh on linux.
+REM Run it from a directory you desire as the base for all libraries.
+git clone https://github.com/KiCad/kicad-library
+git clone https://github.com/KiCad/Displays_7-Segment.pretty
+git clone https://github.com/KiCad/Air_Coils_SML_NEOSID.pretty
+git clone https://github.com/KiCad/Sockets_BNC.pretty
+git clone https://github.com/KiCad/Buzzers_Beepers.pretty
+git clone https://github.com/KiCad/Capacitors_Elko_ThroughHole.pretty
+git clone https://github.com/KiCad/Capacitors.pretty
+git clone https://github.com/KiCad/Capacitors_SMD.pretty
+git clone https://github.com/KiCad/Capacitors_ThroughHole.pretty
+git clone https://github.com/KiCad/Choke_Axial_ThroughHole.pretty
+git clone https://github.com/KiCad/Choke_Radial_ThroughHole.pretty
+git clone https://github.com/KiCad/Choke_SMD.pretty
+git clone https://github.com/KiCad/Choke_Toroid_ThroughHole.pretty
+git clone https://github.com/KiCad/Choke_Common-Mode_Wurth.pretty
+git clone https://github.com/KiCad/Connect.pretty
+git clone https://github.com/KiCad/Connectors_Serial_MOLEX.pretty
+git clone https://github.com/KiCad/Converters_DCDC_ACDC.pretty
+git clone https://github.com/KiCad/Crystals.pretty
+git clone https://github.com/KiCad/Crystals_Oscillators_SMD.pretty
+git clone https://github.com/KiCad/Diodes_SMD.pretty
+git clone https://github.com/KiCad/Diodes_ThroughHole.pretty
+git clone https://github.com/KiCad/Discret.pretty
+git clone https://github.com/KiCad/Display.pretty
+git clone https://github.com/KiCad/Divers.pretty
+git clone https://github.com/KiCad/EuroBoard_Outline.pretty
+git clone https://github.com/KiCad/Fiducials.pretty
+git clone https://github.com/KiCad/Filters_HF_Coils_NEOSID.pretty
+git clone https://github.com/KiCad/Footprint_Symbols.pretty
+git clone https://github.com/KiCad/Fuse_Holders_and_Fuses.pretty
+git clone https://github.com/KiCad/Heatsinks.pretty
+git clone https://github.com/KiCad/Housings_ROHM.pretty
+git clone https://github.com/KiCad/Housings_SIP9.pretty
+git clone https://github.com/KiCad/Housings_SOT-23_SOT-143_TSOT-6.pretty
+git clone https://github.com/KiCad/Housings_SOT-89.pretty
+git clone https://github.com/KiCad/Housings_SOT.pretty
+git clone https://github.com/KiCad/Housings_TO-50.pretty
+git clone https://github.com/KiCad/Housings_TO-78.pretty
+git clone https://github.com/KiCad/Housings_TO-92.pretty
+git clone https://github.com/KiCad/Inductors.pretty
+git clone https://github.com/KiCad/Inductors_NEOSID.pretty
+git clone https://github.com/KiCad/IR-DirectFETs.pretty
+git clone https://github.com/KiCad/Iut.pretty
+git clone https://github.com/KiCad/Labels.pretty
+git clone https://github.com/KiCad/LEDs.pretty
+git clone https://github.com/KiCad/Hall-Effect_Transducers_LEM.pretty
+git clone https://github.com/KiCad/Measurement_Points.pretty
+git clone https://github.com/KiCad/Measurement_Scales.pretty
+git clone https://github.com/KiCad/Mechanical_Sockets.pretty
+git clone https://github.com/KiCad/Mounting_Holes.pretty
+git clone https://github.com/KiCad/Muonde.pretty
+git clone https://github.com/KiCad/NF-Transformers_ETAL.pretty
+git clone https://github.com/KiCad/Oddities.pretty
+git clone https://github.com/KiCad/Transistors_OldSowjetAera.pretty
+git clone https://github.com/KiCad/Opto-Devices.pretty
+git clone https://github.com/KiCad/Oscillator-Modules.pretty
+git clone https://github.com/KiCad/Oscillators.pretty
+git clone https://github.com/KiCad/Pentawatts.pretty
+git clone https://github.com/KiCad/PFF_PSF_PSS_Leadforms.pretty
+git clone https://github.com/KiCad/Pin_Arrays.pretty
+git clone https://github.com/KiCad/Potentiometers.pretty
+git clone https://github.com/KiCad/Power_Integrations.pretty
+git clone https://github.com/KiCad/Printtrafo_CHK.pretty
+git clone https://github.com/KiCad/Relays_ThroughHole.pretty
+git clone https://github.com/KiCad/Resistors_SMD.pretty
+git clone https://github.com/KiCad/Resistors_ThroughHole.pretty
+git clone https://github.com/KiCad/Resistors_Universal.pretty
+git clone https://github.com/KiCad/QFP.pretty
+git clone https://github.com/KiCad/SMD_Packages.pretty
+git clone https://github.com/KiCad/Sockets_DIP.pretty
+git clone https://github.com/KiCad/Sockets_Mini-Universal.pretty
+git clone https://github.com/KiCad/Sockets.pretty
+git clone https://github.com/KiCad/Sockets_MOLEX_KK-System.pretty
+git clone https://github.com/KiCad/Sockets_PGA.pretty
+git clone https://github.com/KiCad/Sockets_WAGO734.pretty
+git clone https://github.com/KiCad/SOIC_Packages.pretty
+git clone https://github.com/KiCad/SSOP_Packages.pretty
+git clone https://github.com/KiCad/Capacitors_Tantalum_SMD.pretty
+git clone https://github.com/KiCad/Terminal_Blocks.pretty
+git clone https://github.com/KiCad/Transformers_SMPS_ThroughHole.pretty
+git clone https://github.com/KiCad/Transistors_SMD.pretty
+git clone https://github.com/KiCad/Transistors_TO-220.pretty
+git clone https://github.com/KiCad/Transistors_TO-247.pretty
+git clone https://github.com/KiCad/Valves.pretty
+git clone https://github.com/KiCad/Wire_Connections_Bridges.pretty
+git clone https://github.com/KiCad/Wire_Pads.pretty
+git clone https://github.com/KiCad/Pin_Headers.pretty
+git clone https://github.com/KiCad/Socket_Strips.pretty
diff --git a/scripts/library-repos-install.sh b/scripts/library-repos-install.sh
new file mode 100755
index 0000000..759184f
--- /dev/null
+++ b/scripts/library-repos-install.sh
@@ -0,0 +1,273 @@
+#!/bin/bash
+# Git KiCad library repos:
+#
+# The "install_prerequisites" step is the only "distro dependent" one. Could modify
+# that step for other linux distros.
+# This script requires "git". The package bzr-git is not up to the task.
+# The first time you run with option --install-or-update that is the slowest, because
+# git clone from github.com is slow.
+# After that updates should run faster.
+
+# There are two reasons why you might want to run this script:
+#
+# 1) You want to contribute to the KiCad library team maintained libraries and have yet to
+# discover or have chosen not to use the COW feature in the Github "Plugin Type".
+#
+# 2) You want to run with local pretty footprint libraries and not those remotely located
+# on https://github.com using Github plugin. After running this script you should be able to
+# a) $ cp ~/kicad_sources/library-repos/kicad-library/template/fp-lib-table.for-pretty ~/.config/kicad/fp-lib-table
+# and then
+# b) set your environment variable KISYSMOD to "~/kicad_sources/library-repos".
+# Edit /etc/profile.d/kicad.sh, then reboot.
+#
+# This will use the KiCad plugin against the *.pretty dirs in that base dir.
+
+
+# Set where the library repos will go, use a full path
+WORKING_TREES=${WORKING_TREES:-~/kicad_sources}
+
+
+usage()
+{
+ echo ""
+ echo " usage:"
+ echo ""
+ echo "./library-sources-install.sh <cmd>"
+ echo " where <cmd> is one of:"
+ echo " --install-prerequisites (install command tools needed here, run once first.)"
+ echo " --install-or-update (from github, the library sources.)"
+ echo " --remove-all-libraries (remove all *.pretty from $WORKING_TREES/library-repos/. )"
+ echo " --remove-orphaned-libraries (remove local libraries which have been deleted or renamed at github.)"
+ echo " --list-libraries (show the full list of github libraries.)"
+ echo " --create-bat-file (cat a windows batch file, redirect to capture to disk.)"
+ echo ""
+ echo "examples (with --install-prerequisites once first):"
+ echo ' $ ./library-sources-install.sh --install-prerequisites'
+ echo ' $ ./library-sources-install.sh --install-or-update'
+}
+
+
+install_prerequisites()
+{
+ # Find a package manager, PM
+ PM=$( command -v yum || command -v apt-get )
+
+ # assume all these Debian, Mint, Ubuntu systems have same prerequisites
+ if [ "$(expr match "$PM" '.*\(apt-get\)')" == "apt-get" ]; then
+ #echo "debian compatible system"
+ sudo apt-get install \
+ git \
+ curl \
+ sed
+
+ # assume all yum systems have same prerequisites
+ elif [ "$(expr match "$PM" '.*\(yum\)')" == "yum" ]; then
+ #echo "red hat compatible system"
+ # Note: if you find this list not to be accurate, please submit a patch:
+ sudo yum install \
+ git \
+ curl \
+ sed
+ else
+ echo
+ echo "Incompatible System. Neither 'yum' nor 'apt-get' found. Not possible to"
+ echo "continue. Please make sure to install git, curl, and sed before using this"
+ echo "script."
+ echo
+ exit 1
+ fi
+}
+
+
+rm_build_dir()
+{
+ local dir="$1"
+ # this file is often created as root, so remove as root
+ sudo rm "$dir/install_manifest.txt" 2> /dev/null
+ rm -rf "$dir"
+}
+
+
+cmake_uninstall()
+{
+ # assume caller set the CWD, and is only telling us about it in $1
+ local dir="$1"
+
+ cwd=`pwd`
+ if [ "$cwd" != "$dir" ]; then
+ echo "missing dir $dir"
+ elif [ ! -e install_manifest.txt ]; then
+ echo
+ echo "Missing file $dir/install_manifest.txt."
+ else
+ echo "uninstalling from $dir"
+ sudo make uninstall
+ sudo rm install_manifest.txt
+ fi
+}
+
+
+detect_pretty_repos()
+{
+ # Check for the correct option to enable extended regular expressions in
+ # sed. This is '-r' for GNU sed and '-E' for (older) BSD-like sed, as on
+ # Mac OSX.
+ if [ $(echo | sed -r '' &>/dev/null; echo $?) -eq 0 ]; then
+ SED_EREGEXP="-r"
+ elif [ $(echo | sed -E '' &>/dev/null; echo $?) -eq 0 ]; then
+ SED_EREGEXP="-E"
+ else
+ echo "Your sed command does not support extended regular expressions. Cannot continue."
+ exit 1
+ fi
+
+ # Use github API to list repos for org KiCad, then subset the JSON reply for only
+ # *.pretty repos in the "full_name" variable.
+ PRETTY_REPOS=`curl -s "https://api.github.com/orgs/KiCad/repos?per_page=99&page=1" \
+ "https://api.github.com/orgs/KiCad/repos?per_page=99&page=2" 2> /dev/null \
+ | sed $SED_EREGEXP 's:.+ "full_name".*"KiCad/(.+\.pretty)",:\1:p;d'`
+
+ #echo "PRETTY_REPOS:$PRETTY_REPOS"
+
+ PRETTY_REPOS=`echo $PRETTY_REPOS | tr " " "\n" | sort`
+
+ #echo "PRETTY_REPOS sorted:$PRETTY_REPOS"
+}
+
+
+checkout_or_update_libraries()
+{
+ if [ ! -d "$WORKING_TREES" ]; then
+ sudo mkdir -p "$WORKING_TREES"
+ echo " mark $WORKING_TREES as owned by me"
+ sudo chown -R `whoami` "$WORKING_TREES"
+ fi
+ cd $WORKING_TREES
+
+ detect_pretty_repos
+
+ if [ ! -e "$WORKING_TREES/library-repos" ]; then
+ mkdir -p "$WORKING_TREES/library-repos"
+ fi
+
+ for repo in kicad-library $PRETTY_REPOS; do
+ # echo "repo:$repo"
+
+ if [ ! -e "$WORKING_TREES/library-repos/$repo" ]; then
+
+ # Preserve the directory extension as ".pretty".
+ # That way those repos can serve as pretty libraries directly if need be.
+
+ echo "installing $WORKING_TREES/library-repos/$repo"
+ git clone "https://github.com/KiCad/$repo" "$WORKING_TREES/library-repos/$repo"
+ else
+ echo "updating $WORKING_TREES/library-repos/$repo"
+ cd "$WORKING_TREES/library-repos/$repo"
+ git pull
+ fi
+ done
+}
+
+
+listcontains()
+{
+ local list=$1
+ local item=$2
+ local ret=1
+ local OIFS=$IFS
+
+ # omit the space character from internal field separator.
+ IFS=$'\n'
+
+ for word in $list; do
+ if [ "$word" == "$item" ]; then
+ ret=0
+ break
+ fi
+ done
+
+ IFS=$OIFS
+ return $ret
+}
+
+
+remove_orphaned_libraries()
+{
+ cd $WORKING_TREES/library-repos
+
+ if [ $? -ne 0 ]; then
+ echo "Directory $WORKING_TREES/library-repos does not exist."
+ echo "The option --remove-orphaned-libraries should be used only after you've run"
+ echo "the --install-or-update at least once."
+ exit 2
+ fi
+
+ detect_pretty_repos
+
+ for mylib in *.pretty; do
+ echo "checking local lib: $mylib"
+
+ if ! listcontains "$PRETTY_REPOS" "$mylib"; then
+ echo "Removing orphaned local library $WORKING_TREES/library-repos/$mylib"
+ rm -rf "$mylib"
+ fi
+ done
+}
+
+
+if [ $# -eq 1 -a "$1" == "--install-or-update" ]; then
+ checkout_or_update_libraries
+ exit
+fi
+
+
+if [ $# -eq 1 -a "$1" == "--remove-orphaned-libraries" ]; then
+ remove_orphaned_libraries
+ exit
+fi
+
+
+if [ $# -eq 1 -a "$1" == "--remove-all-libraries" ]; then
+ rm -rf "$WORKING_TREES/library-repos"
+ exit
+fi
+
+
+if [ $# -eq 1 -a "$1" == "--install-prerequisites" ]; then
+ install_prerequisites
+ exit
+fi
+
+if [ $# -eq 1 -a "$1" == "--list-libraries" ]; then
+
+ # use github API to get repos into PRETTY_REPOS var
+ detect_pretty_repos
+
+ # add the "schematic parts & 3D model" kicad-library to total
+ for repo in $PRETTY_REPOS; do
+ echo "$repo"
+ done
+
+ echo
+ echo "and the special 'kicad-library' which holds 3D stuff and schematic parts"
+
+ exit
+fi
+
+# may re-direct this output to a disk file for Windows *.BAT file creation.
+if [ $# -eq 1 -a "$1" == "--create-bat-file" ]; then
+
+ # use github API to get repos into PRETTY_REPOS var
+ detect_pretty_repos
+
+ echo "REM This file was created using <kicad_src>/scripts/library-repos-install.sh on linux."
+ echo "REM Run it from a directory you desire as the base for all libraries."
+
+ # add the "schematic parts & 3D model" kicad-library to total
+ for repo in kicad-library $PRETTY_REPOS; do
+ echo "git clone https://github.com/KiCad/$repo"
+ done
+ exit
+fi
+
+usage
diff --git a/scripts/osx_build_wx.sh b/scripts/osx_build_wx.sh
new file mode 100755
index 0000000..d803d4c
--- /dev/null
+++ b/scripts/osx_build_wx.sh
@@ -0,0 +1,166 @@
+#!/bin/bash
+#
+# Small helper script for patching/compiling wxWidgets/wxPython on OSX
+#
+# Params
+# $1 wxWidgets/wxPython source folder (relative to current dir)
+# $2 Target bin folder
+# $3 KiCad source folder (relative to current dir)
+# $4 OSX target version (e.g., "10.8")
+# $5 Extra make options (e.g., "-j4")
+
+createPaths() {
+ echo "*** Creating/wiping build and bin folder..."
+
+ rm -rf wx-build
+ rm -rf $1
+ mkdir wx-build
+ mkdir $1
+}
+
+doPatch() {
+ cwd=$(pwd)
+ cd $1
+
+ patchcmd="patch -p0 -RN --dry-run < $cwd/$2"
+ eval $patchcmd &> /dev/null
+ if [ $? -eq 0 ];
+ then
+ echo "*** Patch '$2' already applied, skipping..."
+ else
+ echo "*** Applying patch '$2'..."
+
+ patch -p0 < $cwd/$2
+ if [ $? -ne 0 ];
+ then
+ cd $cwd
+ exit 1
+ fi
+ fi
+
+ cd $cwd
+}
+
+wxWidgets_configure() {
+ echo "*** Configuring wxWidgets..."
+ cwd=$(pwd)
+ cd wx-build
+
+ ../$1/configure \
+ --prefix=$cwd/$2 \
+ --with-opengl \
+ --enable-aui \
+ --enable-utf8 \
+ --enable-html \
+ --enable-stl \
+ --with-libjpeg=builtin \
+ --with-libpng=builtin \
+ --with-regex=builtin \
+ --with-libtiff=builtin \
+ --with-zlib=builtin \
+ --with-expat=builtin \
+ --without-liblzma \
+ --with-macosx-version-min=$3 \
+ --enable-universal-binary=i386,x86_64 \
+ CC=clang \
+ CXX=clang++
+ if [ $? -ne 0 ];
+ then
+ cd ..
+ exit 1
+ fi
+
+ cd ..
+}
+
+wxWidgets_buildInst() {
+ echo "*** Building wxWidgets..."
+ cd wx-build
+
+ make $1 install
+ if [ $? -ne 0 ];
+ then
+ cd ..
+ exit 1
+ fi
+
+ cd ..
+}
+
+wxPython_buildInst() {
+ cwd=$(pwd)
+ cd $1/wxPython
+
+ # build params
+ WXPYTHON_BUILD_OPTS="WX_CONFIG=$cwd/$2/bin/wx-config \
+ BUILD_BASE=$cwd/wx-build \
+ UNICODE=1 \
+ WXPORT=osx_cocoa"
+
+ # build
+ python setup.py build_ext $WXPYTHON_BUILD_OPTS
+ if [ $? -ne 0 ];
+ then
+ cd $cwd
+ exit 1
+ fi
+
+ # install
+ python setup.py install --prefix=$cwd/$2 $WXPYTHON_BUILD_OPTS
+ if [ $? -ne 0 ];
+ then
+ cd $cwd
+ exit 1
+ fi
+
+ cd $cwd
+}
+
+
+# check parameters
+if [ "$#" -lt 4 ];
+then
+ echo "OSX wxWidgets/wxPython build script"
+ echo
+ echo "Usage:"
+ echo " osx_build_wx.sh <src> <bin> <kicad> <osxtarget> [makeopts]"
+ echo " <src> wxWidgets/wxPython source folder"
+ echo " <bin> Destination folder"
+ echo " <kicad> KiCad folder"
+ echo " <osxtarget> OSX target (e.g., 10.7)"
+ echo " [makeopts] Optional: make options for building wxWidgets (e.g., -j4)"
+ exit 1
+fi
+
+# create build paths
+createPaths "$2"
+
+# patch wxWidgets sources
+echo "*** Patching wxWidgets..."
+doPatch "$1" "$3/patches/wxwidgets-3.0.0_macosx.patch"
+doPatch "$1" "$3/patches/wxwidgets-3.0.0_macosx_bug_15908.patch"
+doPatch "$1" "$3/patches/wxwidgets-3.0.0_macosx_soname.patch"
+# high resolution in OpenGL canvas: http://trac.wxwidgets.org/ticket/15700
+doPatch "$1" "$3/patches/wxwidgets-3.0.2_macosx_retina_opengl.patch"
+# patch to support pinch-to-zoom on trackpads
+doPatch "$1" "$3/patches/wxwidgets-3.0.2_macosx_magnify_event.patch"
+
+# configure and build wxWidgets
+wxWidgets_configure "$1" "$2" "$4"
+wxWidgets_buildInst "$5"
+
+# check if source is wxPython
+if [ -d $1/wxPython ];
+then
+ echo "*** Source is wxPython, now building wxPython stuff..."
+ wxPython_buildInst "$1" "$2"
+fi
+
+# remove build dir
+echo "*** Removing build folder"
+rm -rf wx-build
+
+# done
+echo "*** Finished building!"
+
+
diff --git a/scripts/osx_fixbundle.sh b/scripts/osx_fixbundle.sh
new file mode 100755
index 0000000..a1f21de
--- /dev/null
+++ b/scripts/osx_fixbundle.sh
@@ -0,0 +1,129 @@
+#!/bin/bash
+# v 1.1 Supports migration of links (limited to the same directory) - I should add checks..
+# usage osx_fixbundle.sh <bundle-name> <bzr_root>
+
+if [[ ! -f version.h ]]; then
+ echo "**"
+ echo "** ERROR: $0 doesn't seems to be launched from the kicad's bzr root !!!"
+ echo "** Go in the bzr root directory and launch: scripts/osx_fixbundle.sh"
+ echo "**"
+ exit 1
+fi
+
+EXECUTABLES="`find . -name '*.app'`"
+
+#
+# Copies libraries under <bzr_root> in the bundle and relocates them in the binary
+#
+
+function fixbundle() {
+ exec="$1"
+ bzroot="$2"
+ execpath="$3"
+ binary="$4"
+
+ LIBRARIES="`otool -L ${binary} | cut -d' ' -f1`"
+
+ for library in $LIBRARIES; do
+
+ mkdir -p ${execpath}${exec}.app/Contents/Frameworks
+ if [[ "$library" =~ "$bzroot" ]]; then
+ echo "${exec}: Migrating `basename $library` in the bundle"
+ if [ ! -f ${exec}.app/Contents/Frameworks/`basename $library` ]; then
+ if [ ! -L $library ]; then
+ cp -f $library ${execpath}${exec}.app/Contents/Frameworks
+ else
+ resolvelink "$library" "`dirname $library`" "${execpath}/${exec}.app/Contents/Frameworks"
+ fi
+ fi
+ install_name_tool -change $library @executable_path/../Frameworks/`basename $library` ${binary}
+ fi
+ done
+
+ # Resolve issue in python modules (.so)
+ cd ${execpath}
+ MODULES="`find ${exec}.app -name '*.so'`"
+
+ for module in $MODULES; do
+ LIBRARIES="`otool -L $module | cut -d' ' -f1`"
+ mkdir -p ${exec}.app/Contents/Frameworks
+
+ for library in $LIBRARIES; do
+ if [[ "$library" =~ "$bzroot" ]]; then
+ if [ ! -f ${exec}.app/Contents/Frameworks/`basename $library` ]; then
+ if [ ! -L $library ]; then
+ cp -f $library ${exec}.app/Contents/Frameworks
+ else
+ resolvelink "$library" "`dirname $library`" "${execpath}/${exec}.app/Contents/Frameworks"
+ fi
+ fi
+ install_name_tool -change $library @executable_path/../Frameworks/`basename $library` $module
+ fi
+ done
+ echo "${exec}: elaborated module `basename ${module}`"
+ done
+
+ # Resolve issue between DYNLIBS
+ dynlib_migrate="1";
+ dynlib_cycle="0";
+
+ while [ $dynlib_migrate -gt 0 ]; do
+ dynlib_migrate="0";
+ (( dynlib_cycle += 1 ))
+ DYNLIBS="`find ${exec}.app -name '*.dylib'`"
+
+ for dynlib in $DYNLIBS; do
+ LIBRARIES="`otool -L $dynlib | cut -d' ' -f1`"
+ mkdir -p ${exec}.app/Contents/Frameworks
+
+ for library in $LIBRARIES; do
+ if [[ "$library" =~ "$bzroot" ]]; then
+ if [ ! -f ${exec}.app/Contents/Frameworks/`basename $library` ]; then
+ if [ ! -L $library ]; then
+ cp -f $library ${exec}.app/Contents/Frameworks
+ else
+ resolvelink "$library" "`dirname $library`" "${execpath}/${exec}.app/Contents/Frameworks"
+ fi
+ echo "copied `basename $library` into bundle"
+ (( dynlib_migrate += 1))
+ fi
+
+ install_name_tool -change $library @executable_path/../Frameworks/`basename $library` $dynlib
+ fi
+ done
+ done
+ echo "${exec}: bundle dynlib dependencies migration Pass $dynlib_cycle: Migrated $dynlib_migrate libraries in bundle"
+ done
+ cd - >/dev/null
+}
+
+#
+# This supports only links on the same dir (TODO ?)
+#
+
+function resolvelink() {
+ local srclib="`basename $1`"
+ local srcpath="$2"
+ local destpath="$3"
+
+ #if is a file i expect a pointed ""
+ local pointed="`readlink ${srcpath}/${srclib}`"
+
+ if [ ! -f ${pointed} ]; then
+ resolvelink "${pointed}" "${srcpath}" "${destpath}"
+ echo "Link ${srclib} -> ${pointed} "
+ (cd "${destpath}"; ln -s "${pointed}" "${srclib}" )
+ else
+ echo "Copy ${srcpath}/${srclib} -> ${destpath} "
+ cp "${srcpath}/${srclib}" ${destpath}
+ fi
+}
+
+for executable in $EXECUTABLES;
+do
+ myexecpath="`dirname ${executable}`/"
+ myexec="`basename ${executable}|sed -e 's/\.app//'`"
+
+ fixbundle "${myexec}" "$1" "${myexecpath}" "${myexecpath}${myexec}.app/Contents/MacOS/${myexec}"
+ fixbundle "${myexec}" "$1" "${myexecpath}" "${myexecpath}${myexec}.app/Contents/MacOS/_${myexec}.kiface"
+done
diff --git a/scripts/test_kicad_plugin.py b/scripts/test_kicad_plugin.py
new file mode 100755
index 0000000..3bb20cc
--- /dev/null
+++ b/scripts/test_kicad_plugin.py
@@ -0,0 +1,85 @@
+#!/usr/bin/python
+
+# Test the KiCad plugin regarding some expected features.
+
+# 1) Build target _pcbnew after enabling scripting in cmake.
+# $ make _pcbnew
+
+# 2) Changed dir to pcbnew
+# $ cd pcbnew
+# $ pwd
+# build/pcbnew
+
+# 3) Entered following command line, script takes no arguments
+# $ PYTHONPATH=. <path_to>/test_kicad_plugin.py
+
+from pcbnew import IO_MGR, BOARD, MODULE, FPID, UTF8
+from os import rename as mv
+
+tmp_path = '/tmp'
+lib_path1 = "%s/lib1.pretty" % tmp_path
+lib_path2 = "%s/lib2.pretty" % tmp_path
+
+plugin = IO_MGR.PluginFind( IO_MGR.KICAD )
+
+# Expecting "KiCad":
+print( "Plugin Type: %s" % plugin.PluginName() )
+
+try:
+ plugin.FootprintLibDelete( lib_path1 )
+except:
+ pass # ignore, new may not exist if first run
+
+try:
+ plugin.FootprintLibDelete( lib_path2 )
+except:
+ pass # ignore, new may not exist if first run
+
+plugin.FootprintLibCreate( lib_path1 )
+
+# Verify that the same plugin instance can edge trigger on a lib_path change
+# for a FootprintLibCreate()
+plugin.FootprintLibCreate( lib_path2 )
+
+board = BOARD()
+
+# The only way to construct a MODULE is to pass it a BOARD? Yep.
+module = MODULE( board )
+
+fpid = FPID( 'mine' )
+
+module.SetFPID( fpid )
+
+plugin.FootprintSave( lib_path2, module )
+
+# Verify that the same plugin instance can edge trigger on a lib_path change
+# for a FootprintSave()
+plugin.FootprintSave( lib_path1, module )
+
+# create a disparity between the library's name ("footprint"),
+# and the module's internal useless name ("mine"). Module is officially named "footprint" now
+# but has (module mine ...) internally:
+mv( "%s/mine.kicad_mod" % lib_path2, "%s/footprint.kicad_mod" % lib_path2 )
+
+footprint = plugin.FootprintLoad( lib_path2, 'footprint' )
+
+fpid = footprint.GetFPID()
+fpid.SetLibNickname( UTF8( 'mylib' ) )
+name = fpid.Format().GetChars() # example to get the UTF8 char buffer
+
+# Always after a FootprintLoad() the internal name should match the one used to load it.
+print( "Internal name should be 'footprint': '%s'" % name )
+
+# Verify that the same plugin instance can edge trigger on a lib_path change
+# for FootprintLoad()
+footprint = plugin.FootprintLoad( lib_path1, 'mine' )
+
+fpid = footprint.GetFPID()
+fpid.SetLibNickname( UTF8( 'other_mylib' ) )
+
+# Always after a FootprintLoad() the internal name should match the one used to load it.
+# Example to print an UTF8 string
+print( "Internal name should be 'mine': '%s'" % fpid.Format() )
+
+# As of 3-Dec-2013 this test is passed by KICAD_PLUGIN and Wayne is owed an atta boy!
+
diff --git a/scripts/test_plugin.py b/scripts/test_plugin.py
new file mode 100755
index 0000000..3755607
--- /dev/null
+++ b/scripts/test_plugin.py
@@ -0,0 +1,48 @@
+#!/usr/bin/python
+
+# Test a basic back to back FootprintLoad() of a single footprint, and FootprintEnumerate()
+
+# 1) Build target _pcbnew after enabling scripting in cmake.
+# $ make _pcbnew
+
+# 2) Changed dir to pcbnew
+# $ cd pcbnew
+# $ pwd
+# build/pcbnew
+
+# 3) Entered following command line, script takes to arguments: library_path [footprint_name]
+# $ PYTHONPATH=. <path_to>/test_plugin.py https://github.com/liftoff-sr/pretty_footprints [100-LQFP]
+
+
+from __future__ import print_function
+from pcbnew import *
+import sys
+
+if len( sys.argv ) < 2 :
+ print( "usage: script <library_path> [<footprint_name>]" )
+ sys.exit(1)
+
+
+src_libpath = sys.argv[1]
+
+
+src_type = IO_MGR.GuessPluginTypeFromLibPath( src_libpath );
+
+src_plugin = IO_MGR.PluginFind( src_type )
+
+if len( sys.argv ) == 2:
+ list_of_footprints = src_plugin.FootprintEnumerate( src_libpath )
+ for fp in list_of_footprints:
+ print( fp )
+
+elif len( sys.argv ) == 3:
+ # I had some concerns about back to back reads, this verifies it is no problem:
+ module = src_plugin.FootprintLoad( src_libpath, sys.argv[2] )
+ if not module:
+ print( "1st try: module", sys.argv[2], "not found" )
+ module = src_plugin.FootprintLoad( src_libpath, sys.argv[2] )
+ if not module:
+ print( "2nd try: module", sys.argv[2], "not found" )
+ print( module )
+
+
diff --git a/template/CMakeLists.txt b/template/CMakeLists.txt
new file mode 100644
index 0000000..a804e9e
--- /dev/null
+++ b/template/CMakeLists.txt
@@ -0,0 +1,10 @@
+install( FILES
+ kicad.pro
+ gost_landscape.kicad_wks
+ gost_portrait.kicad_wks
+ pagelayout_default.kicad_wks
+ pagelayout_logo.kicad_wks
+
+ DESTINATION ${KICAD_TEMPLATE}
+ COMPONENT resources
+ )
diff --git a/template/gost_landscape.kicad_wks b/template/gost_landscape.kicad_wks
new file mode 100644
index 0000000..6758ea9
--- /dev/null
+++ b/template/gost_landscape.kicad_wks
@@ -0,0 +1,87 @@
+(page_layout
+ (setup (textsize 2.5 2.5)(linewidth 0.3)(textlinewidth 0.25)
+ (left_margin 8)(right_margin 5)(top_margin 5)(bottom_margin 5))
+ (line (name segm1:Line) (start 0 60 lbcorner) (end 12 60 lbcorner) (linewidth 0.6) (repeat 3) (incry 25))
+ (line (name segm2:Line) (start 96 0 ltcorner) (end 96 14 ltcorner) (option page1only) (linewidth 0.6) (repeat 2) (incrx 53))
+ (line (name segm3:Line) (start 82 0 ltcorner) (end 82 14 ltcorner) (linewidth 0.6))
+ (line (name segm4:Line) (start 96 14 ltcorner) (end 149 14 ltcorner) (option page1only) (linewidth 0.6) (repeat 2) (incry -7))
+ (line (name segm5:Line) (start 82 14 ltcorner) (end 96 14 ltcorner) (option page1only) (linewidth 0.6))
+ (line (name segm6:Line) (start 12 14 ltcorner) (end 82 14 ltcorner) (linewidth 0.6))
+ (line (name segm7:Line) (start 0 145 lbcorner) (end 12 145 lbcorner) (linewidth 0.6))
+ (line (name segm8:Line) (start 0 0 lbcorner) (end 12 0 lbcorner) (linewidth 0.6) (repeat 2) (incry 25))
+ (line (name segm9:Line) (start 120 55) (end 120 0) (option page1only) (linewidth 0.6))
+ (line (name segm10:Line) (start 130 55) (end 130 0) (option page1only) (linewidth 0.6))
+ (line (name segm11:Line) (start 145 55) (end 145 0) (option page1only) (linewidth 0.6))
+ (line (name segm12:Line) (start 168 55) (end 168 0) (option page1only) (linewidth 0.6))
+ (line (name segm13:Line) (start 178 55) (end 178 30) (option page1only) (linewidth 0.6))
+ (line (name segm14:Line) (start 185 40) (end 120 40) (option page1only) (repeat 3) (incry 5))
+ (line (name segm15:Line) (start 185 30) (end 120 30) (option page1only) (linewidth 0.6) (repeat 2) (incry 5))
+ (line (name segm16:Line) (start 185 5) (end 120 5) (option page1only) (repeat 5) (incry 5))
+ (line (name segm17:Line) (start 120 63) (end 0 63) (option page1only) (linewidth 0.6))
+ (line (name segm18:Line) (start 120 55) (end 120 63) (option page1only) (linewidth 0.6))
+ (line (name segm19:Line) (start 185 55) (end 0 55) (option page1only) (linewidth 0.6))
+ (line (name segm20:Line) (start 0 145 lbcorner) (end 0 0 lbcorner) (linewidth 0.6) (repeat 2) (incrx 5))
+ (line (name segm21:Line) (start 45 35) (end 45 20) (option page1only) (repeat 2) (incrx -5))
+ (line (name segm22:Line) (start 35 40) (end 35 20) (option page1only) (linewidth 0.6) (repeat 2) (incrx -17))
+ (line (name segm23:Line) (start 50 20) (end 0 20) (option page1only) (linewidth 0.6) (repeat 2) (incry 15))
+ (line (name segm24:Line) (start 50 40) (end 50 0) (option page1only) (linewidth 0.6))
+ (line (name segm25:Line) (start 120 15) (end 0 15) (option page1only) (linewidth 0.6))
+ (line (name segm26:Line) (start 120 40) (end 0 40) (option page1only) (linewidth 0.6))
+ (line (name segm27:Line) (start 185 0) (end 185 55) (option page1only) (linewidth 0.6))
+ (rect (name rect1:Rect) (start 12 0 lbcorner) (end 0 0 rtcorner) (linewidth 0.6))
+ (tbtext ЛиŃŃ‚ (name text1:Text) (pos 173 32.5) (option page1only) (font italic) (justify center))
+ (tbtext %C2 (name text2:Text) (pos 167.5 22.5) (option page1only) (font italic) (maxlen 21.5))
+ (tbtext Пров. (name text3:Text) (pos 184.5 22.5) (option page1only) (font italic))
+ (tbtext Утв. (name text4:Text) (pos 184.5 2.5) (option page1only) (font italic))
+ (tbtext Н.контр. (name text5:Text) (pos 184.5 7.5) (option page1only) (font italic))
+ (tbtext Лит. (name text6:Text) (pos 42 37.5) (option page1only) (font italic) (justify center))
+ (tbtext %C0 (name text7:Text) (pos 60 47.5) (option page1only) (font (linewidth 0.5) (size 5 5) italic) (justify center) (maxlen 119))
+ (tbtext %N (name text8:Text) (pos 8 17.5) (option page1only) (font italic) (justify center))
+ (line (name segm28:Line) (start 185 15) (end 185 0) (option notonpage1) (linewidth 0.6))
+ (line (name segm29:Line) (start 185 15) (end 0 15) (option notonpage1) (linewidth 0.6))
+ (tbtext %Y (name text9:Text) (pos 25 7) (option page1only) (font (linewidth 0.35) (size 3.5 3.5) italic) (justify center) (maxlen 48) (maxheight 14))
+ (tbtext %T (name text10:Text) (pos 85 27.5) (option page1only) (font (linewidth 0.35) (size 3.5 3.5) italic) (justify center) (maxlen 67) (maxheight 22))
+ (tbtext ЛиŃтов (name text11:Text) (pos 29 17.5) (option page1only) (font italic))
+ (tbtext %C0 (name text12:Text) (pos 47 7 ltcorner) (rotate 180) (font (linewidth 0.35) (size 3.5 3.5) italic) (justify center) (maxlen 69))
+ (tbtext %S (name text13:Text) (pos 35 17.5) (option page1only) (font italic) (justify center))
+ (tbtext ЛиŃŃ‚ (name text14:Text) (pos 49 17.5) (option page1only) (font italic))
+ (line (name segm30:Line) (start 30 20) (end 30 15) (option page1only) (linewidth 0.6))
+ (tbtext МаŃŃтаб (name text15:Text) (pos 9 37.5) (option page1only) (font italic) (justify center))
+ (tbtext МаŃŃĐ° (name text16:Text) (pos 26.5 37.5) (option page1only) (font italic) (justify center))
+ (tbtext %C3 (name text17:Text) (pos 167.5 2.5) (option page1only) (font italic) (maxlen 21.5))
+ (tbtext %C1 (name text18:Text) (pos 167.5 27.5) (option page1only) (font italic) (maxlen 21.5))
+ (tbtext Разраб. (name text19:Text) (pos 184.5 27.5) (option page1only) (font italic))
+ (tbtext Дата (name text20:Text) (pos 125 32.5) (option page1only) (font italic) (justify center))
+ (tbtext Подп. (name text21:Text) (pos 137 32.5) (option page1only) (font italic) (justify center))
+ (tbtext N°докŃĐĽ. (name text22:Text) (pos 156.5 32.5) (option page1only) (font italic) (justify center))
+ (tbtext ĐĐ·ĐĽ. (name text23:Text) (pos 181.5 32.5) (option page1only) (font italic) (justify center) (maxlen 6.5))
+ (line (name segm31:Line) (start 0 287 lbcorner) (end 12 287 lbcorner) (option page1only) (linewidth 0.6) (repeat 3) (incry -60))
+ (tbtext Взам.инв.N° (name text24:Text) (pos 2.5 72.5 lbcorner) (rotate 90) (font italic) (justify center))
+ (tbtext Т.контр. (name text25:Text) (pos 184.5 17.5) (option page1only) (font italic))
+ (tbtext "Подп. и дата" (name text26:Text) (pos 2.5 42.5 lbcorner) (rotate 90) (font italic) (justify center))
+ (tbtext Đнв.N°дŃбл. (name text27:Text) (pos 2.5 97.5 lbcorner) (rotate 90) (font italic) (justify center))
+ (tbtext Đнв.N°подл. (name text28:Text) (pos 2.5 12.5 lbcorner) (rotate 90) (font italic) (justify center))
+ (line (name segm32:Line) (start 0 287 lbcorner) (end 0 167 lbcorner) (option page1only) (linewidth 0.6) (repeat 2) (incrx 5))
+ (tbtext "Подп. и дата" (name text29:Text) (pos 2.5 127.5 lbcorner) (rotate 90) (font italic) (justify center))
+ (tbtext "Перв. примен." (name text30:Text) (pos 2.5 257 lbcorner) (option page1only) (rotate 90) (font italic) (justify center))
+ (tbtext "Справ. N°" (name text31:Text) (pos 2.5 197 lbcorner) (option page1only) (rotate 90) (font italic) (justify center))
+ (tbtext %S (name text32:Text) (pos 5 4) (option notonpage1) (font italic) (justify center))
+ (tbtext ЛиŃŃ‚ (name text33:Text) (pos 5 11.5) (option notonpage1) (font italic) (justify center))
+ (tbtext Копировал (name text34:Text) (pos 110 -2.5) (font italic))
+ (tbtext "Формат %Z" (name text35:Text) (pos 40 -2.5) (font italic))
+ (tbtext %C0 (name text36:Text) (pos 65 7.5) (option notonpage1) (font (linewidth 0.5) (size 5 5) italic) (justify center) (maxlen 109) (maxheight 14))
+ (tbtext Дата (name text37:Text) (pos 125 2.5) (option notonpage1) (font italic) (justify center))
+ (tbtext Подп. (name text38:Text) (pos 137.5 2.5) (option notonpage1) (font italic) (justify center))
+ (tbtext N°докŃĐĽ. (name text39:Text) (pos 156.5 2.5) (option notonpage1) (font italic) (justify center))
+ (tbtext ЛиŃŃ‚ (name text40:Text) (pos 173 2.5) (option notonpage1) (font italic) (justify center))
+ (tbtext ĐĐ·ĐĽ. (name text41:Text) (pos 181.5 2.5) (option notonpage1) (font italic) (justify center) (maxlen 6.5))
+ (line (name segm33:Line) (start 10 8) (end 0 8) (option notonpage1) (linewidth 0.6))
+ (line (name segm34:Line) (start 10 15) (end 10 0) (option notonpage1) (linewidth 0.6))
+ (line (name segm35:Line) (start 185 10) (end 120 10) (option notonpage1))
+ (line (name segm36:Line) (start 185 5) (end 120 5) (option notonpage1) (linewidth 0.6))
+ (line (name segm37:Line) (start 120 15) (end 120 0) (option notonpage1) (linewidth 0.6))
+ (line (name segm38:Line) (start 130 15) (end 130 0) (option notonpage1) (linewidth 0.6))
+ (line (name segm39:Line) (start 145 15) (end 145 0) (option notonpage1) (linewidth 0.6))
+ (line (name segm40:Line) (start 168 15) (end 168 0) (option notonpage1) (linewidth 0.6))
+ (line (name segm41:Line) (start 178 15) (end 178 0) (option notonpage1) (linewidth 0.6))
+)
diff --git a/template/gost_portrait.kicad_wks b/template/gost_portrait.kicad_wks
new file mode 100644
index 0000000..0c4fce6
--- /dev/null
+++ b/template/gost_portrait.kicad_wks
@@ -0,0 +1,87 @@
+(page_layout
+ (setup (textsize 2.5 2.5)(linewidth 0.3)(textlinewidth 0.25)
+ (left_margin 8)(right_margin 5)(top_margin 5)(bottom_margin 5))
+ (line (name segm1:Line) (start 0 60 lbcorner) (end 12 60 lbcorner) (linewidth 0.6) (repeat 3) (incry 25))
+ (line (name segm2:Line) (start 14 84 rtcorner) (end 0 84 rtcorner) (option page1only) (linewidth 0.6) (repeat 2) (incry 53))
+ (line (name segm3:Line) (start 14 70 rtcorner) (end 0 70 rtcorner) (linewidth 0.6))
+ (line (name segm4:Line) (start 14 84 rtcorner) (end 14 137 rtcorner) (option page1only) (linewidth 0.6) (repeat 2) (incrx -7))
+ (line (name segm5:Line) (start 14 70 rtcorner) (end 14 84 rtcorner) (option page1only) (linewidth 0.6))
+ (line (name segm6:Line) (start 14 0 rtcorner) (end 14 70 rtcorner) (linewidth 0.6))
+ (line (name segm7:Line) (start 0 145 lbcorner) (end 12 145 lbcorner) (linewidth 0.6))
+ (line (name segm8:Line) (start 0 0 lbcorner) (end 12 0 lbcorner) (linewidth 0.6) (repeat 2) (incry 25))
+ (line (name segm9:Line) (start 120 55) (end 120 0) (option page1only) (linewidth 0.6))
+ (line (name segm10:Line) (start 130 55) (end 130 0) (option page1only) (linewidth 0.6))
+ (line (name segm11:Line) (start 145 55) (end 145 0) (option page1only) (linewidth 0.6))
+ (line (name segm12:Line) (start 168 55) (end 168 0) (option page1only) (linewidth 0.6))
+ (line (name segm13:Line) (start 178 55) (end 178 30) (option page1only) (linewidth 0.6))
+ (line (name segm14:Line) (start 185 40) (end 120 40) (option page1only) (repeat 3) (incry 5))
+ (line (name segm15:Line) (start 185 30) (end 120 30) (option page1only) (linewidth 0.6) (repeat 2) (incry 5))
+ (line (name segm16:Line) (start 185 5) (end 120 5) (option page1only) (repeat 5) (incry 5))
+ (line (name segm17:Line) (start 120 63) (end 0 63) (option page1only) (linewidth 0.6))
+ (line (name segm18:Line) (start 120 55) (end 120 63) (option page1only) (linewidth 0.6))
+ (line (name segm19:Line) (start 185 55) (end 0 55) (option page1only) (linewidth 0.6))
+ (line (name segm20:Line) (start 0 145 lbcorner) (end 0 0 lbcorner) (linewidth 0.6) (repeat 2) (incrx 5))
+ (line (name segm21:Line) (start 45 35) (end 45 20) (option page1only) (repeat 2) (incrx -5))
+ (line (name segm22:Line) (start 35 40) (end 35 20) (option page1only) (linewidth 0.6) (repeat 2) (incrx -17))
+ (line (name segm23:Line) (start 50 20) (end 0 20) (option page1only) (linewidth 0.6) (repeat 2) (incry 15))
+ (line (name segm24:Line) (start 50 40) (end 50 0) (option page1only) (linewidth 0.6))
+ (line (name segm25:Line) (start 120 15) (end 0 15) (option page1only) (linewidth 0.6))
+ (line (name segm26:Line) (start 120 40) (end 0 40) (option page1only) (linewidth 0.6))
+ (line (name segm27:Line) (start 185 0) (end 185 55) (option page1only) (linewidth 0.6))
+ (rect (name rect1:Rect) (start 12 0 lbcorner) (end 0 0 rtcorner) (linewidth 0.6))
+ (tbtext ЛиŃŃ‚ (name text1:Text) (pos 173 32.5) (option page1only) (font italic) (justify center))
+ (tbtext %C2 (name text2:Text) (pos 167.5 22.5) (option page1only) (font italic) (maxlen 21.5))
+ (tbtext Пров. (name text3:Text) (pos 184.5 22.5) (option page1only) (font italic))
+ (tbtext Утв. (name text4:Text) (pos 184.5 2.5) (option page1only) (font italic))
+ (tbtext Н.контр. (name text5:Text) (pos 184.5 7.5) (option page1only) (font italic))
+ (tbtext Лит. (name text6:Text) (pos 42 37.5) (option page1only) (font italic) (justify center))
+ (tbtext %C0 (name text7:Text) (pos 60 47.5) (option page1only) (font (linewidth 0.5) (size 5 5) italic) (justify center) (maxlen 119))
+ (tbtext %N (name text8:Text) (pos 8 17.5) (option page1only) (font italic) (justify center))
+ (line (name segm28:Line) (start 185 15) (end 185 0) (option notonpage1) (linewidth 0.6))
+ (line (name segm29:Line) (start 185 15) (end 0 15) (option notonpage1) (linewidth 0.6))
+ (tbtext %Y (name text9:Text) (pos 25 7) (option page1only) (font (linewidth 0.35) (size 3.5 3.5) italic) (justify center) (maxlen 48) (maxheight 14))
+ (tbtext %T (name text10:Text) (pos 85 27.5) (option page1only) (font (linewidth 0.35) (size 3.5 3.5) italic) (justify center) (maxlen 67) (maxheight 22))
+ (tbtext ЛиŃтов (name text11:Text) (pos 29 17.5) (option page1only) (font italic))
+ (tbtext %C0 (name text12:Text) (pos 7 35 rtcorner) (rotate 90) (font (linewidth 0.35) (size 3.5 3.5) italic) (justify center) (maxlen 69))
+ (tbtext %S (name text13:Text) (pos 35 17.5) (option page1only) (font italic) (justify center))
+ (tbtext ЛиŃŃ‚ (name text14:Text) (pos 49 17.5) (option page1only) (font italic))
+ (line (name segm30:Line) (start 30 20) (end 30 15) (option page1only) (linewidth 0.6))
+ (tbtext МаŃŃтаб (name text15:Text) (pos 9 37.5) (option page1only) (font italic) (justify center))
+ (tbtext МаŃŃĐ° (name text16:Text) (pos 26.5 37.5) (option page1only) (font italic) (justify center))
+ (tbtext %C3 (name text17:Text) (pos 167.5 2.5) (option page1only) (font italic) (maxlen 21.5))
+ (tbtext %C1 (name text18:Text) (pos 167.5 27.5) (option page1only) (font italic) (maxlen 21.5))
+ (tbtext Разраб. (name text19:Text) (pos 184.5 27.5) (option page1only) (font italic))
+ (tbtext Дата (name text20:Text) (pos 125 32.5) (option page1only) (font italic) (justify center))
+ (tbtext Подп. (name text21:Text) (pos 137 32.5) (option page1only) (font italic) (justify center))
+ (tbtext N°докŃĐĽ. (name text22:Text) (pos 156.5 32.5) (option page1only) (font italic) (justify center))
+ (tbtext ĐĐ·ĐĽ. (name text23:Text) (pos 181.5 32.5) (option page1only) (font italic) (justify center) (maxlen 6.5))
+ (line (name segm31:Line) (start 0 287 lbcorner) (end 12 287 lbcorner) (option page1only) (linewidth 0.6) (repeat 3) (incry -60))
+ (tbtext Взам.инв.N° (name text24:Text) (pos 2.5 72.5 lbcorner) (rotate 90) (font italic) (justify center))
+ (tbtext Т.контр. (name text25:Text) (pos 184.5 17.5) (option page1only) (font italic))
+ (tbtext "Подп. и дата" (name text26:Text) (pos 2.5 42.5 lbcorner) (rotate 90) (font italic) (justify center))
+ (tbtext Đнв.N°дŃбл. (name text27:Text) (pos 2.5 97.5 lbcorner) (rotate 90) (font italic) (justify center))
+ (tbtext Đнв.N°подл. (name text28:Text) (pos 2.5 12.5 lbcorner) (rotate 90) (font italic) (justify center))
+ (line (name segm32:Line) (start 0 287 lbcorner) (end 0 167 lbcorner) (option page1only) (linewidth 0.6) (repeat 2) (incrx 5))
+ (tbtext "Подп. и дата" (name text29:Text) (pos 2.5 127.5 lbcorner) (rotate 90) (font italic) (justify center))
+ (tbtext "Перв. примен." (name text30:Text) (pos 2.5 257 lbcorner) (option page1only) (rotate 90) (font italic) (justify center))
+ (tbtext "Справ. N°" (name text31:Text) (pos 2.5 197 lbcorner) (option page1only) (rotate 90) (font italic) (justify center))
+ (tbtext %S (name text32:Text) (pos 5 4) (option notonpage1) (font italic) (justify center))
+ (tbtext ЛиŃŃ‚ (name text33:Text) (pos 5 11.5) (option notonpage1) (font italic) (justify center))
+ (tbtext Копировал (name text34:Text) (pos 110 -2.5) (font italic))
+ (tbtext "Формат %Z" (name text35:Text) (pos 40 -2.5) (font italic))
+ (tbtext %C0 (name text36:Text) (pos 65 7.5) (option notonpage1) (font (linewidth 0.5) (size 5 5) italic) (justify center) (maxlen 109) (maxheight 14))
+ (tbtext Дата (name text37:Text) (pos 125 2.5) (option notonpage1) (font italic) (justify center))
+ (tbtext Подп. (name text38:Text) (pos 137.5 2.5) (option notonpage1) (font italic) (justify center))
+ (tbtext N°докŃĐĽ. (name text39:Text) (pos 156.5 2.5) (option notonpage1) (font italic) (justify center))
+ (tbtext ЛиŃŃ‚ (name text40:Text) (pos 173 2.5) (option notonpage1) (font italic) (justify center))
+ (tbtext ĐĐ·ĐĽ. (name text41:Text) (pos 181.5 2.5) (option notonpage1) (font italic) (justify center) (maxlen 6.5))
+ (line (name segm33:Line) (start 10 8) (end 0 8) (option notonpage1) (linewidth 0.6))
+ (line (name segm34:Line) (start 10 15) (end 10 0) (option notonpage1) (linewidth 0.6))
+ (line (name segm35:Line) (start 185 10) (end 120 10) (option notonpage1))
+ (line (name segm36:Line) (start 185 5) (end 120 5) (option notonpage1) (linewidth 0.6))
+ (line (name segm37:Line) (start 120 15) (end 120 0) (option notonpage1) (linewidth 0.6))
+ (line (name segm38:Line) (start 130 15) (end 130 0) (option notonpage1) (linewidth 0.6))
+ (line (name segm39:Line) (start 145 15) (end 145 0) (option notonpage1) (linewidth 0.6))
+ (line (name segm40:Line) (start 168 15) (end 168 0) (option notonpage1) (linewidth 0.6))
+ (line (name segm41:Line) (start 178 15) (end 178 0) (option notonpage1) (linewidth 0.6))
+)
diff --git a/template/kicad.pro b/template/kicad.pro
new file mode 100644
index 0000000..e2acca8
--- /dev/null
+++ b/template/kicad.pro
@@ -0,0 +1,65 @@
+update=Mon 31 Jul 2017 10:04:56 AM MDT
+version=1
+last_client=kicad
+[general]
+version=1
+RootSch=
+BoardNm=
+[pcbnew]
+version=1
+LastNetListRead=
+UseCmpFile=1
+PadDrill=0.600000000000
+PadDrillOvalY=0.600000000000
+PadSizeH=1.500000000000
+PadSizeV=1.500000000000
+PcbTextSizeV=1.500000000000
+PcbTextSizeH=1.500000000000
+PcbTextThickness=0.300000000000
+ModuleTextSizeV=1.000000000000
+ModuleTextSizeH=1.000000000000
+ModuleTextSizeThickness=0.150000000000
+SolderMaskClearance=0.000000000000
+SolderMaskMinWidth=0.000000000000
+DrawSegmentWidth=0.200000000000
+BoardOutlineThickness=0.100000000000
+ModuleOutlineThickness=0.150000000000
+[cvpcb]
+version=1
+NetIExt=net
+[eeschema]
+version=1
+LibDir=
+[eeschema/libraries]
+LibName1=power
+LibName2=device
+LibName3=switches
+LibName4=relays
+LibName5=motors
+LibName6=transistors
+LibName7=conn
+LibName8=linear
+LibName9=regul
+LibName10=74xx
+LibName11=cmos4000
+LibName12=adc-dac
+LibName13=memory
+LibName14=xilinx
+LibName15=microcontrollers
+LibName16=dsp
+LibName17=microchip
+LibName18=analog_switches
+LibName19=motorola
+LibName20=texas
+LibName21=intel
+LibName22=audio
+LibName23=interface
+LibName24=digital-audio
+LibName25=philips
+LibName26=display
+LibName27=cypress
+LibName28=siliconi
+LibName29=opto
+LibName30=atmel
+LibName31=contrib
+LibName32=valves
diff --git a/template/pagelayout_default.kicad_wks b/template/pagelayout_default.kicad_wks
new file mode 100644
index 0000000..60c1973
--- /dev/null
+++ b/template/pagelayout_default.kicad_wks
@@ -0,0 +1,34 @@
+( page_layout
+ ( setup (textsize 1.5 1.5) (linewidth 0.15) (textlinewidth 0.15) )
+ ( rect (comment "rect around the title block") (linewidth 0.15) (start 110 34) (end 2 2) )
+ ( rect (start 0 0 ltcorner) (end 0 0 rbcorner) (repeat 2) (incrx 2) (incry 2) )
+ ( line (start 50 2 ltcorner) (end 50 0 ltcorner) (repeat 30) (incrx 50) )
+ ( tbtext "1" (pos 25 1 ltcorner) (font (size 1.3 1.3))(repeat 100) (incrx 50) )
+ ( line (start 50 2 lbcorner) (end 50 0 lbcorner) (repeat 30) (incrx 50) )
+ ( tbtext "1" (pos 25 1 lbcorner) (font (size 1.3 1.3)) (repeat 100) (incrx 50) )
+ ( line (start 0 50 ltcorner) (end 2 50 ltcorner) (repeat 30) (incry 50) )
+ ( tbtext "A" (pos 1 25 ltcorner) (font (size 1.3 1.3))
+ (justify center)(repeat 100) (incry 50) )
+ ( line (start 0 50 rtcorner) (end 2 50 rtcorner) (repeat 30) (incry 50) )
+ ( tbtext "A" (pos 1 25 rtcorner) (font (size 1.3 1.3))
+ (justify center) (repeat 100) (incry 50) )
+ ( tbtext "Date: %D" (pos 87 6.9) )
+ ( line (start 110 5.5) end 2 5.5) )
+ ( tbtext "%K" (pos 109 4.1) (comment "Kicad version" ) )
+ ( line (start 110 8.5) end 2 8.5) )
+ ( tbtext "Rev: %R" (pos 24 6.9)(font bold)(justify left) )
+ ( tbtext "Size: %Z" (comment "Paper format name")(pos 109 6.9) )
+ ( tbtext "Id: %S/%N" (comment "Sheet id")(pos 24 4.1) )
+ ( line (start 110 12.5) end 2 12.5) )
+ ( tbtext "Title: %T" (pos 109 10.7)(font bold (size 2 2)) )
+ ( tbtext "File: %F" (pos 109 14.3) )
+ ( line (start 110 18.5) end 2 18.5) )
+ ( tbtext "Sheet: %P" (pos 109 17) )
+ ( tbtext "%Y" (comment "Company name") (pos 109 20)(font bold) )
+ ( tbtext "%C0" (comment "Comment 0") (pos 109 23) )
+ ( tbtext "%C1" (comment "Comment 1") (pos 109 26) )
+ ( tbtext "%C2" (comment "Comment 2") (pos 109 29) )
+ ( tbtext "%C3" (comment "Comment 3") (pos 109 32) )
+ ( line (start 90 8.5) end 90 5.5) )
+ ( line (start 26 8.5) end 26 2) )
+)
diff --git a/template/pagelayout_logo.kicad_wks b/template/pagelayout_logo.kicad_wks
new file mode 100644
index 0000000..dd71096
--- /dev/null
+++ b/template/pagelayout_logo.kicad_wks
@@ -0,0 +1,191 @@
+( page_layout
+ ( setup (textsize 1.5 1.5) (linewidth 0.15) (textlinewidth 0.15) )
+ ( rect (comment "rect around the title block") (linewidth 0.15) (start 110 34) (end 2 2) )
+ ( rect (start 0 0 ltcorner) (end 0 0 rbcorner) (repeat 2) (incrx 2) (incry 2) )
+ ( line (start 50 2 ltcorner) (end 50 0 ltcorner) (repeat 30) (incrx 50) )
+ ( tbtext "1" (pos 25 1 ltcorner) (font (size 1.3 1.3))(repeat 100) (incrx 50) )
+ ( line (start 50 2 lbcorner) (end 50 0 lbcorner) (repeat 30) (incrx 50) )
+ ( tbtext "1" (pos 25 1 lbcorner) (font (size 1.3 1.3)) (repeat 100) (incrx 50) )
+ ( line (start 0 50 ltcorner) (end 2 50 ltcorner) (repeat 30) (incry 50) )
+ ( tbtext "A" (pos 1 25 ltcorner) (font (size 1.3 1.3))
+ (justify center)(repeat 100) (incry 50) )
+ ( line (start 0 50 rtcorner) (end 2 50 rtcorner) (repeat 30) (incry 50) )
+ ( tbtext "A" (pos 1 25 rtcorner) (font (size 1.3 1.3))
+ (justify center) (repeat 100) (incry 50) )
+ ( tbtext "Date: %D" (pos 87 6.9) )
+ ( line (start 110 5.5) end 2 5.5) )
+ ( tbtext "%K" (pos 109 4.1) (comment "Kicad version" ) )
+ ( line (start 110 8.5) end 2 8.5) )
+ ( tbtext "Rev: %R" (pos 24 6.9)(font bold)(justify left) )
+ ( tbtext "Size: %Z" (comment "Paper format name")(pos 109 6.9) )
+ ( tbtext "Id: %S/%N" (comment "Sheet id")(pos 24 4.1) )
+ ( line (start 110 12.5) end 2 12.5) )
+ ( tbtext "Title: %T" (pos 109 10.7)(font bold (size 2 2)) )
+ ( tbtext "File: %F" (pos 109 14.3) )
+ ( line (start 110 18.5) end 2 18.5) )
+ ( tbtext "Sheet: %P" (pos 109 17) )
+ ( tbtext "%Y" (comment "Company name") (pos 109 20)(font bold) )
+ ( tbtext "%C0" (comment "Comment 0") (pos 109 23) )
+ ( tbtext "%C1" (comment "Comment 1") (pos 109 26) )
+ ( tbtext "%C2" (comment "Comment 2") (pos 109 29) )
+ ( tbtext "%C3" (comment "Comment 3") (pos 109 32) )
+ ( line (start 90 8.5) (end 90 5.5) )
+ ( line (start 26 8.5) (end 26 2) )
+
+ ( rect (comment "rect around the logo") (linewidth 0.15) (start 157 34) (end 110 2) )
+ (polygon (pos 134 18 rbcorner) (rotate 20) (linewidth 0.00254)
+ (pts (xy 20.574 8.382) (xy 19.9009 8.382) (xy 19.9009 6.26364) (xy 19.7485 5.98932)
+ (xy 19.71802 5.92328) (xy 19.69262 5.83946) (xy 19.66976 5.72262) (xy 19.65198 5.56006)
+ (xy 19.63674 5.33908) (xy 19.6215 5.04952) (xy 19.61134 4.67614) (xy 19.60372 4.20624)
+ (xy 19.5961 3.62712) (xy 19.58848 2.92862) (xy 19.5834 2.09296) (xy 19.57832 1.11252)
+ (xy 19.57578 0.27432) (xy 19.55038 -5.16636) (xy 18.15592 -5.16636) (xy 17.69364 -5.16382)
+ (xy 17.29232 -5.15874) (xy 16.98752 -5.15112) (xy 16.80464 -5.14096) (xy 16.764 -5.13334)
+ (xy 16.79194 -5.0419) (xy 16.8656 -4.85648) (xy 16.891 -4.79806) (xy 16.93672 -4.64312)
+ (xy 16.97228 -4.42468) (xy 16.99514 -4.11734) (xy 17.01038 -3.69824) (xy 17.01546 -3.14198)
+ (xy 17.018 -2.82702) (xy 17.01546 -2.25806) (xy 17.01292 -1.83896) (xy 17.0053 -1.5494)
+ (xy 16.99006 -1.36652) (xy 16.9672 -1.27508) (xy 16.93164 -1.25222) (xy 16.88592 -1.28016)
+ (xy 16.8656 -1.29794) (xy 16.47444 -1.5494) (xy 15.96644 -1.71704) (xy 15.3797 -1.7907)
+ (xy 15.2527 -1.79324) (xy 14.53134 -1.71958) (xy 13.89634 -1.50622) (xy 13.35278 -1.14808)
+ (xy 12.9032 -0.65024) (xy 12.5476 -0.0127) (xy 12.28598 0.762) (xy 12.22756 1.016)
+ (xy 12.1539 1.50368) (xy 12.11326 2.07264) (xy 12.10564 2.667) (xy 12.13104 3.23342)
+ (xy 12.18692 3.71348) (xy 12.2301 3.91668) (xy 12.49934 4.66344) (xy 12.88288 5.2959)
+ (xy 13.36802 5.8039) (xy 13.94968 6.17982) (xy 14.39164 6.35508) (xy 14.76248 6.41858)
+ (xy 15.21968 6.4262) (xy 15.69466 6.38302) (xy 16.11376 6.29158) (xy 16.20266 6.2611)
+ (xy 16.49984 6.1341) (xy 16.76146 5.99694) (xy 16.87576 5.92074) (xy 17.06118 5.7912)
+ (xy 17.15262 5.78104) (xy 17.1831 5.9055) (xy 17.18564 6.00964) (xy 17.18564 6.26364)
+ (xy 18.542 6.26364) (xy 19.9009 6.26364) (xy 19.9009 8.382) (xy 11.51636 8.382)
+ (xy 11.51636 6.26364) (xy 11.3665 6.03504) (xy 11.32586 5.9563) (xy 11.2903 5.84962)
+ (xy 11.26236 5.69468) (xy 11.24204 5.4737) (xy 11.22172 5.17144) (xy 11.20648 4.76504)
+ (xy 11.19124 4.2418) (xy 11.17854 3.57886) (xy 11.16838 3.00736) (xy 11.1506 2.16408)
+ (xy 11.13282 1.47066) (xy 11.10996 0.90932) (xy 11.07694 0.45974) (xy 11.03122 0.10414)
+ (xy 10.97026 -0.1778) (xy 10.89152 -0.40386) (xy 10.78738 -0.59436) (xy 10.6553 -0.76962)
+ (xy 10.49274 -0.94234) (xy 10.40892 -1.0287) (xy 9.93394 -1.38684) (xy 9.34212 -1.63576)
+ (xy 8.64616 -1.77546) (xy 7.85622 -1.80086) (xy 6.99262 -1.7145) (xy 6.31952 -1.57988)
+ (xy 5.92836 -1.48844) (xy 5.58038 -1.41478) (xy 5.31622 -1.36906) (xy 5.19938 -1.35636)
+ (xy 5.0419 -1.32588) (xy 4.99364 -1.27254) (xy 5.02666 -1.1557) (xy 5.10794 -0.92964)
+ (xy 5.22224 -0.635) (xy 5.35432 -0.31496) (xy 5.48132 -0.01524) (xy 5.588 0.22098)
+ (xy 5.65658 0.35052) (xy 5.6642 0.36068) (xy 5.76834 0.36322) (xy 5.98424 0.31496)
+ (xy 6.26872 0.22098) (xy 6.3119 0.2032) (xy 6.83768 0.04572) (xy 7.3406 -0.03048)
+ (xy 7.79018 -0.0254) (xy 8.14324 0.0635) (xy 8.2677 0.13462) (xy 8.49376 0.40132)
+ (xy 8.60044 0.68326) (xy 8.69188 1.05664) (xy 7.66826 1.10744) (xy 6.97738 1.16078)
+ (xy 6.41096 1.24968) (xy 5.92836 1.3843) (xy 5.49148 1.5748) (xy 5.24764 1.70942)
+ (xy 4.77012 2.08534) (xy 4.4196 2.54762) (xy 4.19608 3.0734) (xy 4.09448 3.63474)
+ (xy 4.11988 4.20878) (xy 4.26974 4.76758) (xy 4.54406 5.28828) (xy 4.9403 5.74294)
+ (xy 5.31876 6.02742) (xy 5.85978 6.2738) (xy 6.47954 6.40588) (xy 7.12724 6.42112)
+ (xy 7.74954 6.30936) (xy 7.75208 6.30936) (xy 8.06958 6.18744) (xy 8.37438 6.02742)
+ (xy 8.46582 5.96646) (xy 8.763 5.74802) (xy 8.7884 6.00456) (xy 8.8138 6.26364)
+ (xy 10.16508 6.26364) (xy 11.51636 6.26364) (xy 11.51636 8.382) (xy 4.13766 8.382)
+ (xy 4.13766 5.44068) (xy 4.10972 5.36702) (xy 4.0132 5.1816) (xy 3.86334 4.91744)
+ (xy 3.68046 4.6101) (xy 3.48742 4.29006) (xy 3.29946 3.99034) (xy 3.1369 3.74142)
+ (xy 3.10896 3.70078) (xy 2.9464 3.46456) (xy 2.65684 3.7211) (xy 2.20726 4.05892)
+ (xy 1.76276 4.26212) (xy 1.27254 4.34848) (xy 1.05664 4.3561) (xy 0.70104 4.34594)
+ (xy 0.43942 4.29768) (xy 0.19558 4.19862) (xy 0.05842 4.11988) (xy -0.38862 3.76682)
+ (xy -0.75946 3.28168) (xy -1.03378 2.7051) (xy -1.07188 2.58572) (xy -1.143 2.30378)
+ (xy -1.19126 1.98374) (xy -1.21666 1.58496) (xy -1.22936 1.0668) (xy -1.22936 0.889)
+ (xy -1.22428 0.381) (xy -1.21158 0) (xy -1.17856 -0.30226) (xy -1.12776 -0.56896)
+ (xy -1.04902 -0.84328) (xy -1.02108 -0.92964) (xy -0.75438 -1.53416) (xy -0.3937 -2.01676)
+ (xy 0.04318 -2.36474) (xy 0.54864 -2.57048) (xy 0.99822 -2.62636) (xy 1.54686 -2.55524)
+ (xy 2.05486 -2.35712) (xy 2.35712 -2.15138) (xy 2.5527 -1.98882) (xy 2.68986 -1.88468)
+ (xy 2.7305 -1.86436) (xy 2.794 -1.9304) (xy 2.92608 -2.10312) (xy 3.10388 -2.35458)
+ (xy 3.30962 -2.65176) (xy 3.51536 -2.96164) (xy 3.70586 -3.25374) (xy 3.85318 -3.4925)
+ (xy 3.94208 -3.64998) (xy 3.95732 -3.69316) (xy 3.86588 -3.74396) (xy 3.66268 -3.85064)
+ (xy 3.3782 -3.99288) (xy 3.22326 -4.06654) (xy 2.54508 -4.36118) (xy 1.92532 -4.5466)
+ (xy 1.31318 -4.64058) (xy 0.87122 -4.65836) (xy 0.02286 -4.59232) (xy -0.7493 -4.38912)
+ (xy -1.46558 -4.0386) (xy -2.15392 -3.5306) (xy -2.38252 -3.32232) (xy -2.9464 -2.70764)
+ (xy -3.37312 -2.04978) (xy -3.6957 -1.30302) (xy -3.82524 -0.87376) (xy -3.94208 -0.26924)
+ (xy -4.00304 0.43434) (xy -4.00812 1.17348) (xy -3.95986 1.88976) (xy -3.85572 2.52222)
+ (xy -3.82524 2.64922) (xy -3.51028 3.53314) (xy -3.07848 4.32562) (xy -2.53746 5.0165)
+ (xy -1.905 5.588) (xy -1.19126 6.02488) (xy -0.44958 6.30682) (xy 0.01016 6.39064)
+ (xy 0.57404 6.4262) (xy 1.17856 6.41604) (xy 1.76276 6.3627) (xy 2.26822 6.26364)
+ (xy 2.286 6.25856) (xy 2.72288 6.11124) (xy 3.23088 5.90296) (xy 3.73888 5.66166)
+ (xy 4.13766 5.44068) (xy 4.13766 8.382) (xy 0 8.382) (xy -4.74218 8.382)
+ (xy -4.74218 1.7653) (xy -4.74218 0.71882) (xy -4.74218 -0.55118) (xy -4.74218 -1.66116)
+ (xy -4.74218 -2.62382) (xy -4.74472 -3.44678) (xy -4.7498 -4.14528) (xy -4.75742 -4.7244)
+ (xy -4.77012 -5.19938) (xy -4.79044 -5.57784) (xy -4.81584 -5.87248) (xy -4.84886 -6.09346)
+ (xy -4.8895 -6.2484) (xy -4.94284 -6.35254) (xy -5.0038 -6.4135) (xy -5.07746 -6.44144)
+ (xy -5.16382 -6.44906) (xy -5.26542 -6.44398) (xy -5.37972 -6.4389) (xy -5.40512 -6.4389)
+ (xy -5.6515 -6.4516) (xy -5.79374 -6.51002) (xy -5.90042 -6.65734) (xy -5.969 -6.79958)
+ (xy -6.23062 -7.1755) (xy -6.57606 -7.4295) (xy -6.97738 -7.56158) (xy -7.40156 -7.57682)
+ (xy -7.81558 -7.47014) (xy -8.1915 -7.24662) (xy -8.49376 -6.90626) (xy -8.58774 -6.73354)
+ (xy -8.72998 -6.43636) (xy -14.05382 -6.43636) (xy -15.14602 -6.43636) (xy -16.08074 -6.43382)
+ (xy -16.87068 -6.43128) (xy -17.52854 -6.42874) (xy -18.06702 -6.42112) (xy -18.49628 -6.4135)
+ (xy -18.83156 -6.40334) (xy -19.08302 -6.39064) (xy -19.2659 -6.3754) (xy -19.38782 -6.35508)
+ (xy -19.46402 -6.33222) (xy -19.50974 -6.30428) (xy -19.50974 -6.30174) (xy -19.53514 -6.26618)
+ (xy -19.55546 -6.1976) (xy -19.57324 -6.08838) (xy -19.59102 -5.92582) (xy -19.60372 -5.69976)
+ (xy -19.61388 -5.40004) (xy -19.6215 -5.0165) (xy -19.62912 -4.53644) (xy -19.6342 -3.95224)
+ (xy -19.63928 -3.24866) (xy -19.64182 -2.41808) (xy -19.64182 -1.4478) (xy -19.64436 -0.32766)
+ (xy -19.64436 0.71882) (xy -19.64436 1.9685) (xy -19.64182 3.06324) (xy -19.64182 4.00812)
+ (xy -19.63928 4.81584) (xy -19.6342 5.49656) (xy -19.62912 6.06552) (xy -19.6215 6.52526)
+ (xy -19.61134 6.8961) (xy -19.60118 7.18058) (xy -19.58848 7.39394) (xy -19.5707 7.54634)
+ (xy -19.55292 7.64794) (xy -19.53006 7.71144) (xy -19.50974 7.73938) (xy -19.47418 7.76224)
+ (xy -19.4056 7.7851) (xy -19.29892 7.80288) (xy -19.1389 7.81812) (xy -18.91538 7.83082)
+ (xy -18.62074 7.84098) (xy -18.24228 7.85114) (xy -17.76984 7.85622) (xy -17.1958 7.86384)
+ (xy -16.50238 7.86638) (xy -15.68704 7.86892) (xy -14.732 7.87146) (xy -13.63218 7.87146)
+ (xy -12.37488 7.87146) (xy -12.192 7.874) (xy -10.91438 7.87146) (xy -9.79424 7.87146)
+ (xy -8.82142 7.86892) (xy -7.98576 7.86638) (xy -7.2771 7.86384) (xy -6.68782 7.85876)
+ (xy -6.20014 7.85114) (xy -5.81152 7.84352) (xy -5.50672 7.83336) (xy -5.27304 7.81812)
+ (xy -5.1054 7.80542) (xy -4.9911 7.78764) (xy -4.91998 7.76732) (xy -4.87934 7.74192)
+ (xy -4.8768 7.73938) (xy -4.8514 7.70128) (xy -4.83108 7.6327) (xy -4.8133 7.52348)
+ (xy -4.79552 7.36092) (xy -4.78282 7.1374) (xy -4.77266 6.83768) (xy -4.76504 6.45414)
+ (xy -4.75742 5.97408) (xy -4.75234 5.38734) (xy -4.74726 4.6863) (xy -4.74472 3.85318)
+ (xy -4.74472 2.88544) (xy -4.74218 1.7653) (xy -4.74218 8.382) (xy -20.574 8.382)
+ (xy -20.574 0) (xy -20.574 -8.382) (xy 0 -8.382) (xy 20.574 -8.382)
+ (xy 20.574 0) (xy 20.574 8.382) (xy 20.574 8.382))
+ (pts (xy -9.4742 6.26364) (xy -11.25728 6.26364) (xy -13.04036 6.26364) (xy -13.04036 6.0071)
+ (xy -13.07846 5.76072) (xy -13.17752 5.48386) (xy -13.20038 5.4356) (xy -13.28674 5.29336)
+ (xy -13.45692 5.0419) (xy -13.68806 4.70662) (xy -13.96746 4.31292) (xy -14.27226 3.88112)
+ (xy -14.58976 3.4417) (xy -14.8971 3.02006) (xy -15.1765 2.63652) (xy -15.41526 2.31902)
+ (xy -15.58798 2.09296) (xy -15.67942 1.98882) (xy -15.7099 2.03454) (xy -15.73022 2.24282)
+ (xy -15.74038 2.61874) (xy -15.74292 3.15976) (xy -15.7353 3.76682) (xy -15.72514 4.37896)
+ (xy -15.71244 4.84886) (xy -15.69974 5.19684) (xy -15.67688 5.45084) (xy -15.64894 5.63372)
+ (xy -15.6083 5.76834) (xy -15.55496 5.88518) (xy -15.5194 5.94614) (xy -15.33144 6.26364)
+ (xy -16.96974 6.26364) (xy -18.61058 6.26364) (xy -18.45056 5.94868) (xy -18.415 5.87502)
+ (xy -18.38706 5.78612) (xy -18.36166 5.67182) (xy -18.34134 5.51688) (xy -18.3261 5.30606)
+ (xy -18.3134 5.02666) (xy -18.30324 4.66598) (xy -18.29816 4.20624) (xy -18.29308 3.63728)
+ (xy -18.29054 2.94386) (xy -18.29054 2.11328) (xy -18.29054 1.12776) (xy -18.288 0.889)
+ (xy -18.29054 -0.13208) (xy -18.29054 -0.99568) (xy -18.29308 -1.71958) (xy -18.29562 -2.31394)
+ (xy -18.30324 -2.79654) (xy -18.31086 -3.18008) (xy -18.32356 -3.47472) (xy -18.3388 -3.70078)
+ (xy -18.35658 -3.86588) (xy -18.37944 -3.9878) (xy -18.40738 -4.08178) (xy -18.4404 -4.15798)
+ (xy -18.45056 -4.17322) (xy -18.61058 -4.48818) (xy -17.01038 -4.48818) (xy -16.51 -4.48564)
+ (xy -16.07566 -4.48056) (xy -15.73022 -4.4704) (xy -15.49908 -4.4577) (xy -15.41018 -4.44246)
+ (xy -15.44574 -4.3434) (xy -15.53464 -4.16052) (xy -15.55496 -4.12496) (xy -15.61084 -3.9878)
+ (xy -15.65402 -3.7973) (xy -15.6845 -3.52806) (xy -15.70736 -3.15468) (xy -15.72514 -2.64922)
+ (xy -15.73276 -2.37236) (xy -15.75054 -1.70434) (xy -15.75816 -1.20396) (xy -15.748 -0.86106)
+ (xy -15.70228 -0.6731) (xy -15.61084 -0.63754) (xy -15.46098 -0.74422) (xy -15.24 -0.99314)
+ (xy -14.93266 -1.37668) (xy -14.5288 -1.8923) (xy -14.51102 -1.91516) (xy -14.04112 -2.51714)
+ (xy -13.67028 -3.00482) (xy -13.39088 -3.39344) (xy -13.18768 -3.70078) (xy -13.0556 -3.94208)
+ (xy -12.9794 -4.13004) (xy -12.95654 -4.28244) (xy -12.954 -4.29768) (xy -12.954 -4.48818)
+ (xy -11.23696 -4.48818) (xy -9.51992 -4.48818) (xy -10.07364 -3.91668) (xy -10.25398 -3.7211)
+ (xy -10.50544 -3.43154) (xy -10.81278 -3.06832) (xy -11.1633 -2.64922) (xy -11.54176 -2.19202)
+ (xy -11.93038 -1.71704) (xy -12.319 -1.23952) (xy -12.68476 -0.78232) (xy -13.02004 -0.36068)
+ (xy -13.30452 0.00254) (xy -13.5255 0.2921) (xy -13.66774 0.49022) (xy -13.716 0.57404)
+ (xy -13.66774 0.66294) (xy -13.53058 0.86868) (xy -13.31976 1.17348) (xy -13.04798 1.55702)
+ (xy -12.7254 2.00406) (xy -12.3698 2.49428) (xy -11.99388 3.00736) (xy -11.61034 3.5306)
+ (xy -11.23442 4.0386) (xy -10.87628 4.51866) (xy -10.55116 4.95046) (xy -10.27176 5.31622)
+ (xy -10.05332 5.59562) (xy -9.91108 5.76834) (xy -9.4742 6.26364) (xy -9.4742 6.26364))
+ (pts (xy -5.75818 6.23316) (xy -5.83946 6.24332) (xy -6.06044 6.25094) (xy -6.39826 6.25856)
+ (xy -6.82498 6.26364) (xy -7.239 6.26364) (xy -7.71906 6.2611) (xy -8.13562 6.25856)
+ (xy -8.4582 6.25094) (xy -8.6614 6.24078) (xy -8.72236 6.23316) (xy -8.69188 6.14172)
+ (xy -8.61822 5.95376) (xy -8.59536 5.89534) (xy -8.5598 5.72262) (xy -8.52932 5.40512)
+ (xy -8.50392 4.9657) (xy -8.4836 4.42468) (xy -8.46836 3.81) (xy -8.4582 3.14198)
+ (xy -8.45566 2.44602) (xy -8.45566 1.74498) (xy -8.46074 1.06172) (xy -8.47344 0.42164)
+ (xy -8.49122 -0.15494) (xy -8.51408 -0.64262) (xy -8.54202 -1.01854) (xy -8.57504 -1.25984)
+ (xy -8.59028 -1.31572) (xy -8.7122 -1.61036) (xy -7.36346 -1.61036) (xy -6.01218 -1.61036)
+ (xy -6.01218 1.99136) (xy -6.01218 2.8956) (xy -6.00964 3.6449) (xy -6.00456 4.2545)
+ (xy -5.9944 4.74472) (xy -5.98424 5.12572) (xy -5.96646 5.41274) (xy -5.94614 5.6261)
+ (xy -5.92074 5.77596) (xy -5.89026 5.88264) (xy -5.88518 5.89534) (xy -5.8039 6.09854)
+ (xy -5.76072 6.22046) (xy -5.75818 6.23316) (xy -5.75818 6.23316))
+ (pts (xy 8.636 3.4544) (xy 8.62838 3.80238) (xy 8.60298 4.0259) (xy 8.54964 4.17322)
+ (xy 8.45312 4.28752) (xy 8.4455 4.29514) (xy 8.08736 4.52628) (xy 7.66826 4.64058)
+ (xy 7.24408 4.62788) (xy 7.05866 4.572) (xy 6.77672 4.37388) (xy 6.5913 4.07924)
+ (xy 6.5405 3.74142) (xy 6.54558 3.70078) (xy 6.60146 3.45186) (xy 6.68782 3.25882)
+ (xy 6.70052 3.24104) (xy 6.96468 3.03784) (xy 7.35076 2.88798) (xy 7.81812 2.8067)
+ (xy 8.0645 2.794) (xy 8.636 2.794) (xy 8.636 3.4544) (xy 8.636 3.4544))
+ (pts (xy 17.018 4.04876) (xy 16.7259 4.2291) (xy 16.34744 4.3942) (xy 15.94358 4.46024)
+ (xy 15.56766 4.4196) (xy 15.35938 4.3307) (xy 15.13332 4.15544) (xy 14.9733 3.93192)
+ (xy 14.86408 3.6322) (xy 14.80058 3.23342) (xy 14.77518 2.70764) (xy 14.77264 2.45364)
+ (xy 14.79296 1.77546) (xy 14.859 1.23952) (xy 14.97076 0.82296) (xy 15.1384 0.508)
+ (xy 15.33652 0.29718) (xy 15.66418 0.127) (xy 16.06042 0.08382) (xy 16.48714 0.17018)
+ (xy 16.68018 0.254) (xy 17.018 0.42672) (xy 17.018 2.23774) (xy 17.018 4.04876)
+ (xy 17.018 4.04876)) )
+)
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
new file mode 100644
index 0000000..ade13d4
--- /dev/null
+++ b/tools/CMakeLists.txt
@@ -0,0 +1,80 @@
+
+set( MAKE_LINK_MAPS true )
+
+if( 0 )
+
+ project(kicad-tools)
+
+ cmake_minimum_required( VERSION 2.8 FATAL_ERROR )
+
+ set( PROJECT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../ )
+
+ # message( "PROJECT_SOURCE_DIR=${PROJECT_SOURCE_DIR}" )
+
+ # Path to local CMake modules.
+ set( CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/CMakeModules )
+
+ include( CheckFindPackageResult )
+
+ ##########################
+ # Find wxWidgets library #
+ ##########################
+ # Here you can define what libraries of wxWidgets you need for your
+ # application. You can figure out what libraries you need here;
+ # http://www.wxwidgets.org/manuals/2.8/wx_librarieslist.html
+
+ # On Apple only wxwidgets 2.9 or higher doesn't need to find aui part of base
+ if(APPLE)
+ find_package(wxWidgets COMPONENTS gl adv html core net base xml QUIET)
+ else(APPLE)
+ find_package(wxWidgets COMPONENTS gl aui adv html core net base xml QUIET)
+ endif(APPLE)
+ check_find_package_result(wxWidgets_FOUND "wxWidgets")
+
+
+ # Include wxWidgets macros.
+ include(${wxWidgets_USE_FILE})
+
+ # make config.h
+ include( PerformFeatureChecks )
+ perform_feature_checks()
+
+
+endif()
+
+include_directories(
+ ${PROJECT_SOURCE_DIR}/include
+ ${PROJECT_SOURCE_DIR}/pcbnew
+ ${BOOST_INCLUDE}
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_BINARY_DIR}
+ )
+
+
+add_executable( container_test
+ EXCLUDE_FROM_ALL
+ container_test.cpp
+ )
+target_link_libraries( container_test
+ common
+ polygon
+ bitmaps
+ ${wxWidgets_LIBRARIES}
+ )
+
+add_executable( test-nm-biu-to-ascii-mm-round-tripping
+ EXCLUDE_FROM_ALL
+ test-nm-biu-to-ascii-mm-round-tripping.cpp
+ )
+
+add_executable( property_tree
+ EXCLUDE_FROM_ALL
+ property_tree.cpp
+ ../common/richio.cpp
+ ../common/dsnlexer.cpp
+ ../common/ptree.cpp
+ )
+target_link_libraries( property_tree
+ ${wxWidgets_LIBRARIES}
+ )
+
diff --git a/tools/Info.plist b/tools/Info.plist
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tools/Info.plist
diff --git a/tools/checkcoding.py b/tools/checkcoding.py
new file mode 100755
index 0000000..7b00151
--- /dev/null
+++ b/tools/checkcoding.py
@@ -0,0 +1,134 @@
+#!/usr/bin/env python
+# Created for KiCad project by Miguel
+# Some modifications by Edwin
+# GPL2
+
+import subprocess
+import os
+import difflib
+
+
+# class for checking and uncrustifying files
+# defaults to cpp,cxx,h,hpp and c files
+class coding_checker(object):
+ file_filter = ["cpp", "cxx", "h", "hpp", "c"]
+
+ # Function to call uncrustify, it returns the re-formated code and
+ # any errors
+ #
+
+ def uncrustify_file(self, filename=None):
+ try:
+ args = ("uncrustify", "-c", "uncrustify.cfg", "-f", filename)
+ popen = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ popen.wait()
+ return [popen.stdout.readlines(), popen.stderr.read()]
+ except OSError as e:
+ print "System returned : {e}\nCould not run uncrustify. Is it installed?".format(e=e.strerror)
+ return [None, None]
+
+ # This function runs bzr, and gets the list of modified files
+ def bzr_modified(self):
+ modified_files = []
+ args = ("bzr", "status")
+ try:
+ popen = subprocess.Popen(args, stdout=subprocess.PIPE)
+ popen.wait()
+ output = popen.stdout.readlines()
+ except OSError as e:
+ print "System returned : {e}\nCould not run bzr. Is it installed?".format(e=e.strerror)
+ return None
+
+ in_modifieds = False
+ for line in output:
+ line = line.rstrip("\r\n")
+ if line.endswith(":"):
+ in_modifieds = False
+ if line.startswith("modified:"):
+ in_modifieds = True
+ continue
+ if line.startswith("added:"):
+ in_modifieds = True
+ continue
+
+ if in_modifieds:
+ modified_files.append(line.lstrip("\t ").rstrip("\t "))
+
+ return modified_files
+
+
+ def extension(self, filename):
+ return os.path.splitext(filename)[1][1:].strip().lower()
+
+
+ def read_file(self, filename):
+ f = open(filename, 'r')
+ data = f.readlines()
+ f.close()
+ return data
+
+
+ def ask_user(self, filename):
+ msg = 'Shall I clean %s ?' % filename
+ return raw_input("%s (y/N/E) " % msg).lower()
+
+
+ def main(self):
+ # make list of modified file names
+ modified_files = self.bzr_modified()
+
+ if not modified_files:
+ print "No modified files\n"
+ else:
+
+ for filename in modified_files:
+ if self.extension(filename) in self.file_filter:
+ self.compare_and_suggest(filename)
+
+ def compare_and_suggest(self,filename):
+ # if it is a 'c' file try to uncrustify
+ [uncrustified, errors] = self.uncrustify_file(filename)
+
+ if not (uncrustified and errors):
+ print "Program end"
+ # problem in uncrustify
+ return
+
+ original = self.read_file(filename)
+
+ if len(errors.split("\n")) > 2:
+ print "There was a problem processing " + filename + ":" + errors
+ return
+
+ if uncrustified == original:
+ print filename + " looks perfect!, well done!"
+ else:
+ print "Suggestions for: " + filename
+
+ diff = difflib.unified_diff(original, uncrustified, filename, filename + ".uncrustified")
+
+ for line in diff:
+ print line.rstrip("\r\n")
+ print ""
+ reply = self.ask_user(filename)
+
+ if reply in ["y", "yes"]:
+ f = open(filename, 'w')
+ for line in uncrustified:
+ f.write(line)
+ f.close()
+ print filename + " UPDATED"
+
+ if reply in ["e", "ed", "edit"]:
+ os.system("$EDITOR " + filename)
+
+ print ""
+
+
+if __name__ == '__main__':
+ print "This program tries to do 2 things\n" \
+ "1) call bzr to find changed files (related to the KiCad project)\n" \
+ "2) call uncrustify on the changed files (to make the files comply with coding standards)\n"
+
+ cc = coding_checker()
+ cc.main()
diff --git a/tools/container_test.cpp b/tools/container_test.cpp
new file mode 100644
index 0000000..3b65dad
--- /dev/null
+++ b/tools/container_test.cpp
@@ -0,0 +1,132 @@
+
+#include <base_struct.h>
+#include <boost/ptr_container/ptr_vector.hpp>
+#include <deque>
+#include <dlist.h>
+#include <time.h>
+#include <common.h>
+
+#define TEST_NODES 100000000
+
+
+//typedef std::vector<EDA_ITEM*> EDA_ITEMV;
+//typedef std::deque<EDA_ITEM*> EDA_ITEMV;
+typedef boost::ptr_vector<EDA_ITEM> EDA_ITEMV;
+
+class MY_ITEM : public EDA_ITEM
+{
+public:
+
+ MY_ITEM( KICAD_T id ) :
+ EDA_ITEM( id )
+ {}
+
+
+#if defined(DEBUG)
+ void Show( int nestLevel, std::ostream& os ) const
+ {
+ ShowDummy( os );
+ }
+#endif
+};
+
+
+void heap_warm_up();
+
+int main( int argc, char** argv )
+{
+ EDA_ITEMV v;
+ DLIST<EDA_ITEM> dlist;
+
+ unsigned vAllocStart;
+ unsigned vAllocStop;
+ unsigned vIterateStart;
+ unsigned vIterateStop;
+
+ unsigned dAllocStart;
+ unsigned dAllocStop;
+ unsigned dIterateStart;
+ unsigned dIterateStop;
+
+ heap_warm_up();
+
+ vAllocStart = GetRunningMicroSecs();
+
+ for( int i=0; i<TEST_NODES; ++i )
+ {
+ v.push_back( new MY_ITEM( NOT_USED ) );
+ }
+
+ vAllocStop = GetRunningMicroSecs();
+ vIterateStart = vAllocStop;
+
+ for( EDA_ITEMV::const_iterator it = v.begin(); it != v.end(); ++it )
+ {
+ if( it->Type() == -22 )
+ {
+ printf( "never this\n" );
+ break;
+ }
+ }
+
+ vIterateStop = GetRunningMicroSecs();
+
+#if 0
+ for( int i=0; i<TEST_NODES; ++i )
+ {
+ delete v[i];
+ }
+#endif
+
+ v.clear();
+
+
+ dAllocStart = GetRunningMicroSecs();
+
+ for( int i=0; i<TEST_NODES; ++i )
+ {
+ dlist.PushBack( new MY_ITEM( NOT_USED ) );
+ }
+
+ dAllocStop = GetRunningMicroSecs();
+ dIterateStart = dAllocStop;
+
+ for( const EDA_ITEM* it = dlist; it; it = it->Next() )
+ {
+ if( it->Type() == -22 )
+ {
+ printf( "never this\n" );
+ break;
+ }
+ }
+
+ dIterateStop = GetRunningMicroSecs();
+
+ printf( "vector alloc: %u usecs iterate: %u usecs\n",
+ vAllocStop - vAllocStart,
+ vIterateStop - vIterateStart );
+
+ printf( "dlist alloc: %u usecs iterate: %u usecs\n",
+ dAllocStop - dAllocStart,
+ dIterateStop - dIterateStart );
+}
+
+
+void heap_warm_up()
+{
+ // dry run allocate enough object for process to obtain all memory needed
+
+ EDA_ITEMV vec;
+
+ for( int i=0; i<TEST_NODES; ++i )
+ {
+ vec.push_back( new MY_ITEM( NOT_USED ) );
+ }
+
+ for( int i=0; i<TEST_NODES; ++i )
+ {
+ // delete vec[i];
+ }
+
+ vec.clear();
+}
diff --git a/tools/property_tree.cpp b/tools/property_tree.cpp
new file mode 100644
index 0000000..0386d21
--- /dev/null
+++ b/tools/property_tree.cpp
@@ -0,0 +1,91 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
+ * Copyright (C) 2012 KiCad Developers, see CHANGELOG.TXT for contributors.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+
+// This is a propertytree test utility.
+// It can convert XML to sexpressions or beautify s-expressions in non-specctra mode.
+
+
+#include <assert.h>
+#include <ptree.h>
+#include <richio.h>
+#include <dsnlexer.h>
+#include <macros.h>
+#include <boost/property_tree/xml_parser.hpp>
+
+
+void usage()
+{
+ fprintf( stderr, "Usage: parser_gen <grammar_s-expression_file>\n" );
+ exit( 1 );
+}
+
+
+int main( int argc, char** argv )
+{
+ if( argc != 2 )
+ {
+ usage();
+ }
+
+ FILE* fp = fopen( argv[1], "r" );
+ if( !fp )
+ {
+ fprintf( stderr, "Unable to open '%s'\n", argv[1] );
+ usage();
+ }
+
+ static const KEYWORD empty_keywords[1] = {};
+
+ DSNLEXER lexer( empty_keywords, 0, fp, FROM_UTF8( argv[1] ) );
+
+ try
+ {
+ PTREE doc;
+
+#if 0
+ using namespace boost::property_tree;
+
+ read_xml( argv[1], doc, xml_parser::trim_whitespace | xml_parser::no_comments );
+#else
+ Scan( &doc, &lexer );
+#endif
+
+#if 1
+ STRING_FORMATTER sf;
+ Format( &sf, 0, 0, doc );
+ printf( "%s", sf.GetString().c_str() );
+#else
+ // writing the unchanged ptree in file2.xml
+ boost::property_tree::xml_writer_settings<char> settings( ' ', 2 );
+ write_xml( "/tmp/output.xml", doc, std::locale(), settings );
+#endif
+
+ }
+ catch( const IO_ERROR& ioe )
+ {
+ fprintf( stderr, "%s\n", TO_UTF8( ioe.errorText ) );
+ }
+}
+
diff --git a/tools/test-nm-biu-to-ascii-mm-round-tripping.cpp b/tools/test-nm-biu-to-ascii-mm-round-tripping.cpp
new file mode 100644
index 0000000..0ddb383
--- /dev/null
+++ b/tools/test-nm-biu-to-ascii-mm-round-tripping.cpp
@@ -0,0 +1,120 @@
+
+/*
+ A test program to which explores the ability to round trip a nanometer
+ internal unit in the form of a 32 bit int, out to ASCII floating point
+ millimeters and back in without variation. It tests all 4 billion values
+ that an int can hold, and converts to ASCII and back and verifies integrity
+ of the round tripped value.
+
+ Author: Dick Hollenbeck
+*/
+
+
+#include <limits.h>
+#include <stdio.h>
+#include <cmath>
+#include <string>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+
+static inline int KiROUND( double v )
+{
+ return int( v < 0 ? v - 0.5 : v + 0.5 );
+}
+
+
+typedef int BIU;
+
+#define BIU_PER_MM 1e6
+
+
+//double scale = BIU_PER_MM;
+//double scale = UM_PER_BIU;
+double scale = 1.0/BIU_PER_MM;
+
+
+std::string biuFmt( BIU aValue )
+{
+ double engUnits = aValue * scale;
+ char temp[48];
+ int len;
+
+ if( engUnits != 0.0 && fabsl( engUnits ) <= 0.0001 )
+ {
+ len = snprintf( temp, sizeof( temp ), "%.10f", engUnits );
+
+ while( --len > 0 && temp[len] == '0' )
+ temp[len] = '\0';
+
+ ++len;
+ }
+ else
+ {
+ len = snprintf( temp, sizeof( temp ), "%.10g", engUnits );
+ }
+
+ return std::string( temp, len );;
+}
+
+
+int parseBIU( const char* s )
+{
+ double d = strtod( s, NULL );
+ return KiROUND( double( d * BIU_PER_MM ) );
+// return int( d * BIU_PER_MM );
+}
+
+
+int main( int argc, char** argv )
+{
+ unsigned mismatches = 0;
+
+ if( argc > 1 )
+ {
+ // take a value on the command line and round trip it back to ASCII.
+
+ int i = parseBIU( argv[1] );
+
+ printf( "%s: i:%d\n", __func__, i );
+
+ std::string s = biuFmt( i );
+
+ printf( "%s: s:%s\n", __func__, s.c_str() );
+
+ exit(0);
+ }
+
+ // printf( "sizeof(long double): %zd\n", sizeof( long double ) );
+
+ // Emperically prove that we can round trip all 4 billion 32 bit integers representative
+ // of nanometers out to textual floating point millimeters, and back without error using
+ // the above two functions.
+// for( int i = INT_MIN; int64_t( i ) <= int64_t( INT_MAX ); ++i )
+ for( int64_t j = INT_MIN; j <= int64_t( INT_MAX ); ++j )
+ {
+ int i = int( j );
+
+ std::string s = biuFmt( int( i ) );
+
+ int r = parseBIU( s.c_str() );
+
+ if( r != i )
+ {
+ printf( "i:%d biuFmt:%s r:%d\n", i, s.c_str(), r );
+ ++mismatches;
+ }
+
+ if( !( i & 0xFFFFFF ) )
+ {
+ printf( " %08x", i );
+ fflush( stdout );
+ }
+ }
+
+ printf( "mismatches:%u\n", mismatches );
+
+ return 0;
+}
+
diff --git a/uncrustify.cfg b/uncrustify.cfg
new file mode 100644
index 0000000..8d5d47b
--- /dev/null
+++ b/uncrustify.cfg
@@ -0,0 +1,1612 @@
+# Uncrustify 0.60
+
+#
+# General options
+#
+
+# The type of line endings
+newlines = auto # auto/lf/crlf/cr
+
+# The original size of tabs in the input
+input_tab_size = 4 # number
+
+# The size of tabs in the output (only used if align_with_tabs=true)
+output_tab_size = 4 # number
+
+# The ASCII value of the string escape char, usually 92 (\) or 94 (^). (Pawn)
+string_escape_char = 92 # number
+
+# Alternate string escape char for Pawn. Only works right before the quote char.
+string_escape_char2 = 0 # number
+
+# Allow interpreting '>=' and '>>=' as part of a template in 'void f(list<list<B>>=val);'.
+# If true (default), 'assert(x<0 && y>=3)' will be broken.
+# Improvements to template detection may make this option obsolete.
+tok_split_gte = false # false/true
+
+# Control what to do with the UTF-8 BOM (recommend 'remove')
+utf8_bom = ignore # ignore/add/remove/force
+
+# If the file contains bytes with values between 128 and 255, but is not UTF-8, then output as UTF-8
+utf8_byte = true # false/true
+
+# Force the output encoding to UTF-8
+utf8_force = true # false/true
+
+#
+# Indenting
+#
+
+# The number of columns to indent per level.
+# Usually 2, 3, 4, or 8.
+indent_columns = 4 # number
+
+# The continuation indent. If non-zero, this overrides the indent of '(' and '=' continuation indents.
+# For FreeBSD, this is set to 4. Negative value is absolute and not increased for each ( level
+indent_continue = 0 # number
+
+# How to use tabs when indenting code
+# 0=spaces only
+# 1=indent with tabs to brace level, align with spaces
+# 2=indent and align with tabs, using spaces when not on a tabstop
+indent_with_tabs = 0 # number
+
+# Comments that are not a brace level are indented with tabs on a tabstop.
+# Requires indent_with_tabs=2. If false, will use spaces.
+indent_cmt_with_tabs = false # false/true
+
+# Whether to indent strings broken by '\' so that they line up
+indent_align_string = true # false/true
+
+# The number of spaces to indent multi-line XML strings.
+# Requires indent_align_string=True
+indent_xml_string = 0 # number
+
+# Spaces to indent '{' from level
+indent_brace = 0 # number
+
+# Whether braces are indented to the body level
+indent_braces = false # false/true
+
+# Disabled indenting function braces if indent_braces is true
+indent_braces_no_func = false # false/true
+
+# Disabled indenting class braces if indent_braces is true
+indent_braces_no_class = false # false/true
+
+# Disabled indenting struct braces if indent_braces is true
+indent_braces_no_struct = false # false/true
+
+# Indent based on the size of the brace parent, i.e. 'if' => 3 spaces, 'for' => 4 spaces, etc.
+indent_brace_parent = false # false/true
+
+# Whether the 'namespace' body is indented
+indent_namespace = false # false/true
+
+# The number of spaces to indent a namespace block
+indent_namespace_level = 0 # number
+
+# If the body of the namespace is longer than this number, it won't be indented.
+# Requires indent_namespace=true. Default=0 (no limit)
+indent_namespace_limit = 0 # number
+
+# Whether the 'extern "C"' body is indented
+indent_extern = false # false/true
+
+# Whether the 'class' body is indented
+indent_class = true # false/true
+
+# Whether to indent the stuff after a leading class colon
+indent_class_colon = false # false/true
+
+# Virtual indent from the ':' for member initializers. Default is 2
+indent_ctor_init_leading = 2 # number
+
+# Additional indenting for constructor initializer list
+indent_ctor_init = 0 # number
+
+# False=treat 'else\nif' as 'else if' for indenting purposes
+# True=indent the 'if' one level
+indent_else_if = true # false/true
+
+# Amount to indent variable declarations after a open brace. neg=relative, pos=absolute
+indent_var_def_blk = 0 # number
+
+# Indent continued variable declarations instead of aligning.
+indent_var_def_cont = false # false/true
+
+# True: force indentation of function definition to start in column 1
+# False: use the default behavior
+indent_func_def_force_col1 = false # false/true
+
+# True: indent continued function call parameters one indent level
+# False: align parameters under the open paren
+indent_func_call_param = true # false/true
+
+# Same as indent_func_call_param, but for function defs
+indent_func_def_param = true # false/true
+
+# Same as indent_func_call_param, but for function protos
+indent_func_proto_param = true # false/true
+
+# Same as indent_func_call_param, but for class declarations
+indent_func_class_param = true # false/true
+
+# Same as indent_func_call_param, but for class variable constructors
+indent_func_ctor_var_param = true # false/true
+
+# Same as indent_func_call_param, but for templates
+indent_template_param = true # false/true
+
+# Double the indent for indent_func_xxx_param options
+indent_func_param_double = true # false/true
+
+# Indentation column for standalone 'const' function decl/proto qualifier
+indent_func_const = 0 # number
+
+# Indentation column for standalone 'throw' function decl/proto qualifier
+indent_func_throw = 1 # number
+
+# The number of spaces to indent a continued '->' or '.'
+# Usually set to 0, 1, or indent_columns.
+indent_member = 0 # number
+
+# Spaces to indent single line ('//') comments on lines before code
+indent_sing_line_comments = 0 # number
+
+# If set, will indent trailing single line ('//') comments relative
+# to the code instead of trying to keep the same absolute column
+indent_relative_single_line_comments = false # false/true
+
+# Spaces to indent 'case' from 'switch'
+# Usually 0 or indent_columns.
+indent_switch_case = 0 # number
+
+# Spaces to shift the 'case' line, without affecting any other lines
+# Usually 0.
+indent_case_shift = 0 # number
+
+# Spaces to indent '{' from 'case'.
+# By default, the brace will appear under the 'c' in case.
+# Usually set to 0 or indent_columns.
+indent_case_brace = 0 # number
+
+# Whether to indent comments found in first column
+indent_col1_comment = false # false/true
+
+# How to indent goto labels
+# >0 : absolute column where 1 is the leftmost column
+# <=0 : subtract from brace indent
+indent_label = 1 # number
+
+# Same as indent_label, but for access specifiers that are followed by a colon
+indent_access_spec = 1 # number
+
+# Indent the code after an access specifier by one level.
+# If set, this option forces 'indent_access_spec=0'
+indent_access_spec_body = false # false/true
+
+# If an open paren is followed by a newline, indent the next line so that it lines up after the open paren (not recommended)
+indent_paren_nl = false # false/true
+
+# Controls the indent of a close paren after a newline.
+# 0: Indent to body level
+# 1: Align under the open paren
+# 2: Indent to the brace level
+indent_paren_close = 0 # number
+
+# Controls the indent of a comma when inside a paren.If TRUE, aligns under the open paren
+indent_comma_paren = false # false/true
+
+# Controls the indent of a BOOL operator when inside a paren.If TRUE, aligns under the open paren
+indent_bool_paren = false # false/true
+
+# If 'indent_bool_paren' is true, controls the indent of the first expression. If TRUE, aligns the first expression to the following ones
+indent_first_bool_expr = true # false/true
+
+# If an open square is followed by a newline, indent the next line so that it lines up after the open square (not recommended)
+indent_square_nl = false # false/true
+
+# Don't change the relative indent of ESQL/C 'EXEC SQL' bodies
+indent_preserve_sql = false # false/true
+
+# Align continued statements at the '='. Default=True
+# If FALSE or the '=' is followed by a newline, the next line is indent one tab.
+indent_align_assign = true # false/true
+
+# Indent OC blocks at brace level instead of usual rules.
+indent_oc_block = false # false/true
+
+# Indent OC blocks in a message relative to the parameter name.
+# 0=use indent_oc_block rules, 1+=spaces to indent
+indent_oc_block_msg = 0 # number
+
+# Minimum indent for subsequent parameters
+indent_oc_msg_colon = 0 # number
+
+#
+# Spacing options
+#
+
+# Add or remove space around arithmetic operator '+', '-', '/', '*', etc
+sp_arith = force # ignore/add/remove/force
+
+# Add or remove space around assignment operator '=', '+=', etc
+sp_assign = force # ignore/add/remove/force
+
+# Add or remove space around '=' in C++11 lambda capture specifications. Overrides sp_assign
+sp_cpp_lambda_assign = ignore # ignore/add/remove/force
+
+# Add or remove space after the capture specification in C++11 lambda.
+sp_cpp_lambda_paren = ignore # ignore/add/remove/force
+
+# Add or remove space around assignment operator '=' in a prototype
+sp_assign_default = ignore # ignore/add/remove/force
+
+# Add or remove space before assignment operator '=', '+=', etc. Overrides sp_assign.
+sp_before_assign = ignore # ignore/add/remove/force
+
+# Add or remove space after assignment operator '=', '+=', etc. Overrides sp_assign.
+sp_after_assign = ignore # ignore/add/remove/force
+
+# Add or remove space around assignment '=' in enum
+sp_enum_assign = ignore # ignore/add/remove/force
+
+# Add or remove space before assignment '=' in enum. Overrides sp_enum_assign.
+sp_enum_before_assign = ignore # ignore/add/remove/force
+
+# Add or remove space after assignment '=' in enum. Overrides sp_enum_assign.
+sp_enum_after_assign = ignore # ignore/add/remove/force
+
+# Add or remove space around preprocessor '##' concatenation operator. Default=Add
+sp_pp_concat = add # ignore/add/remove/force
+
+# Add or remove space after preprocessor '#' stringify operator. Also affects the '#@' charizing operator.
+sp_pp_stringify = add # ignore/add/remove/force
+
+# Add or remove space before preprocessor '#' stringify operator as in '#define x(y) L#y'.
+sp_before_pp_stringify = ignore # ignore/add/remove/force
+
+# Add or remove space around boolean operators '&&' and '||'
+sp_bool = ignore # ignore/add/remove/force
+
+# Add or remove space around compare operator '<', '>', '==', etc
+sp_compare = ignore # ignore/add/remove/force
+
+# Add or remove space inside '(' and ')'
+sp_inside_paren = ignore # ignore/add/remove/force
+
+# Add or remove space between nested parens: '((' vs ') )'
+sp_paren_paren = ignore # ignore/add/remove/force
+
+# Add or remove space between back-to-back parens: ')(' vs ') ('
+sp_cparen_oparen = ignore # ignore/add/remove/force
+
+# Whether to balance spaces inside nested parens
+sp_balance_nested_parens = true # false/true
+
+# Add or remove space between ')' and '{'
+sp_paren_brace = add # ignore/add/remove/force
+
+# Add or remove space before pointer star '*'
+sp_before_ptr_star = remove # ignore/add/remove/force
+
+# Add or remove space before pointer star '*' that isn't followed by a variable name
+# If set to 'ignore', sp_before_ptr_star is used instead.
+sp_before_unnamed_ptr_star = ignore # ignore/add/remove/force
+
+# Add or remove space between pointer stars '*'
+sp_between_ptr_star = remove # ignore/add/remove/force
+
+# Add or remove space after pointer star '*', if followed by a word.
+sp_after_ptr_star = force # ignore/add/remove/force
+
+# Add or remove space after a pointer star '*', if followed by a func proto/def.
+sp_after_ptr_star_func = ignore # ignore/add/remove/force
+
+# Add or remove space after a pointer star '*', if followed by an open paren (function types).
+sp_ptr_star_paren = ignore # ignore/add/remove/force
+
+# Add or remove space before a pointer star '*', if followed by a func proto/def.
+sp_before_ptr_star_func = ignore # ignore/add/remove/force
+
+# Add or remove space before a reference sign '&'
+sp_before_byref = remove # ignore/add/remove/force
+
+# Add or remove space before a reference sign '&' that isn't followed by a variable name
+# If set to 'ignore', sp_before_byref is used instead.
+sp_before_unnamed_byref = ignore # ignore/add/remove/force
+
+# Add or remove space after reference sign '&', if followed by a word.
+sp_after_byref = force # ignore/add/remove/force
+
+# Add or remove space after a reference sign '&', if followed by a func proto/def.
+sp_after_byref_func = ignore # ignore/add/remove/force
+
+# Add or remove space before a reference sign '&', if followed by a func proto/def.
+sp_before_byref_func = ignore # ignore/add/remove/force
+
+# Add or remove space between type and word. Default=Force
+sp_after_type = force # ignore/add/remove/force
+
+# Add or remove space before the paren in the D constructs 'template Foo(' and 'class Foo('.
+sp_before_template_paren = ignore # ignore/add/remove/force
+
+# Add or remove space in 'template <' vs 'template<'.
+# If set to ignore, sp_before_angle is used.
+sp_template_angle = force # ignore/add/remove/force
+
+# Add or remove space before '<>'
+sp_before_angle = ignore # ignore/add/remove/force
+
+# Add or remove space inside '<' and '>'
+sp_inside_angle = remove # ignore/add/remove/force
+
+# Add or remove space after '<>'
+sp_after_angle = force # ignore/add/remove/force
+
+# Add or remove space between '<>' and '(' as found in 'new List<byte>();'
+sp_angle_paren = ignore # ignore/add/remove/force
+
+# Add or remove space between '<>' and a word as in 'List<byte> m;'
+sp_angle_word = ignore # ignore/add/remove/force
+
+# Add or remove space between '>' and '>' in '>>' (template stuff C++/C# only). Default=Add
+sp_angle_shift = add # ignore/add/remove/force
+
+# Permit removal of the space between '>>' in 'foo<bar<int> >' (C++11 only). Default=False
+# sp_angle_shift cannot remove the space without this option.
+sp_permit_cpp11_shift = false # false/true
+
+# Add or remove space before '(' of 'if', 'for', 'switch', and 'while'
+sp_before_sparen = remove # ignore/add/remove/force
+
+# Add or remove space inside if-condition '(' and ')'
+sp_inside_sparen = add # ignore/add/remove/force
+
+# Add or remove space before if-condition ')'. Overrides sp_inside_sparen.
+sp_inside_sparen_close = ignore # ignore/add/remove/force
+
+# Add or remove space before if-condition '('. Overrides sp_inside_sparen.
+sp_inside_sparen_open = ignore # ignore/add/remove/force
+
+# Add or remove space after ')' of 'if', 'for', 'switch', and 'while'
+sp_after_sparen = ignore # ignore/add/remove/force
+
+# Add or remove space between ')' and '{' of 'if', 'for', 'switch', and 'while'
+sp_sparen_brace = add # ignore/add/remove/force
+
+# Add or remove space between 'invariant' and '(' in the D language.
+sp_invariant_paren = ignore # ignore/add/remove/force
+
+# Add or remove space after the ')' in 'invariant (C) c' in the D language.
+sp_after_invariant_paren = ignore # ignore/add/remove/force
+
+# Add or remove space before empty statement ';' on 'if', 'for' and 'while'
+sp_special_semi = add # ignore/add/remove/force
+
+# Add or remove space before ';'. Default=Remove
+sp_before_semi = remove # ignore/add/remove/force
+
+# Add or remove space before ';' in non-empty 'for' statements
+sp_before_semi_for = remove # ignore/add/remove/force
+
+# Add or remove space before a semicolon of an empty part of a for statement.
+sp_before_semi_for_empty = add # ignore/add/remove/force
+
+# Add or remove space after ';', except when followed by a comment. Default=Add
+sp_after_semi = add # ignore/add/remove/force
+
+# Add or remove space after ';' in non-empty 'for' statements. Default=Force
+sp_after_semi_for = force # ignore/add/remove/force
+
+# Add or remove space after the final semicolon of an empty part of a for statement: for ( ; ; <here> ).
+sp_after_semi_for_empty = ignore # ignore/add/remove/force
+
+# Add or remove space before '[' (except '[]')
+sp_before_square = remove # ignore/add/remove/force
+
+# Add or remove space before '[]'
+sp_before_squares = ignore # ignore/add/remove/force
+
+# Add or remove space inside a non-empty '[' and ']'
+sp_inside_square = ignore # ignore/add/remove/force
+
+# Add or remove space after ','
+sp_after_comma = force # ignore/add/remove/force
+
+# Add or remove space before ','
+sp_before_comma = remove # ignore/add/remove/force
+
+# Add or remove space between an open paren and comma: '(,' vs '( ,'
+sp_paren_comma = force # ignore/add/remove/force
+
+# Add or remove space before the variadic '...' when preceded by a non-punctuator
+sp_before_ellipsis = ignore # ignore/add/remove/force
+
+# Add or remove space after class ':'
+sp_after_class_colon = ignore # ignore/add/remove/force
+
+# Add or remove space before class ':'
+sp_before_class_colon = ignore # ignore/add/remove/force
+
+# Add or remove space before case ':'. Default=Remove
+sp_before_case_colon = remove # ignore/add/remove/force
+
+# Add or remove space between 'operator' and operator sign
+sp_after_operator = ignore # ignore/add/remove/force
+
+# Add or remove space between the operator symbol and the open paren, as in 'operator ++('
+sp_after_operator_sym = ignore # ignore/add/remove/force
+
+# Add or remove space after C/D cast, i.e. 'cast(int)a' vs 'cast(int) a' or '(int)a' vs '(int) a'
+sp_after_cast = force # ignore/add/remove/force
+
+# Add or remove spaces inside cast parens
+sp_inside_paren_cast = remove # ignore/add/remove/force
+
+# Add or remove space between the type and open paren in a C++ cast, i.e. 'int(exp)' vs 'int (exp)'
+sp_cpp_cast_paren = ignore # ignore/add/remove/force
+
+# Add or remove space between 'sizeof' and '('
+sp_sizeof_paren = remove # ignore/add/remove/force
+
+# Add or remove space after the tag keyword (Pawn)
+sp_after_tag = ignore # ignore/add/remove/force
+
+# Add or remove space inside enum '{' and '}'
+sp_inside_braces_enum = add # ignore/add/remove/force
+
+# Add or remove space inside struct/union '{' and '}'
+sp_inside_braces_struct = add # ignore/add/remove/force
+
+# Add or remove space inside '{' and '}'
+sp_inside_braces = add # ignore/add/remove/force
+
+# Add or remove space inside '{}'
+sp_inside_braces_empty = remove # ignore/add/remove/force
+
+# Add or remove space between return type and function name
+# A minimum of 1 is forced except for pointer return types.
+sp_type_func = add # ignore/add/remove/force
+
+# Add or remove space between function name and '(' on function declaration
+sp_func_proto_paren = remove # ignore/add/remove/force
+
+# Add or remove space between function name and '(' on function definition
+sp_func_def_paren = remove # ignore/add/remove/force
+
+# Add or remove space inside empty function '()'
+sp_inside_fparens = remove # ignore/add/remove/force
+
+# Add or remove space inside function '(' and ')'
+sp_inside_fparen = add # ignore/add/remove/force
+
+# Add or remove space inside the first parens in the function type: 'void (*x)(...)'
+sp_inside_tparen = ignore # ignore/add/remove/force
+
+# Add or remove between the parens in the function type: 'void (*x)(...)'
+sp_after_tparen_close = remove # ignore/add/remove/force
+
+# Add or remove space between ']' and '(' when part of a function call.
+sp_square_fparen = ignore # ignore/add/remove/force
+
+# Add or remove space between ')' and '{' of function
+sp_fparen_brace = add # ignore/add/remove/force
+
+# Add or remove space between function name and '(' on function calls
+sp_func_call_paren = remove # ignore/add/remove/force
+
+# Add or remove space between function name and '()' on function calls without parameters.
+# If set to 'ignore' (the default), sp_func_call_paren is used.
+sp_func_call_paren_empty = remove # ignore/add/remove/force
+
+# Add or remove space between the user function name and '(' on function calls
+# You need to set a keyword to be a user function, like this: 'set func_call_user _' in the config file.
+sp_func_call_user_paren = ignore # ignore/add/remove/force
+
+# Add or remove space between a constructor/destructor and the open paren
+sp_func_class_paren = remove # ignore/add/remove/force
+
+# Add or remove space between 'return' and '('
+sp_return_paren = add # ignore/add/remove/force
+
+# Add or remove space between '__attribute__' and '('
+sp_attribute_paren = ignore # ignore/add/remove/force
+
+# Add or remove space between 'defined' and '(' in '#if defined (FOO)'
+sp_defined_paren = remove # ignore/add/remove/force
+
+# Add or remove space between 'throw' and '(' in 'throw (something)'
+sp_throw_paren = ignore # ignore/add/remove/force
+
+# Add or remove space between 'throw' and anything other than '(' as in '@throw [...];'
+sp_after_throw = add # ignore/add/remove/force
+
+# Add or remove space between 'catch' and '(' in 'catch (something) { }'
+# If set to ignore, sp_before_sparen is used.
+sp_catch_paren = remove # ignore/add/remove/force
+
+# Add or remove space between 'version' and '(' in 'version (something) { }' (D language)
+# If set to ignore, sp_before_sparen is used.
+sp_version_paren = ignore # ignore/add/remove/force
+
+# Add or remove space between 'scope' and '(' in 'scope (something) { }' (D language)
+# If set to ignore, sp_before_sparen is used.
+sp_scope_paren = ignore # ignore/add/remove/force
+
+# Add or remove space between macro and value
+sp_macro = add # ignore/add/remove/force
+
+# Add or remove space between macro function ')' and value
+sp_macro_func = add # ignore/add/remove/force
+
+# Add or remove space between 'else' and '{' if on the same line
+sp_else_brace = add # ignore/add/remove/force
+
+# Add or remove space between '}' and 'else' if on the same line
+sp_brace_else = ignore # ignore/add/remove/force
+
+# Add or remove space between '}' and the name of a typedef on the same line
+sp_brace_typedef = ignore # ignore/add/remove/force
+
+# Add or remove space between 'catch' and '{' if on the same line
+sp_catch_brace = add # ignore/add/remove/force
+
+# Add or remove space between '}' and 'catch' if on the same line
+sp_brace_catch = add # ignore/add/remove/force
+
+# Add or remove space between 'finally' and '{' if on the same line
+sp_finally_brace = add # ignore/add/remove/force
+
+# Add or remove space between '}' and 'finally' if on the same line
+sp_brace_finally = add # ignore/add/remove/force
+
+# Add or remove space between 'try' and '{' if on the same line
+sp_try_brace = add # ignore/add/remove/force
+
+# Add or remove space between get/set and '{' if on the same line
+sp_getset_brace = add # ignore/add/remove/force
+
+# Add or remove space between a variable and '{' for C++ uniform initialization
+sp_word_brace = add # ignore/add/remove/force
+
+# Add or remove space between a variable and '{' for a namespace
+sp_word_brace_ns = add # ignore/add/remove/force
+
+# Add or remove space before the '::' operator
+sp_before_dc = ignore # ignore/add/remove/force
+
+# Add or remove space after the '::' operator
+sp_after_dc = ignore # ignore/add/remove/force
+
+# Add or remove around the D named array initializer ':' operator
+sp_d_array_colon = ignore # ignore/add/remove/force
+
+# Add or remove space after the '!' (not) operator. Default=Remove
+sp_not = remove # ignore/add/remove/force
+
+# Add or remove space after the '~' (invert) operator. Default=Remove
+sp_inv = remove # ignore/add/remove/force
+
+# Add or remove space after the '&' (address-of) operator. Default=Remove
+# This does not affect the spacing after a '&' that is part of a type.
+sp_addr = remove # ignore/add/remove/force
+
+# Add or remove space around the '.' or '->' operators. Default=Remove
+sp_member = remove # ignore/add/remove/force
+
+# Add or remove space after the '*' (dereference) operator. Default=Remove
+# This does not affect the spacing after a '*' that is part of a type.
+sp_deref = remove # ignore/add/remove/force
+
+# Add or remove space after '+' or '-', as in 'x = -5' or 'y = +7'. Default=Remove
+sp_sign = remove # ignore/add/remove/force
+
+# Add or remove space before or after '++' and '--', as in '(--x)' or 'y++;'. Default=Remove
+sp_incdec = remove # ignore/add/remove/force
+
+# Add or remove space before a backslash-newline at the end of a line. Default=Add
+sp_before_nl_cont = add # ignore/add/remove/force
+
+# Add or remove space after the scope '+' or '-', as in '-(void) foo;' or '+(int) bar;'
+sp_after_oc_scope = ignore # ignore/add/remove/force
+
+# Add or remove space after the colon in message specs
+# '-(int) f:(int) x;' vs '-(int) f: (int) x;'
+sp_after_oc_colon = ignore # ignore/add/remove/force
+
+# Add or remove space before the colon in message specs
+# '-(int) f: (int) x;' vs '-(int) f : (int) x;'
+sp_before_oc_colon = ignore # ignore/add/remove/force
+
+# Add or remove space after the colon in immutable dictionary expression
+# 'NSDictionary *test = @{@"foo" :@"bar"};'
+sp_after_oc_dict_colon = ignore # ignore/add/remove/force
+
+# Add or remove space before the colon in immutable dictionary expression
+# 'NSDictionary *test = @{@"foo" :@"bar"};'
+sp_before_oc_dict_colon = ignore # ignore/add/remove/force
+
+# Add or remove space after the colon in message specs
+# '[object setValue:1];' vs '[object setValue: 1];'
+sp_after_send_oc_colon = ignore # ignore/add/remove/force
+
+# Add or remove space before the colon in message specs
+# '[object setValue:1];' vs '[object setValue :1];'
+sp_before_send_oc_colon = ignore # ignore/add/remove/force
+
+# Add or remove space after the (type) in message specs
+# '-(int)f: (int) x;' vs '-(int)f: (int)x;'
+sp_after_oc_type = add # ignore/add/remove/force
+
+# Add or remove space after the first (type) in message specs
+# '-(int) f:(int)x;' vs '-(int)f:(int)x;'
+sp_after_oc_return_type = ignore # ignore/add/remove/force
+
+# Add or remove space between '@selector' and '('
+# '@selector(msgName)' vs '@selector (msgName)'
+# Also applies to @protocol() constructs
+sp_after_oc_at_sel = ignore # ignore/add/remove/force
+
+# Add or remove space between '@selector(x)' and the following word
+# '@selector(foo) a:' vs '@selector(foo)a:'
+sp_after_oc_at_sel_parens = ignore # ignore/add/remove/force
+
+# Add or remove space inside '@selector' parens
+# '@selector(foo)' vs '@selector( foo )'
+# Also applies to @protocol() constructs
+sp_inside_oc_at_sel_parens = ignore # ignore/add/remove/force
+
+# Add or remove space before a block pointer caret
+# '^int (int arg){...}' vs. ' ^int (int arg){...}'
+sp_before_oc_block_caret = ignore # ignore/add/remove/force
+
+# Add or remove space after a block pointer caret
+# '^int (int arg){...}' vs. '^ int (int arg){...}'
+sp_after_oc_block_caret = ignore # ignore/add/remove/force
+
+# Add or remove space between the receiver and selector in a message.
+# '[receiver selector ...]'
+sp_after_oc_msg_receiver = ignore # ignore/add/remove/force
+
+# Add or remove space after @property.
+sp_after_oc_property = ignore # ignore/add/remove/force
+
+# Add or remove space around the ':' in 'b ? t : f'
+sp_cond_colon = add # ignore/add/remove/force
+
+# Add or remove space before the ':' in 'b ? t : f'. Overrides sp_cond_colon.
+sp_cond_colon_before = ignore # ignore/add/remove/force
+
+# Add or remove space after the ':' in 'b ? t : f'. Overrides sp_cond_colon.
+sp_cond_colon_after = ignore # ignore/add/remove/force
+
+# Add or remove space around the '?' in 'b ? t : f'
+sp_cond_question = add # ignore/add/remove/force
+
+# Add or remove space before the '?' in 'b ? t : f'. Overrides sp_cond_question.
+sp_cond_question_before = ignore # ignore/add/remove/force
+
+# Add or remove space after the '?' in 'b ? t : f'. Overrides sp_cond_question.
+sp_cond_question_after = ignore # ignore/add/remove/force
+
+# Fix the spacing between 'case' and the label. Only 'ignore' and 'force' make sense here.
+sp_case_label = force # ignore/add/remove/force
+
+# Control the space around the D '..' operator.
+sp_range = ignore # ignore/add/remove/force
+
+# Control the spacing after ':' in 'for (TYPE VAR : EXPR)' (Java)
+sp_after_for_colon = force # ignore/add/remove/force
+
+# Control the spacing before ':' in 'for (TYPE VAR : EXPR)' (Java)
+sp_before_for_colon = force # ignore/add/remove/force
+
+# Control the spacing in 'extern (C)' (D)
+sp_extern_paren = ignore # ignore/add/remove/force
+
+# Control the space after the opening of a C++ comment '// A' vs '//A'
+sp_cmt_cpp_start = force # ignore/add/remove/force
+
+# Controls the spaces between #else or #endif and a trailing comment
+sp_endif_cmt = force # ignore/add/remove/force
+
+# Controls the spaces after 'new', 'delete', and 'delete[]'
+sp_after_new = force # ignore/add/remove/force
+
+# Controls the spaces before a trailing or embedded comment
+sp_before_tr_emb_cmt = add # ignore/add/remove/force
+
+# Number of spaces before a trailing or embedded comment
+sp_num_before_tr_emb_cmt = 4 # number
+
+# Control space between a Java annotation and the open paren.
+sp_annotation_paren = ignore # ignore/add/remove/force
+
+#
+# Code alignment (not left column spaces/tabs)
+#
+
+# Whether to keep non-indenting tabs
+align_keep_tabs = false # false/true
+
+# Whether to use tabs for aligning
+align_with_tabs = false # false/true
+
+# Whether to bump out to the next tab when aligning
+align_on_tabstop = true # false/true
+
+# Whether to left-align numbers
+align_number_left = true # false/true
+
+# Whether to keep whitespace not required for alignment.
+align_keep_extra_space = false # false/true
+
+# Align variable definitions in prototypes and functions
+align_func_params = false # false/true
+
+# Align parameters in single-line functions that have the same name.
+# The function names must already be aligned with each other.
+align_same_func_call_params = false # false/true
+
+# The span for aligning variable definitions (0=don't align)
+align_var_def_span = 1 # number
+
+# How to align the star in variable definitions.
+# 0=Part of the type 'void * foo;'
+# 1=Part of the variable 'void *foo;'
+# 2=Dangling 'void *foo;'
+align_var_def_star_style = 0 # number
+
+# How to align the '&' in variable definitions.
+# 0=Part of the type
+# 1=Part of the variable
+# 2=Dangling
+align_var_def_amp_style = 0 # number
+
+# The threshold for aligning variable definitions (0=no limit)
+align_var_def_thresh = 1 # number
+
+# The gap for aligning variable definitions
+align_var_def_gap = 1 # number
+
+# Whether to align the colon in struct bit fields
+align_var_def_colon = true # false/true
+
+# Whether to align any attribute after the variable name
+align_var_def_attribute = false # false/true
+
+# Whether to align inline struct/enum/union variable definitions
+align_var_def_inline = false # false/true
+
+# The span for aligning on '=' in assignments (0=don't align)
+align_assign_span = 1 # number
+
+# The threshold for aligning on '=' in assignments (0=no limit)
+align_assign_thresh = 1 # number
+
+# The span for aligning on '=' in enums (0=don't align)
+align_enum_equ_span = 1 # number
+
+# The threshold for aligning on '=' in enums (0=no limit)
+align_enum_equ_thresh = 1 # number
+
+# The span for aligning struct/union (0=don't align)
+align_var_struct_span = 1 # number
+
+# The threshold for aligning struct/union member definitions (0=no limit)
+align_var_struct_thresh = 1 # number
+
+# The gap for aligning struct/union member definitions
+align_var_struct_gap = 1 # number
+
+# The span for aligning struct initializer values (0=don't align)
+align_struct_init_span = 1 # number
+
+# The minimum space between the type and the synonym of a typedef
+align_typedef_gap = 1 # number
+
+# The span for aligning single-line typedefs (0=don't align)
+align_typedef_span = 1 # number
+
+# How to align typedef'd functions with other typedefs
+# 0: Don't mix them at all
+# 1: align the open paren with the types
+# 2: align the function type name with the other type names
+align_typedef_func = 0 # number
+
+# Controls the positioning of the '*' in typedefs. Just try it.
+# 0: Align on typedef type, ignore '*'
+# 1: The '*' is part of type name: typedef int *pint;
+# 2: The '*' is part of the type, but dangling: typedef int *pint;
+align_typedef_star_style = 0 # number
+
+# Controls the positioning of the '&' in typedefs. Just try it.
+# 0: Align on typedef type, ignore '&'
+# 1: The '&' is part of type name: typedef int &pint;
+# 2: The '&' is part of the type, but dangling: typedef int &pint;
+align_typedef_amp_style = 0 # number
+
+# The span for aligning comments that end lines (0=don't align)
+align_right_cmt_span = 3 # number
+
+# If aligning comments, mix with comments after '}' and #endif with less than 3 spaces before the comment
+align_right_cmt_mix = false # false/true
+
+# If a trailing comment is more than this number of columns away from the text it follows,
+# it will qualify for being aligned. This has to be > 0 to do anything.
+align_right_cmt_gap = 0 # number
+
+# Align trailing comment at or beyond column N; 'pulls in' comments as a bonus side effect (0=ignore)
+align_right_cmt_at_col = 0 # number
+
+# The span for aligning function prototypes (0=don't align)
+align_func_proto_span = 1 # number
+
+# Minimum gap between the return type and the function name.
+align_func_proto_gap = 0 # number
+
+# Align function protos on the 'operator' keyword instead of what follows
+align_on_operator = true # false/true
+
+# Whether to mix aligning prototype and variable declarations.
+# If true, align_var_def_XXX options are used instead of align_func_proto_XXX options.
+align_mix_var_proto = false # false/true
+
+# Align single-line functions with function prototypes, uses align_func_proto_span
+align_single_line_func = false # false/true
+
+# Aligning the open brace of single-line functions.
+# Requires align_single_line_func=true, uses align_func_proto_span
+align_single_line_brace = false # false/true
+
+# Gap for align_single_line_brace.
+align_single_line_brace_gap = 0 # number
+
+# The span for aligning ObjC msg spec (0=don't align)
+align_oc_msg_spec_span = 0 # number
+
+# Whether to align macros wrapped with a backslash and a newline.
+# This will not work right if the macro contains a multi-line comment.
+align_nl_cont = false # false/true
+
+# # Align macro functions and variables together
+align_pp_define_together = false # false/true
+
+# The minimum space between label and value of a preprocessor define
+align_pp_define_gap = 1 # number
+
+# The span for aligning on '#define' bodies (0=don't align)
+align_pp_define_span = 2 # number
+
+# Align lines that start with '<<' with previous '<<'. Default=true
+align_left_shift = true # false/true
+
+# Span for aligning parameters in an Obj-C message call on the ':' (0=don't align)
+align_oc_msg_colon_span = 0 # number
+
+# If true, always align with the first parameter, even if it is too short.
+align_oc_msg_colon_first = false # false/true
+
+# Aligning parameters in an Obj-C '+' or '-' declaration on the ':'
+align_oc_decl_colon = false # false/true
+
+#
+# Newline adding and removing options
+#
+
+# Whether to collapse empty blocks between '{' and '}'
+nl_collapse_empty_body = false # false/true
+
+# Don't split one-line braced assignments - 'foo_t f = { 1, 2 };'
+nl_assign_leave_one_liners = true # false/true
+
+# Don't split one-line braced statements inside a class xx { } body
+nl_class_leave_one_liners = true # false/true
+
+# Don't split one-line enums: 'enum foo { BAR = 15 };'
+nl_enum_leave_one_liners = false # false/true
+
+# Don't split one-line get or set functions
+nl_getset_leave_one_liners = false # false/true
+
+# Don't split one-line function definitions - 'int foo() { return 0; }'
+nl_func_leave_one_liners = false # false/true
+
+# Don't split one-line C++11 lambdas - '[]() { return 0; }'
+nl_cpp_lambda_leave_one_liners = false # false/true
+
+# Don't split one-line if/else statements - 'if(a) b++;'
+nl_if_leave_one_liners = false # false/true
+
+# Don't split one-line OC messages
+nl_oc_msg_leave_one_liner = false # false/true
+
+# Add or remove newlines at the start of the file
+nl_start_of_file = ignore # ignore/add/remove/force
+
+# The number of newlines at the start of the file (only used if nl_start_of_file is 'add' or 'force'
+nl_start_of_file_min = 0 # number
+
+# Add or remove newline at the end of the file
+nl_end_of_file = force # ignore/add/remove/force
+
+# The number of newlines at the end of the file (only used if nl_end_of_file is 'add' or 'force')
+nl_end_of_file_min = 1 # number
+
+# Add or remove newline between '=' and '{'
+nl_assign_brace = add # ignore/add/remove/force
+
+# Add or remove newline between '=' and '[' (D only)
+nl_assign_square = add # ignore/add/remove/force
+
+# Add or remove newline after '= [' (D only). Will also affect the newline before the ']'
+nl_after_square_assign = ignore # ignore/add/remove/force
+
+# The number of blank lines after a block of variable definitions at the top of a function body
+# 0 = No change (default)
+nl_func_var_def_blk = 1 # number
+
+# The number of newlines before a block of typedefs
+# 0 = No change (default)
+nl_typedef_blk_start = 1 # number
+
+# The number of newlines after a block of typedefs
+# 0 = No change (default)
+nl_typedef_blk_end = 1 # number
+
+# The maximum consecutive newlines within a block of typedefs
+# 0 = No change (default)
+nl_typedef_blk_in = 0 # number
+
+# The number of newlines before a block of variable definitions not at the top of a function body
+# 0 = No change (default)
+nl_var_def_blk_start = 0 # number
+
+# The number of newlines after a block of variable definitions not at the top of a function body
+# 0 = No change (default)
+nl_var_def_blk_end = 1 # number
+
+# The maximum consecutive newlines within a block of variable definitions
+# 0 = No change (default)
+nl_var_def_blk_in = 0 # number
+
+# Add or remove newline between a function call's ')' and '{', as in:
+# list_for_each(item, &list) { }
+nl_fcall_brace = remove # ignore/add/remove/force
+
+# Add or remove newline between 'enum' and '{'
+nl_enum_brace = force # ignore/add/remove/force
+
+# Add or remove newline between 'struct and '{'
+nl_struct_brace = force # ignore/add/remove/force
+
+# Add or remove newline between 'union' and '{'
+nl_union_brace = force # ignore/add/remove/force
+
+# Add or remove newline between 'if' and '{'
+nl_if_brace = force # ignore/add/remove/force
+
+# Add or remove newline between '}' and 'else'
+nl_brace_else = force # ignore/add/remove/force
+
+# Add or remove newline between 'else if' and '{'
+# If set to ignore, nl_if_brace is used instead
+nl_elseif_brace = force # ignore/add/remove/force
+
+# Add or remove newline between 'else' and '{'
+nl_else_brace = force # ignore/add/remove/force
+
+# Add or remove newline between 'else' and 'if'
+nl_else_if = remove # ignore/add/remove/force
+
+# Add or remove newline between '}' and 'finally'
+nl_brace_finally = force # ignore/add/remove/force
+
+# Add or remove newline between 'finally' and '{'
+nl_finally_brace = force # ignore/add/remove/force
+
+# Add or remove newline between 'try' and '{'
+nl_try_brace = force # ignore/add/remove/force
+
+# Add or remove newline between get/set and '{'
+nl_getset_brace = ignore # ignore/add/remove/force
+
+# Add or remove newline between 'for' and '{'
+nl_for_brace = force # ignore/add/remove/force
+
+# Add or remove newline between 'catch' and '{'
+nl_catch_brace = force # ignore/add/remove/force
+
+# Add or remove newline between '}' and 'catch'
+nl_brace_catch = force # ignore/add/remove/force
+
+# Add or remove newline between 'while' and '{'
+nl_while_brace = force # ignore/add/remove/force
+
+# Add or remove newline between 'scope (x)' and '{' (D)
+nl_scope_brace = ignore # ignore/add/remove/force
+
+# Add or remove newline between 'unittest' and '{' (D)
+nl_unittest_brace = ignore # ignore/add/remove/force
+
+# Add or remove newline between 'version (x)' and '{' (D)
+nl_version_brace = ignore # ignore/add/remove/force
+
+# Add or remove newline between 'using' and '{'
+nl_using_brace = ignore # ignore/add/remove/force
+
+# Add or remove newline between two open or close braces.
+# Due to general newline/brace handling, REMOVE may not work.
+nl_brace_brace = ignore # ignore/add/remove/force
+
+# Add or remove newline between 'do' and '{'
+nl_do_brace = ignore # ignore/add/remove/force
+
+# Add or remove newline between '}' and 'while' of 'do' statement
+nl_brace_while = remove # ignore/add/remove/force
+
+# Add or remove newline between 'switch' and '{'
+nl_switch_brace = force # ignore/add/remove/force
+
+# Add a newline between ')' and '{' if the ')' is on a different line than the if/for/etc.
+# Overrides nl_for_brace, nl_if_brace, nl_switch_brace, nl_while_switch, and nl_catch_brace.
+nl_multi_line_cond = false # false/true
+
+# Force a newline in a define after the macro name for multi-line defines.
+nl_multi_line_define = false # false/true
+
+# Whether to put a newline before 'case' statement
+nl_before_case = true # false/true
+
+# Add or remove newline between ')' and 'throw'
+nl_before_throw = ignore # ignore/add/remove/force
+
+# Whether to put a newline after 'case' statement
+nl_after_case = true # false/true
+
+# Add or remove a newline between a case ':' and '{'. Overrides nl_after_case.
+nl_case_colon_brace = force # ignore/add/remove/force
+
+# Newline between namespace and {
+nl_namespace_brace = remove # ignore/add/remove/force
+
+# Add or remove newline between 'template<>' and whatever follows.
+nl_template_class = force # ignore/add/remove/force
+
+# Add or remove newline between 'class' and '{'
+nl_class_brace = force # ignore/add/remove/force
+
+# Add or remove newline after each ',' in the constructor member initialization
+nl_class_init_args = add # ignore/add/remove/force
+
+# Add or remove newline between return type and function name in a function definition
+nl_func_type_name = remove # ignore/add/remove/force
+
+# Add or remove newline between return type and function name inside a class {}
+# Uses nl_func_type_name or nl_func_proto_type_name if set to ignore.
+nl_func_type_name_class = ignore # ignore/add/remove/force
+
+# Add or remove newline between function scope and name in a definition
+# Controls the newline after '::' in 'void A::f() { }'
+nl_func_scope_name = remove # ignore/add/remove/force
+
+# Add or remove newline between return type and function name in a prototype
+nl_func_proto_type_name = remove # ignore/add/remove/force
+
+# Add or remove newline between a function name and the opening '('
+nl_func_paren = remove # ignore/add/remove/force
+
+# Add or remove newline between a function name and the opening '(' in the definition
+nl_func_def_paren = remove # ignore/add/remove/force
+
+# Add or remove newline after '(' in a function declaration
+nl_func_decl_start = remove # ignore/add/remove/force
+
+# Add or remove newline after '(' in a function definition
+nl_func_def_start = remove # ignore/add/remove/force
+
+# Overrides nl_func_decl_start when there is only one parameter.
+nl_func_decl_start_single = remove # ignore/add/remove/force
+
+# Overrides nl_func_def_start when there is only one parameter.
+nl_func_def_start_single = remove # ignore/add/remove/force
+
+# Add or remove newline after each ',' in a function declaration
+nl_func_decl_args = ignore # ignore/add/remove/force
+
+# Add or remove newline after each ',' in a function definition
+nl_func_def_args = ignore # ignore/add/remove/force
+
+# Add or remove newline before the ')' in a function declaration
+nl_func_decl_end = ignore # ignore/add/remove/force
+
+# Add or remove newline before the ')' in a function definition
+nl_func_def_end = ignore # ignore/add/remove/force
+
+# Overrides nl_func_decl_end when there is only one parameter.
+nl_func_decl_end_single = remove # ignore/add/remove/force
+
+# Overrides nl_func_def_end when there is only one parameter.
+nl_func_def_end_single = remove # ignore/add/remove/force
+
+# Add or remove newline between '()' in a function declaration.
+nl_func_decl_empty = remove # ignore/add/remove/force
+
+# Add or remove newline between '()' in a function definition.
+nl_func_def_empty = remove # ignore/add/remove/force
+
+# Whether to put each OC message parameter on a separate line
+# See nl_oc_msg_leave_one_liner
+nl_oc_msg_args = false # false/true
+
+# Add or remove newline between function signature and '{'
+nl_fdef_brace = force # ignore/add/remove/force
+
+# Add or remove newline between C++11 lambda signature and '{'
+nl_cpp_ldef_brace = ignore # ignore/add/remove/force
+
+# Add or remove a newline between the return keyword and return expression.
+nl_return_expr = remove # ignore/add/remove/force
+
+# Whether to put a newline after semicolons, except in 'for' statements
+nl_after_semicolon = false # false/true
+
+# Whether to put a newline after brace open.
+# This also adds a newline before the matching brace close.
+nl_after_brace_open = true # false/true
+
+# If nl_after_brace_open and nl_after_brace_open_cmt are true, a newline is
+# placed between the open brace and a trailing single-line comment.
+nl_after_brace_open_cmt = true # false/true
+
+# Whether to put a newline after a virtual brace open with a non-empty body.
+# These occur in un-braced if/while/do/for statement bodies.
+nl_after_vbrace_open = true # false/true
+
+# Whether to put a newline after a virtual brace open with an empty body.
+# These occur in un-braced if/while/do/for statement bodies.
+nl_after_vbrace_open_empty = true # false/true
+
+# Whether to put a newline after a brace close.
+# Does not apply if followed by a necessary ';'.
+nl_after_brace_close = true # false/true
+
+# Whether to put a newline after a virtual brace close.
+# Would add a newline before return in: 'if (foo) a++; return;'
+nl_after_vbrace_close = true # false/true
+
+# Control the newline between the close brace and 'b' in: 'struct { int a; } b;'
+# Affects enums, unions, and structures. If set to ignore, uses nl_after_brace_close
+nl_brace_struct_var = ignore # ignore/add/remove/force
+
+# Whether to alter newlines in '#define' macros
+nl_define_macro = false # false/true
+
+# Whether to not put blanks after '#ifxx', '#elxx', or before '#endif'
+nl_squeeze_ifdef = false # false/true
+
+# Add or remove blank line before 'if'
+nl_before_if = add # ignore/add/remove/force
+
+# Add or remove blank line after 'if' statement
+nl_after_if = add # ignore/add/remove/force
+
+# Add or remove blank line before 'for'
+nl_before_for = add # ignore/add/remove/force
+
+# Add or remove blank line after 'for' statement
+nl_after_for = add # ignore/add/remove/force
+
+# Add or remove blank line before 'while'
+nl_before_while = add # ignore/add/remove/force
+
+# Add or remove blank line after 'while' statement
+nl_after_while = add # ignore/add/remove/force
+
+# Add or remove blank line before 'switch'
+nl_before_switch = add # ignore/add/remove/force
+
+# Add or remove blank line after 'switch' statement
+nl_after_switch = add # ignore/add/remove/force
+
+# Add or remove blank line before 'do'
+nl_before_do = add # ignore/add/remove/force
+
+# Add or remove blank line after 'do/while' statement
+nl_after_do = add # ignore/add/remove/force
+
+# Whether to double-space commented-entries in struct/enum
+nl_ds_struct_enum_cmt = true # false/true
+
+# Whether to double-space before the close brace of a struct/union/enum
+# (lower priority than 'eat_blanks_before_close_brace')
+nl_ds_struct_enum_close_brace = false # false/true
+
+# Add or remove a newline around a class colon.
+# Related to pos_class_colon, nl_class_init_args, and pos_comma.
+nl_class_colon = ignore # ignore/add/remove/force
+
+# Change simple unbraced if statements into a one-liner
+# 'if(b)\n i++;' => 'if(b) i++;'
+nl_create_if_one_liner = false # false/true
+
+# Change simple unbraced for statements into a one-liner
+# 'for (i=0;i<5;i++)\n foo(i);' => 'for (i=0;i<5;i++) foo(i);'
+nl_create_for_one_liner = false # false/true
+
+# Change simple unbraced while statements into a one-liner
+# 'while (i<5)\n foo(i++);' => 'while (i<5) foo(i++);'
+nl_create_while_one_liner = false # false/true
+
+#
+# Positioning options
+#
+
+# The position of arithmetic operators in wrapped expressions
+pos_arith = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force
+
+# The position of assignment in wrapped expressions.
+# Do not affect '=' followed by '{'
+pos_assign = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force
+
+# The position of boolean operators in wrapped expressions
+pos_bool = lead # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force
+
+# The position of comparison operators in wrapped expressions
+pos_compare = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force
+
+# The position of conditional (b ? t : f) operators in wrapped expressions
+pos_conditional = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force
+
+# The position of the comma in wrapped expressions
+pos_comma = trail # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force
+
+# The position of the comma in the constructor initialization list
+pos_class_comma = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force
+
+# The position of colons between constructor and member initialization
+pos_class_colon = trail # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force
+
+#
+# Line Splitting options
+#
+
+# Try to limit code width to N number of columns
+code_width = 100 # number
+
+# Whether to fully split long 'for' statements at semi-colons
+ls_for_split_full = true # false/true
+
+# Whether to fully split long function protos/calls at commas
+ls_func_split_full = true # false/true
+
+# Whether to split lines as close to code_width as possible and ignore some groupings
+ls_code_width = false # false/true
+
+#
+# Blank line options
+#
+
+# The maximum consecutive newlines
+nl_max = 3 # number
+
+# The number of newlines after a function prototype, if followed by another function prototype
+nl_after_func_proto = 0 # number
+
+# The number of newlines after a function prototype, if not followed by another function prototype
+nl_after_func_proto_group = 2 # number
+
+# The number of newlines after '}' of a multi-line function body
+nl_after_func_body = 3 # number
+
+# The number of newlines after '}' of a multi-line function body in a class declaration
+nl_after_func_body_class = 2 # number
+
+# The number of newlines after '}' of a single line function body
+nl_after_func_body_one_liner = 1 # number
+
+# The minimum number of newlines before a multi-line comment.
+# Doesn't apply if after a brace open or another multi-line comment.
+nl_before_block_comment = 1 # number
+
+# The minimum number of newlines before a single-line C comment.
+# Doesn't apply if after a brace open or other single-line C comments.
+nl_before_c_comment = 1 # number
+
+# The minimum number of newlines before a CPP comment.
+# Doesn't apply if after a brace open or other CPP comments.
+nl_before_cpp_comment = 1 # number
+
+# Whether to force a newline after a multi-line comment.
+nl_after_multiline_comment = false # false/true
+
+# The number of newlines after '}' or ';' of a struct/enum/union definition
+nl_after_struct = 0 # number
+
+# The number of newlines after '}' or ';' of a class definition
+nl_after_class = 0 # number
+
+# The number of newlines before a 'private:', 'public:', 'protected:', 'signals:', or 'slots:' label.
+# Will not change the newline count if after a brace open.
+# 0 = No change.
+nl_before_access_spec = 2 # number
+
+# The number of newlines after a 'private:', 'public:', 'protected:', 'signals:', or 'slots:' label.
+# 0 = No change.
+nl_after_access_spec = 0 # number
+
+# The number of newlines between a function def and the function comment.
+# 0 = No change.
+nl_comment_func_def = 0 # number
+
+# The number of newlines after a try-catch-finally block that isn't followed by a brace close.
+# 0 = No change.
+nl_after_try_catch_finally = 1 # number
+
+# The number of newlines before and after a property, indexer or event decl.
+# 0 = No change.
+nl_around_cs_property = 0 # number
+
+# The number of newlines between the get/set/add/remove handlers in C#.
+# 0 = No change.
+nl_between_get_set = 0 # number
+
+# Add or remove newline between C# property and the '{'
+nl_property_brace = ignore # ignore/add/remove/force
+
+# Whether to remove blank lines after '{'
+eat_blanks_after_open_brace = true # false/true
+
+# Whether to remove blank lines before '}'
+eat_blanks_before_close_brace = true # false/true
+
+# How aggressively to remove extra newlines not in preproc.
+# 0: No change
+# 1: Remove most newlines not handled by other config
+# 2: Remove all newlines and reformat completely by config
+nl_remove_extra_newlines = 0 # number
+
+# Whether to put a blank line before 'return' statements, unless after an open brace.
+nl_before_return = false # false/true
+
+# Whether to put a blank line after 'return' statements, unless followed by a close brace.
+nl_after_return = false # false/true
+
+# Whether to put a newline after a Java annotation statement.
+# Only affects annotations that are after a newline.
+nl_after_annotation = ignore # ignore/add/remove/force
+
+# Controls the newline between two annotations.
+nl_between_annotation = ignore # ignore/add/remove/force
+
+#
+# Code modifying options (non-whitespace)
+#
+
+# Add or remove braces on single-line 'do' statement
+mod_full_brace_do = add # ignore/add/remove/force
+
+# Add or remove braces on single-line 'for' statement
+mod_full_brace_for = ignore # ignore/add/remove/force
+
+# Add or remove braces on single-line function definitions. (Pawn)
+mod_full_brace_function = ignore # ignore/add/remove/force
+
+# Add or remove braces on single-line 'if' statement. Will not remove the braces if they contain an 'else'.
+mod_full_brace_if = ignore # ignore/add/remove/force
+
+# Make all if/elseif/else statements in a chain be braced or not. Overrides mod_full_brace_if.
+# If any must be braced, they are all braced. If all can be unbraced, then the braces are removed.
+mod_full_brace_if_chain = false # false/true
+
+# Don't remove braces around statements that span N newlines
+mod_full_brace_nl = 3 # number
+
+# Add or remove braces on single-line 'while' statement
+mod_full_brace_while = ignore # ignore/add/remove/force
+
+# Add or remove braces on single-line 'using ()' statement
+mod_full_brace_using = ignore # ignore/add/remove/force
+
+# Add or remove unnecessary paren on 'return' statement
+mod_paren_on_return = remove # ignore/add/remove/force
+
+# Whether to change optional semicolons to real semicolons
+mod_pawn_semicolon = false # false/true
+
+# Add parens on 'while' and 'if' statement around bools
+mod_full_paren_if_bool = false # false/true
+
+# Whether to remove superfluous semicolons
+mod_remove_extra_semicolon = false # false/true
+
+# If a function body exceeds the specified number of newlines and doesn't have a comment after
+# the close brace, a comment will be added.
+mod_add_long_function_closebrace_comment = 0 # number
+
+# If a namespace body exceeds the specified number of newlines and doesn't have a comment after
+# the close brace, a comment will be added.
+mod_add_long_namespace_closebrace_comment = 0 # number
+
+# If a switch body exceeds the specified number of newlines and doesn't have a comment after
+# the close brace, a comment will be added.
+mod_add_long_switch_closebrace_comment = 0 # number
+
+# If an #ifdef body exceeds the specified number of newlines and doesn't have a comment after
+# the #endif, a comment will be added.
+mod_add_long_ifdef_endif_comment = 0 # number
+
+# If an #ifdef or #else body exceeds the specified number of newlines and doesn't have a comment after
+# the #else, a comment will be added.
+mod_add_long_ifdef_else_comment = 0 # number
+
+# If TRUE, will sort consecutive single-line 'import' statements [Java, D]
+mod_sort_import = false # false/true
+
+# If TRUE, will sort consecutive single-line 'using' statements [C#]
+mod_sort_using = false # false/true
+
+# If TRUE, will sort consecutive single-line '#include' statements [C/C++] and '#import' statements [Obj-C]
+# This is generally a bad idea, as it may break your code.
+mod_sort_include = false # false/true
+
+# If TRUE, it will move a 'break' that appears after a fully braced 'case' before the close brace.
+mod_move_case_break = false # false/true
+
+# Will add or remove the braces around a fully braced case statement.
+# Will only remove the braces if there are no variable declarations in the block.
+mod_case_brace = ignore # ignore/add/remove/force
+
+# If TRUE, it will remove a void 'return;' that appears as the last statement in a function.
+mod_remove_empty_return = true # false/true
+
+#
+# Comment modifications
+#
+
+# Try to wrap comments at cmt_width columns
+cmt_width = 0 # number
+
+# Set the comment reflow mode (default: 0)
+# 0: no reflowing (apart from the line wrapping due to cmt_width)
+# 1: no touching at all
+# 2: full reflow
+cmt_reflow_mode = 0 # number
+
+# If false, disable all multi-line comment changes, including cmt_width. keyword substitution, and leading chars.
+# Default is true.
+cmt_indent_multi = true # false/true
+
+# Whether to group c-comments that look like they are in a block
+cmt_c_group = false # false/true
+
+# Whether to put an empty '/*' on the first line of the combined c-comment
+cmt_c_nl_start = false # false/true
+
+# Whether to put a newline before the closing '*/' of the combined c-comment
+cmt_c_nl_end = false # false/true
+
+# Whether to group cpp-comments that look like they are in a block
+cmt_cpp_group = false # false/true
+
+# Whether to put an empty '/*' on the first line of the combined cpp-comment
+cmt_cpp_nl_start = false # false/true
+
+# Whether to put a newline before the closing '*/' of the combined cpp-comment
+cmt_cpp_nl_end = false # false/true
+
+# Whether to change cpp-comments into c-comments
+cmt_cpp_to_c = false # false/true
+
+# Whether to put a star on subsequent comment lines
+cmt_star_cont = true # false/true
+
+# The number of spaces to insert at the start of subsequent comment lines
+cmt_sp_before_star_cont = 0 # number
+
+# The number of spaces to insert after the star on subsequent comment lines
+cmt_sp_after_star_cont = 1 # number
+
+# For multi-line comments with a '*' lead, remove leading spaces if the first and last lines of
+# the comment are the same length. Default=True
+cmt_multi_check_last = true # false/true
+
+# The filename that contains text to insert at the head of a file if the file doesn't start with a C/C++ comment.
+# Will substitute $(filename) with the current file's name.
+cmt_insert_file_header = "" # string
+
+# The filename that contains text to insert at the end of a file if the file doesn't end with a C/C++ comment.
+# Will substitute $(filename) with the current file's name.
+cmt_insert_file_footer = "" # string
+
+# The filename that contains text to insert before a function implementation if the function isn't preceded with a C/C++ comment.
+# Will substitute $(function) with the function name and $(javaparam) with the javadoc @param and @return stuff.
+# Will also substitute $(fclass) with the class name: void CFoo::Bar() { ... }
+cmt_insert_func_header = "" # string
+
+# The filename that contains text to insert before a class if the class isn't preceded with a C/C++ comment.
+# Will substitute $(class) with the class name.
+cmt_insert_class_header = "" # string
+
+# The filename that contains text to insert before a Obj-C message specification if the method isn't preceeded with a C/C++ comment.
+# Will substitute $(message) with the function name and $(javaparam) with the javadoc @param and @return stuff.
+cmt_insert_oc_msg_header = "" # string
+
+# If a preprocessor is encountered when stepping backwards from a function name, then
+# this option decides whether the comment should be inserted.
+# Affects cmt_insert_oc_msg_header, cmt_insert_func_header and cmt_insert_class_header.
+cmt_insert_before_preproc = false # false/true
+
+#
+# Preprocessor options
+#
+
+# Control indent of preprocessors inside #if blocks at brace level 0
+pp_indent = ignore # ignore/add/remove/force
+
+# Whether to indent #if/#else/#endif at the brace level (true) or from column 1 (false)
+pp_indent_at_level = false # false/true
+
+# If pp_indent_at_level=false, specifies the number of columns to indent per level. Default=1.
+pp_indent_count = 1 # number
+
+# Add or remove space after # based on pp_level of #if blocks
+pp_space = ignore # ignore/add/remove/force
+
+# Sets the number of spaces added with pp_space
+pp_space_count = 1 # number
+
+# The indent for #region and #endregion in C# and '#pragma region' in C/C++
+pp_indent_region = 0 # number
+
+# Whether to indent the code between #region and #endregion
+pp_region_indent_code = false # false/true
+
+# If pp_indent_at_level=true, sets the indent for #if, #else, and #endif when not at file-level
+pp_indent_if = 1 # number
+
+# Control whether to indent the code between #if, #else and #endif when not at file-level
+pp_if_indent_code = false # false/true
+
+# Whether to indent '#define' at the brace level (true) or from column 1 (false)
+pp_define_at_level = false # false/true
+
+# You can force a token to be a type with the 'type' option.
+# Example:
+# type myfoo1 myfoo2
+#
+# You can create custom macro-based indentation using macro-open,
+# macro-else and macro-close.
+# Example:
+# macro-open BEGIN_TEMPLATE_MESSAGE_MAP
+# macro-open BEGIN_MESSAGE_MAP
+# macro-close END_MESSAGE_MAP
+#
+# You can assign any keyword to any type with the set option.
+# set func_call_user _ N_
+#
+# The full syntax description of all custom definition config entries
+# is shown below:
+#
+# define custom tokens as:
+# - embed whitespace in token using '' escape character, or
+# put token in quotes
+# - these: ' " and ` are recognized as quote delimiters
+#
+# type token1 token2 token3 ...
+# ^ optionally specify multiple tokens on a single line
+# define def_token output_token
+# ^ output_token is optional, then NULL is assumed
+# macro-open token
+# macro-close token
+# macro-else token
+# set id token1 token2 ...
+# ^ optionally specify multiple tokens on a single line
+# ^ id is one of the names in token_enum.h sans the CT_ prefix,
+# e.g. PP_PRAGMA
+#
+# all tokens are separated by any mix of ',' commas, '=' equal signs
+# and whitespace (space, tab)
+#
diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt
new file mode 100644
index 0000000..04489b3
--- /dev/null
+++ b/utils/CMakeLists.txt
@@ -0,0 +1,2 @@
+add_subdirectory( idftools )
+
diff --git a/utils/idftools/CMakeLists.txt b/utils/idftools/CMakeLists.txt
new file mode 100644
index 0000000..4334403
--- /dev/null
+++ b/utils/idftools/CMakeLists.txt
@@ -0,0 +1,36 @@
+include_directories(
+ "${CMAKE_SOURCE_DIR}/lib_dxf"
+ "${CMAKE_SOURCE_DIR}/utils/idftools"
+ ${OPENGL_INCLUDE_DIR}
+ ${Boost_INCLUDE_DIR}
+ )
+
+link_directories(
+ "${CMAKE_BINARY_DIR}/lib_dxf"
+ )
+
+add_library( idf3 STATIC
+ idf_helpers.cpp idf_common.cpp idf_outlines.cpp
+ idf_parser.cpp vrml_layer.cpp )
+
+add_executable( idfcyl idf_cylinder.cpp )
+add_executable( idfrect idf_rect.cpp )
+add_executable( dxf2idf dxf2idfmain.cpp dxf2idf.cpp )
+add_executable( idf2vrml idf2vrml.cpp )
+
+add_dependencies( idf2vrml boost )
+
+target_link_libraries( dxf2idf lib_dxf idf3 ${wxWidgets_LIBRARIES} )
+
+target_link_libraries( idf2vrml idf3 ${OPENGL_LIBRARIES} ${wxWidgets_LIBRARIES} )
+
+if( APPLE )
+ # puts binaries into the *.app bundle while linking
+ set_target_properties( idfcyl idfrect dxf2idf idf2vrml PROPERTIES
+ RUNTIME_OUTPUT_DIRECTORY ${OSX_BUNDLE_BUILD_BIN_DIR}
+ )
+else()
+ install( TARGETS idfcyl idfrect dxf2idf idf2vrml
+ DESTINATION ${KICAD_BIN}
+ COMPONENT binary )
+endif()
diff --git a/utils/idftools/dxf2idf.cpp b/utils/idftools/dxf2idf.cpp
new file mode 100644
index 0000000..7147cda
--- /dev/null
+++ b/utils/idftools/dxf2idf.cpp
@@ -0,0 +1,460 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2014 Cirilo Bernardo
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <cstdio>
+#include <iostream>
+#include <libdxfrw.h>
+#include <dxf2idf.h>
+
+// differences in angle smaller than MIN_ANG are considered equal
+#define MIN_ANG (0.01)
+
+// min and max bulge bracketing min. arc before transition to line segment
+// and max. arc limit
+// MIN_BULGE = 0.002 ~0.45 degrees
+// MAX_BULGE = 2000 ~89.97 degrees
+#define MIN_BULGE 0.002
+#define MAX_BULGE 2000.0
+
+DXF2IDF::~DXF2IDF()
+{
+ while( !lines.empty() )
+ {
+#ifdef DEBUG_IDF
+ IDF3::printSeg( lines.back() );
+#endif
+ delete lines.back();
+ lines.pop_back();
+ }
+}
+
+
+bool DXF2IDF::ReadDxf( const std::string aFile )
+{
+ dxfRW* reader = new dxfRW( aFile.c_str() );
+
+ if( !reader )
+ return false;
+
+ bool success = reader->read( this, true );
+
+ delete reader;
+ return success;
+}
+
+
+void DXF2IDF::addLine( const DRW_Line& data )
+{
+ IDF_POINT p1, p2;
+
+ p1.x = data.basePoint.x * m_scale;
+ p1.y = data.basePoint.y * m_scale;
+ p2.x = data.secPoint.x * m_scale;
+ p2.y = data.secPoint.y * m_scale;
+
+ insertLine( p1, p2 );
+ return;
+}
+
+
+void DXF2IDF::addCircle( const DRW_Circle& data )
+{
+ IDF_POINT p1, p2;
+
+ p1.x = data.basePoint.x * m_scale;
+ p1.y = data.basePoint.y * m_scale;
+
+ p2.x = p1.x + data.radious * m_scale;
+ p2.y = p1.y;
+
+ IDF_SEGMENT* seg = new IDF_SEGMENT( p1, p2, 360, true );
+
+ if( seg )
+ lines.push_back( seg );
+
+ return;
+}
+
+
+void DXF2IDF::addArc( const DRW_Arc& data )
+{
+ IDF_POINT p1, p2;
+
+ p1.x = data.basePoint.x * m_scale;
+ p1.y = data.basePoint.y * m_scale;
+
+ // note: DXF circles always run CCW
+ double ea = data.endangle;
+
+ while( ea < data.staangle )
+ ea += M_PI;
+
+ p2.x = p1.x + cos( data.staangle ) * data.radious * m_scale;
+ p2.y = p1.y + sin( data.staangle ) * data.radious * m_scale;
+
+ double angle = ( ea - data.staangle ) * 180.0 / M_PI;
+
+ IDF_SEGMENT* seg = new IDF_SEGMENT( p1, p2, angle, true );
+
+ if( seg )
+ lines.push_back( seg );
+
+ return;
+}
+
+
+bool DXF2IDF::WriteOutline( FILE* aFile, bool isInch )
+{
+ if( lines.empty() )
+ {
+ std::cerr << "* DXF2IDF: empty outline\n";
+ return false;
+ }
+
+ // 1. find lowest X value
+ // 2. string an outline together
+ // 3. emit warnings if more than 1 outline
+ IDF_OUTLINE outline;
+
+ IDF3::GetOutline( lines, outline );
+
+ if( outline.empty() )
+ {
+ std::cerr << "* DXF2IDF::WriteOutline(): no valid outline in file\n";
+ return false;
+ }
+
+ if( !lines.empty() )
+ {
+ std::cerr << "* DXF2IDF::WriteOutline(): WARNING: more than 1 outline in file\n";
+ std::cerr << "* Only the first outline will be used\n";
+ }
+
+ char loopDir = '1';
+
+ if( outline.IsCCW() )
+ loopDir = '0';
+
+ std::list<IDF_SEGMENT*>::iterator bo;
+ std::list<IDF_SEGMENT*>::iterator eo;
+
+ if( outline.size() == 1 )
+ {
+ if( !outline.front()->IsCircle() )
+ {
+ std::cerr << "* DXF2IDF::WriteOutline(): bad outline\n";
+ return false;
+ }
+
+ // NOTE: a circle always has an angle of 360, never -360,
+ // otherwise SolidWorks chokes on the file.
+ if( isInch )
+ {
+ fprintf( aFile, "%c %d %d 0\n", loopDir,
+ (int) (1000 * outline.front()->startPoint.x),
+ (int) (1000 * outline.front()->startPoint.y) );
+ fprintf( aFile, "%c %d %d 360\n", loopDir,
+ (int) (1000 * outline.front()->endPoint.x),
+ (int) (1000 * outline.front()->endPoint.y) );
+ }
+ else
+ {
+ fprintf( aFile, "%c %.3f %.3f 0\n", loopDir,
+ outline.front()->startPoint.x, outline.front()->startPoint.y );
+ fprintf( aFile, "%c %.3f %.3f 360\n", loopDir,
+ outline.front()->endPoint.x, outline.front()->endPoint.y );
+ }
+
+ return true;
+ }
+
+ // ensure that the very last point is the same as the very first point
+ outline.back()-> endPoint = outline.front()->startPoint;
+
+ bo = outline.begin();
+ eo = outline.end();
+
+ // for the first item we write out both points
+ if( (*bo)->angle < MIN_ANG && (*bo)->angle > -MIN_ANG )
+ {
+ if( isInch )
+ {
+ fprintf( aFile, "%c %d %d 0\n", loopDir,
+ (int) (1000 * (*bo)->startPoint.x),
+ (int) (1000 * (*bo)->startPoint.y) );
+ fprintf( aFile, "%c %d %d 0\n", loopDir,
+ (int) (1000 * (*bo)->endPoint.x),
+ (int) (1000 * (*bo)->endPoint.y) );
+ }
+ else
+ {
+ fprintf( aFile, "%c %.3f %.3f 0\n", loopDir,
+ (*bo)->startPoint.x, (*bo)->startPoint.y );
+ fprintf( aFile, "%c %.3f %.3f 0\n", loopDir,
+ (*bo)->endPoint.x, (*bo)->endPoint.y );
+ }
+ }
+ else
+ {
+ if( isInch )
+ {
+ fprintf( aFile, "%c %d %d 0\n", loopDir,
+ (int) (1000 * (*bo)->startPoint.x),
+ (int) (1000 * (*bo)->startPoint.y) );
+ fprintf( aFile, "%c %d %d %.2f\n", loopDir,
+ (int) (1000 * (*bo)->endPoint.x),
+ (int) (1000 * (*bo)->endPoint.y),
+ (*bo)->angle );
+ }
+ else
+ {
+ fprintf( aFile, "%c %.3f %.3f 0\n", loopDir,
+ (*bo)->startPoint.x, (*bo)->startPoint.y );
+ fprintf( aFile, "%c %.3f %.3f %.2f\n", loopDir,
+ (*bo)->endPoint.x, (*bo)->endPoint.y, (*bo)->angle );
+ }
+ }
+
+ ++bo;
+
+ // for all other segments we only write out the last point
+ while( bo != eo )
+ {
+ if( isInch )
+ {
+ if( (*bo)->angle < MIN_ANG && (*bo)->angle > -MIN_ANG )
+ {
+ fprintf( aFile, "%c %d %d 0\n", loopDir,
+ (int) (1000 * (*bo)->endPoint.x),
+ (int) (1000 * (*bo)->endPoint.y) );
+ }
+ else
+ {
+ fprintf( aFile, "%c %d %d %.2f\n", loopDir,
+ (int) (1000 * (*bo)->endPoint.x),
+ (int) (1000 * (*bo)->endPoint.y),
+ (*bo)->angle );
+ }
+ }
+ else
+ {
+ if( (*bo)->angle < MIN_ANG && (*bo)->angle > -MIN_ANG )
+ {
+ fprintf( aFile, "%c %.5f %.5f 0\n", loopDir,
+ (*bo)->endPoint.x, (*bo)->endPoint.y );
+ }
+ else
+ {
+ fprintf( aFile, "%c %.5f %.5f %.2f\n", loopDir,
+ (*bo)->endPoint.x, (*bo)->endPoint.y, (*bo)->angle );
+ }
+ }
+
+ ++bo;
+ }
+
+ return true;
+}
+
+
+void DXF2IDF::addHeader( const DRW_Header* data )
+{
+ std::map<std::string, DRW_Variant*>::const_iterator it;
+ m_scale = 1.0; // assume no scale factor
+
+ for( it = data->vars.begin(); it != data->vars.end(); ++it )
+ {
+ std::string key = ( (*it).first ).c_str();
+
+ if( key == "$INSUNITS" )
+ {
+ DRW_Variant* var = (*it).second;
+
+ switch( var->content.i )
+ {
+ case 1: // inches
+ m_scale = 25.4;
+ break;
+
+ case 2: // feet
+ m_scale = 304.8;
+ break;
+
+ case 5: // centimeters
+ m_scale = 10.0;
+ break;
+
+ case 6: // meters
+ m_scale = 1000.0;
+ break;
+
+ case 8: // microinches
+ m_scale = 2.54e-5;
+ break;
+
+ case 9: // mils
+ m_scale = 0.0254;
+ break;
+
+ case 10: // yards
+ m_scale = 914.4;
+ break;
+
+ case 11: // Angstroms
+ m_scale = 1.0e-7;
+ break;
+
+ case 12: // nanometers
+ m_scale = 1.0e-6;
+ break;
+
+ case 13: // micrometers
+ m_scale = 1.0e-3;
+ break;
+
+ case 14: // decimeters
+ m_scale = 100.0;
+ break;
+
+ default:
+ // use the default of 1.0 for:
+ // 0: Unspecified Units
+ // 4: mm
+ // 3: miles
+ // 7: kilometers
+ // 15: decameters
+ // 16: hectometers
+ // 17: gigameters
+ // 18: AU
+ // 19: lightyears
+ // 20: parsecs
+ break;
+ }
+ }
+ }
+}
+
+
+void DXF2IDF::addLWPolyline(const DRW_LWPolyline& data )
+{
+ IDF_POINT poly_start;
+ IDF_POINT seg_start;
+ IDF_POINT seg_end;
+ double bulge = 0.0;
+
+ if( !data.vertlist.empty() )
+ {
+ DRW_Vertex2D* vertex = data.vertlist[0];
+ seg_start.x = vertex->x * m_scale;
+ seg_start.y = vertex->y * m_scale;
+ poly_start = seg_start;
+ bulge = vertex->bulge;
+ }
+
+ for( size_t i = 1; i < data.vertlist.size(); ++i )
+ {
+ DRW_Vertex2D* vertex = data.vertlist[i];
+ seg_end.x = vertex->x * m_scale;
+ seg_end.y = vertex->y * m_scale;
+
+ if( std::abs( bulge ) < MIN_BULGE )
+ insertLine( seg_start, seg_end );
+ else
+ insertArc( seg_start, seg_end, bulge );
+
+ seg_start = seg_end;
+ bulge = vertex->bulge;
+ }
+
+ // Polyline flags bit 0 indicates closed (1) or open (0) polyline
+ if( data.flags & 1 )
+ {
+ if( std::abs( bulge ) < MIN_BULGE )
+ insertLine( seg_start, poly_start );
+ else
+ insertArc( seg_start, poly_start, bulge );
+ }
+
+ return;
+}
+
+
+void DXF2IDF::addPolyline(const DRW_Polyline& data )
+{
+ IDF_POINT poly_start;
+ IDF_POINT seg_start;
+ IDF_POINT seg_end;
+
+ if( !data.vertlist.empty() )
+ {
+ DRW_Vertex* vertex = data.vertlist[0];
+ seg_start.x = vertex->basePoint.x * m_scale;
+ seg_start.y = vertex->basePoint.y * m_scale;
+ poly_start = seg_start;
+ }
+
+ for( size_t i = 1; i < data.vertlist.size(); ++i )
+ {
+ DRW_Vertex* vertex = data.vertlist[i];
+ seg_end.x = vertex->basePoint.x * m_scale;
+ seg_end.y = vertex->basePoint.y * m_scale;
+ insertLine( seg_start, seg_end );
+ seg_start = seg_end;
+ }
+
+ // Polyline flags bit 0 indicates closed (1) or open (0) polyline
+ if( data.flags & 1 )
+ insertLine( seg_start, poly_start );
+
+ return;
+}
+
+
+void DXF2IDF::insertLine( const IDF_POINT& aSegStart, const IDF_POINT& aSegEnd )
+{
+ IDF_SEGMENT* seg = new IDF_SEGMENT( aSegStart, aSegEnd );
+
+ if( seg )
+ lines.push_back( seg );
+
+ return;
+}
+
+
+void DXF2IDF::insertArc( const IDF_POINT& aSegStart, const IDF_POINT& aSegEnd,
+ double aBulge )
+{
+ if( aBulge < -MAX_BULGE )
+ aBulge = -MAX_BULGE;
+ else if( aBulge > MAX_BULGE )
+ aBulge = MAX_BULGE;
+
+ double ang = 720.0 * atan( aBulge ) / M_PI;
+
+ IDF_SEGMENT* seg = new IDF_SEGMENT( aSegStart, aSegEnd, ang, false );
+
+ if( seg )
+ lines.push_back( seg );
+
+ return;
+}
diff --git a/utils/idftools/dxf2idf.h b/utils/idftools/dxf2idf.h
new file mode 100644
index 0000000..2eb8ead
--- /dev/null
+++ b/utils/idftools/dxf2idf.h
@@ -0,0 +1,102 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2014 Cirilo Bernardo
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifndef DXF2IDF_H
+#define DXF2IDF_H
+
+#include <string>
+#include <drw_interface.h>
+#include <idf_common.h>
+
+class DXF2IDF : public DRW_Interface
+{
+private:
+ std::list< IDF_SEGMENT* > lines; // Unsorted list of graphical segments
+ double m_scale; // scaling factor to mm
+
+ void insertLine( const IDF_POINT& aSegStart, const IDF_POINT& aSegEnd );
+ void insertArc( const IDF_POINT& aSegStart, const IDF_POINT& aSegEnd, double aBulge );
+
+public:
+ ~DXF2IDF();
+
+ bool ReadDxf( const std::string aFile );
+ bool WriteOutline( FILE* aFile, bool isInch );
+
+private:
+ // DRW_Interface implemented callback functions
+ virtual void addHeader( const DRW_Header* data );
+ virtual void addLine(const DRW_Line& data);
+ virtual void addArc(const DRW_Arc& data );
+ virtual void addCircle(const DRW_Circle& data );
+ virtual void addLWPolyline(const DRW_LWPolyline& data );
+ virtual void addPolyline(const DRW_Polyline& data );
+
+ // DRW_Interface callbacks unsupported by DXF2IDF
+ virtual void addLType( const DRW_LType& data ){}
+ virtual void addLayer( const DRW_Layer& data ){}
+ virtual void addDimStyle( const DRW_Dimstyle& data ){}
+ virtual void addVport(const DRW_Vport& data){}
+ virtual void addTextStyle(const DRW_Textstyle& data){}
+ virtual void addBlock(const DRW_Block& data ){}
+ virtual void setBlock(const int handle){}
+ virtual void endBlock(){}
+ virtual void addPoint(const DRW_Point& data ){}
+ virtual void addRay(const DRW_Ray& data ){}
+ virtual void addXline(const DRW_Xline& data ){}
+ virtual void addEllipse(const DRW_Ellipse& data ){}
+ virtual void addSpline(const DRW_Spline* data ){}
+ virtual void addKnot(const DRW_Entity&){}
+ virtual void addInsert(const DRW_Insert& data ){}
+ virtual void addTrace(const DRW_Trace& data ){}
+ virtual void add3dFace(const DRW_3Dface& data ){}
+ virtual void addSolid(const DRW_Solid& data ){}
+ virtual void addMText(const DRW_MText& data){}
+ virtual void addText(const DRW_Text& data ){}
+ virtual void addDimAlign(const DRW_DimAligned *data ){}
+ virtual void addDimLinear(const DRW_DimLinear *data ){}
+ virtual void addDimRadial(const DRW_DimRadial *data ){}
+ virtual void addDimDiametric(const DRW_DimDiametric *data ){}
+ virtual void addDimAngular(const DRW_DimAngular *data ){}
+ virtual void addDimAngular3P(const DRW_DimAngular3p *data ){}
+ virtual void addDimOrdinate(const DRW_DimOrdinate *data ){}
+ virtual void addLeader(const DRW_Leader *data ){}
+ virtual void addHatch(const DRW_Hatch* data ){}
+ virtual void addViewport(const DRW_Viewport& data){}
+ virtual void addImage(const DRW_Image* data ){}
+ virtual void linkImage(const DRW_ImageDef* data ){}
+ virtual void addComment(const char*){}
+ virtual void writeHeader(DRW_Header& data){}
+ virtual void writeBlocks(){}
+ virtual void writeBlockRecords(){}
+ virtual void writeEntities(){}
+ virtual void writeLTypes(){}
+ virtual void writeLayers(){}
+ virtual void writeTextstyles(){}
+ virtual void writeVports(){}
+ virtual void writeDimstyles(){}
+ virtual void addAppId( const DRW_AppId& data ) {}
+ virtual void writeAppId() {}
+};
+
+#endif // DXF2IDF_H
diff --git a/utils/idftools/dxf2idfmain.cpp b/utils/idftools/dxf2idfmain.cpp
new file mode 100644
index 0000000..df804c1
--- /dev/null
+++ b/utils/idftools/dxf2idfmain.cpp
@@ -0,0 +1,190 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2014 Cirilo Bernardo
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <cstdio>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <list>
+#include <dxf2idf.h>
+
+using namespace std;
+
+int main( int argc, char **argv )
+{
+ list< string > comments;
+ string line;
+ stringstream tstr;
+
+ string dname; // DXF filename
+ string gname; // Geometry Name
+ string pname; // Part Name
+ double height; // extrusion height
+ bool inch = false; // true = inches, false = mm
+ bool ok;
+
+ if( argc == 1 )
+ {
+ // no arguments; print out usage information
+ cout << "dxf2idf: this program takes line, arc, and circle segments\n";
+ cout << " from a DXF file and creates an IDF component outline file.\n\n";
+ cout << "Input:\n";
+ cout << " DXF filename: the input file, must end in '.dxf'\n";
+ cout << " Units: mm, in (millimeters or inches)\n";
+ cout << " Geometry Name: string, as per IDF version 3.0 specification\n";
+ cout << " Part Name: as per IDF version 3.0 specification of Part Number\n";
+ cout << " Height: extruded height of the outline\n";
+ cout << " Comments: all non-empty lines are comments to be added to\n";
+ cout << " the IDF file. An empty line signifies the end of\n";
+ cout << " the comment block.\n";
+ cout << " File name: output filename, must end in '.idf'\n\n";
+ }
+
+ line.clear();
+ while( line.empty() || line.find( ".dxf" ) == string::npos )
+ {
+ cout << "* DXF filename: ";
+
+ line.clear();
+ std::getline( cin, line );
+ }
+ dname = line;
+
+ line.clear();
+ while( line.compare( "mm" ) && line.compare( "in" )
+ && line.compare( "MM" ) && line.compare( "IN" ) )
+ {
+ cout << "* Units (mm,in): ";
+ line.clear();
+ std::getline( cin, line );
+ }
+
+ if( line.compare( "mm" ) && line.compare( "MM" ) )
+ inch = true;
+
+ line.clear();
+ while( line.empty() )
+ {
+ cout << "* Geometry name: ";
+ line.clear();
+ std::getline( cin, line );
+
+ if( line.find( "\"" ) != string::npos )
+ {
+ cerr << "[INFO] geometry name may not contain quotation marks\n";
+ line.clear();
+ }
+ }
+ gname = line;
+
+ line.clear();
+ while( line.empty() )
+ {
+ cout << "* Part name: ";
+ line.clear();
+ std::getline( cin, line );
+
+ if( line.find( "\"" ) != string::npos )
+ {
+ cerr << "[INFO] part name may not contain quotation marks\n";
+ line.clear();
+ }
+ }
+ pname = line;
+
+ ok = false;
+ while( !ok )
+ {
+ cout << "* Height: ";
+
+ line.clear();
+ std::getline( cin, line );
+
+ tstr.clear();
+ tstr.str( line );
+
+ tstr >> height;
+ if( !tstr.fail() && height > 0.001 )
+ ok = true;
+ }
+
+ cout << "* COMMENTS: any non-blank line is a comment;\n";
+ cout << " a blank line signifies the end of comments.\n";
+ ok = false;
+ while( !ok )
+ {
+ line.clear();
+ std::getline( cin, line );
+
+ if( line.empty() )
+ {
+ ok = true;
+ }
+ else
+ {
+ if( line[0] != '#' )
+ line.insert( 0, "# " );
+
+ comments.push_back( line );
+ }
+ }
+
+ line.clear();
+ while( line.empty() || line.find( ".idf" ) == string::npos )
+ {
+ cout << "* File name (*.idf): ";
+
+ line.clear();
+ std::getline( cin, line );
+ }
+
+ DXF2IDF dxf;
+
+ dxf.ReadDxf( dname.c_str() );
+
+ FILE* fp = fopen( line.c_str(), "w" );
+
+ list< string >::const_iterator scom = comments.begin();
+ list< string >::const_iterator ecom = comments.end();
+
+ while( scom != ecom )
+ {
+ fprintf( fp, "%s\n", (*scom).c_str() );
+ ++scom;
+ }
+
+ fprintf( fp, ".ELECTRICAL\n" );
+
+ if( inch )
+ fprintf( fp, "\"%s\" \"%s\" THOU %d\n", gname.c_str(),
+ pname.c_str(), (int) (height * 1000.0) );
+ else
+ fprintf( fp, "\"%s\" \"%s\" MM %.3f\n", gname.c_str(),
+ pname.c_str(), height );
+
+ dxf.WriteOutline( fp, inch );
+
+ fprintf( fp, ".END_ELECTRICAL\n" );
+
+ return 0;
+} \ No newline at end of file
diff --git a/utils/idftools/idf2vrml.cpp b/utils/idftools/idf2vrml.cpp
new file mode 100644
index 0000000..df66e4b
--- /dev/null
+++ b/utils/idftools/idf2vrml.cpp
@@ -0,0 +1,990 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2014 Cirilo Bernardo
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/*
+ * This program takes an IDF base name, loads the board outline
+ * and component outine files, and creates a single VRML file.
+ * The VRML file can be used to visually verify the IDF files
+ * before sending them to a mechanical designer. The output scale
+ * is 10:1; this scale was chosen because VRML was originally
+ * intended to describe large virtual worlds and rounding errors
+ * would be more likely if we used a 1:1 scale.
+ */
+
+
+#include <iostream>
+#include <iomanip>
+#include <fstream>
+#include <string>
+#include <sstream>
+#include <cmath>
+#include <cstdio>
+#include <cerrno>
+#include <list>
+#include <utility>
+#include <clocale>
+#include <vector>
+#include <cstdlib>
+#include <cstring>
+#include <algorithm>
+#include <libgen.h>
+#include <unistd.h>
+#include <boost/ptr_container/ptr_map.hpp>
+
+#include <idf_helpers.h>
+#include <idf_common.h>
+#include <idf_parser.h>
+#include <vrml_layer.h>
+
+#ifndef MIN_ANG
+#define MIN_ANG 0.01
+#endif
+
+extern char* optarg;
+extern int optopt;
+
+using namespace std;
+using namespace boost;
+
+#define CLEANUP do { \
+setlocale( LC_ALL, "C" ); \
+} while( 0 );
+
+// define colors
+struct VRML_COLOR
+{
+ double diff[3];
+ double emis[3];
+ double spec[3];
+ double ambi;
+ double tran;
+ double shin;
+};
+
+struct VRML_IDS
+{
+ int colorIndex;
+ std::string objectName;
+ bool used;
+ bool bottom;
+ double dX, dY, dZ, dA;
+
+ VRML_IDS()
+ {
+ colorIndex = 0;
+ used = false;
+ bottom = false;
+ dX = 0.0;
+ dY = 0.0;
+ dZ = 0.0;
+ dA = 0.0;
+ }
+};
+
+#define NCOLORS 7
+VRML_COLOR colors[NCOLORS] =
+{
+ { { 0, 0.82, 0.247 }, { 0, 0, 0 }, { 0, 0.82, 0.247 }, 0.9, 0, 0.1 },
+ { { 1, 0, 0 }, { 1, 0, 0 }, { 1, 0, 0 }, 0.9, 0, 0.1 },
+ { { 0.659, 0, 0.463 }, { 0, 0, 0 }, { 0.659, 0, 0.463 }, 0.9, 0, 0.1 },
+ { { 0.659, 0.294, 0 }, { 0, 0, 0 }, { 0.659, 0.294, 0 }, 0.9, 0, 0.1 },
+ { { 0, 0.918, 0.659 }, { 0, 0, 0 }, { 0, 0.918, 0.659 }, 0.9, 0, 0.1 },
+ { { 0.808, 0.733, 0.071 }, { 0, 0, 0 }, { 0.808, 0.733 , 0.071 }, 0.9, 0, 0.1 },
+ { { 0.102, 1, 0.984 }, { 0, 0, 0 }, { 0.102, 1, 0.984 }, 0.9, 0, 0.1 }
+};
+
+bool WriteHeader( IDF3_BOARD& board, std::ofstream& file );
+bool MakeBoard( IDF3_BOARD& board, std::ofstream& file );
+bool MakeComponents( IDF3_BOARD& board, std::ofstream& file, bool compact );
+bool MakeOtherOutlines( IDF3_BOARD& board, std::ofstream& file );
+bool PopulateVRML( VRML_LAYER& model, const std::list< IDF_OUTLINE* >* items, bool bottom,
+ double scale, double dX = 0.0, double dY = 0.0, double angle = 0.0 );
+bool AddSegment( VRML_LAYER& model, IDF_SEGMENT* seg, int icont, int iseg );
+bool WriteTriangles( std::ofstream& file, VRML_IDS* vID, VRML_LAYER* layer, bool plane,
+ bool top, double top_z, double bottom_z, int precision, bool compact );
+inline void TransformPoint( IDF_SEGMENT& seg, double frac, bool bottom,
+ double dX, double dY, double angle );
+VRML_IDS* GetColor( boost::ptr_map<const std::string, VRML_IDS>& cmap,
+ int& index, const std::string& uid );
+
+
+void PrintUsage( void )
+{
+ cout << "-\nUsage: idf2vrml -f input_file.emn -s scale_factor {-k} {-d} {-z} {-m}\n";
+ cout << "flags:\n";
+ cout << " -k: produce KiCad-friendly VRML output; default is compact VRML\n";
+ cout << " -d: suppress substitution of default outlines\n";
+ cout << " -z: suppress rendering of zero-height outlines\n";
+ cout << " -m: print object mapping to stdout for debugging purposes\n";
+ cout << "example to produce a model for use by KiCad: idf2vrml -f input.emn -s 0.3937008 -k\n\n";
+ return;
+}
+
+bool nozeroheights;
+bool showObjectMapping;
+
+int main( int argc, char **argv )
+{
+ // IDF implicitly requires the C locale
+ setlocale( LC_ALL, "C" );
+
+ // Essential inputs:
+ // 1. IDF file
+ // 2. Output scale: internal IDF units are mm, so 1 = 1mm per VRML unit,
+ // 0.1 = 1cm per VRML unit, 0.01 = 1m per VRML unit,
+ // 1/25.4 = 1in per VRML unit, 1/2.54 = 0.1in per VRML unit (KiCad model)
+ // 3. KiCad-friendly output (do not reuse features via DEF+USE)
+ // Render each component to VRML; if the user wants
+ // a KiCad friendly output then we must avoid DEF+USE;
+ // otherwise we employ DEF+USE to minimize file size
+
+ std::string inputFilename;
+ double scaleFactor = 1.0;
+ bool compact = true;
+ bool nooutlinesubs = false;
+ int ichar;
+
+ nozeroheights = false;
+ showObjectMapping = false;
+
+ while( ( ichar = getopt( argc, argv, ":f:s:kdzm" ) ) != -1 )
+ {
+ switch( ichar )
+ {
+ case 'f':
+ inputFilename = optarg;
+ break;
+
+ case 's':
+ do
+ {
+ errno = 0;
+ char* cp = NULL;
+ scaleFactor = strtod( optarg, &cp );
+
+ if( errno || cp == optarg )
+ {
+ cerr << "* invalid scale factor: '" << optarg << "'\n";
+ return -1;
+ }
+
+ if( scaleFactor < 0.001 || scaleFactor > 10 )
+ {
+ cerr << "* scale factor out of range (" << scaleFactor << "); range is 0.001 to 10.0\n";
+ return -1;
+ }
+
+ } while( 0 );
+ break;
+
+ case 'k':
+ compact = false;
+ break;
+
+ case 'd':
+ nooutlinesubs = true;
+ break;
+
+ case 'z':
+ nozeroheights = true;
+ break;
+
+ case 'm':
+ showObjectMapping = true;
+ break;
+
+ case ':':
+ cerr << "* Missing parameter to option '-" << ((char) optopt) << "'\n";
+ PrintUsage();
+ return -1;
+ break;
+
+ default:
+ cerr << "* Unexpected option: '-";
+
+ if( ichar == '?' )
+ cerr << ((char) optopt) << "'\n";
+ else
+ cerr << ((char) ichar) << "'\n";
+
+ PrintUsage();
+ return -1;
+ break;
+ }
+ }
+
+ if( inputFilename.empty() )
+ {
+ cerr << "* no IDF filename supplied\n";
+ PrintUsage();
+ return -1;
+ }
+
+ IDF3_BOARD pcb( IDF3::CAD_ELEC );
+
+ cout << "** Reading file: " << inputFilename << "\n";
+
+ if( !pcb.ReadFile( FROM_UTF8( inputFilename.c_str() ), nooutlinesubs ) )
+ {
+ cerr << "** Failed to read IDF data:\n";
+ cerr << pcb.GetError() << "\n\n";
+
+ return -1;
+ }
+
+ // set the scale and output precision ( scale 1 == precision 5)
+ pcb.SetUserScale( scaleFactor );
+
+ if( scaleFactor < 0.01 )
+ pcb.SetUserPrecision( 8 );
+ else if( scaleFactor < 0.1 )
+ pcb.SetUserPrecision( 7 );
+ else if( scaleFactor < 1.0 )
+ pcb.SetUserPrecision( 6 );
+ else if( scaleFactor < 10.0 )
+ pcb.SetUserPrecision( 5 );
+ else
+ pcb.SetUserPrecision( 4 );
+
+ // Create the VRML file and write the header
+ char* bnp = (char*) malloc( inputFilename.size() + 1 );
+ strcpy( bnp, inputFilename.c_str() );
+
+ std::string fname = basename( bnp );
+ free( bnp );
+ std::string::iterator itf = fname.end();
+ *(--itf) = 'l';
+ *(--itf) = 'r';
+ *(--itf) = 'w';
+
+ cout << "Writing file: '" << fname << "'\n";
+
+ std::ofstream ofile;
+ ofile.open( fname.c_str(), std::ios_base::out );
+
+ ofile << fixed; // do not use exponents in VRML output
+ WriteHeader( pcb, ofile );
+
+ // STEP 1: Render the PCB alone
+ MakeBoard( pcb, ofile );
+
+ // STEP 2: Render the components
+ MakeComponents( pcb, ofile, compact );
+
+ // STEP 3: Render the OTHER outlines
+ MakeOtherOutlines( pcb, ofile );
+
+ ofile << "]\n}\n";
+ ofile.close();
+
+ // restore the locale
+ setlocale( LC_ALL, "" );
+ return 0;
+}
+
+
+bool WriteHeader( IDF3_BOARD& board, std::ofstream& file )
+{
+ std::string bname = board.GetBoardName();
+
+ if( bname.empty() )
+ {
+ bname = "BoardWithNoName";
+ }
+ else
+ {
+ std::string::iterator ss = bname.begin();
+ std::string::iterator se = bname.end();
+
+ while( ss != se )
+ {
+ if( *ss == '/' || *ss == ' ' || *ss == ':' )
+ *ss = '_';
+
+ ++ss;
+ }
+ }
+
+ file << "#VRML V2.0 utf8\n\n";
+ file << "WorldInfo {\n";
+ file << " title \"" << bname << "\"\n}\n\n";
+ file << "Transform {\n";
+ file << "children [\n";
+
+ return !file.fail();
+}
+
+
+bool MakeBoard( IDF3_BOARD& board, std::ofstream& file )
+{
+ VRML_LAYER vpcb;
+
+ if( board.GetBoardOutlinesSize() < 1 )
+ {
+ ERROR_IDF << "\n";
+ cerr << "* Cannot proceed; no board outline in IDF object\n";
+ return false;
+ }
+
+ double scale = board.GetUserScale();
+
+ // set the arc parameters according to output scale
+ int tI;
+ double tMin, tMax;
+ vpcb.GetArcParams( tI, tMin, tMax );
+ vpcb.SetArcParams( tI, tMin * scale, tMax * scale );
+
+ if( !PopulateVRML( vpcb, board.GetBoardOutline()->GetOutlines(), false, board.GetUserScale() ) )
+ {
+ return false;
+ }
+
+ vpcb.EnsureWinding( 0, false );
+
+ int nvcont = vpcb.GetNContours() - 1;
+
+ while( nvcont > 0 )
+ vpcb.EnsureWinding( nvcont--, true );
+
+ // Add the drill holes
+ const std::list<IDF_DRILL_DATA*>* drills = &board.GetBoardDrills();
+
+ std::list<IDF_DRILL_DATA*>::const_iterator sd = drills->begin();
+ std::list<IDF_DRILL_DATA*>::const_iterator ed = drills->end();
+
+ while( sd != ed )
+ {
+ vpcb.AddCircle( (*sd)->GetDrillXPos() * scale, (*sd)->GetDrillYPos() * scale,
+ (*sd)->GetDrillDia() * scale / 2.0, true );
+ ++sd;
+ }
+
+ std::map< std::string, IDF3_COMPONENT* >*const comp = board.GetComponents();
+ std::map< std::string, IDF3_COMPONENT* >::const_iterator sc = comp->begin();
+ std::map< std::string, IDF3_COMPONENT* >::const_iterator ec = comp->end();
+
+ while( sc != ec )
+ {
+ drills = sc->second->GetDrills();
+ sd = drills->begin();
+ ed = drills->end();
+
+ while( sd != ed )
+ {
+ vpcb.AddCircle( (*sd)->GetDrillXPos() * scale, (*sd)->GetDrillYPos() * scale,
+ (*sd)->GetDrillDia() * scale / 2.0, true );
+ ++sd;
+ }
+
+ ++sc;
+ }
+
+ // tesselate and write out
+ vpcb.Tesselate( NULL );
+
+ double thick = board.GetBoardThickness() / 2.0 * scale;
+
+ VRML_IDS tvid;
+ tvid.colorIndex = 0;
+
+ WriteTriangles( file, &tvid, &vpcb, false, false,
+ thick, -thick, board.GetUserPrecision(), false );
+
+ return true;
+}
+
+bool PopulateVRML( VRML_LAYER& model, const std::list< IDF_OUTLINE* >* items, bool bottom, double scale,
+ double dX, double dY, double angle )
+{
+ // empty outlines are not unusual so we fail quietly
+ if( items->size() < 1 )
+ return false;
+
+ int nvcont = 0;
+ int iseg = 0;
+
+ std::list< IDF_OUTLINE* >::const_iterator scont = items->begin();
+ std::list< IDF_OUTLINE* >::const_iterator econt = items->end();
+ std::list<IDF_SEGMENT*>::iterator sseg;
+ std::list<IDF_SEGMENT*>::iterator eseg;
+
+ IDF_SEGMENT lseg;
+
+ while( scont != econt )
+ {
+ nvcont = model.NewContour();
+
+ if( nvcont < 0 )
+ {
+ ERROR_IDF << "\n";
+ cerr << "* cannot create an outline\n";
+ return false;
+ }
+
+ if( (*scont)->size() < 1 )
+ {
+ ERROR_IDF << "invalid contour: no vertices\n";
+ return false;
+ }
+
+ sseg = (*scont)->begin();
+ eseg = (*scont)->end();
+
+ iseg = 0;
+ while( sseg != eseg )
+ {
+ lseg = **sseg;
+ TransformPoint( lseg, scale, bottom, dX, dY, angle );
+
+ if( !AddSegment( model, &lseg, nvcont, iseg ) )
+ return false;
+
+ ++iseg;
+ ++sseg;
+ }
+
+ ++scont;
+ }
+
+ return true;
+}
+
+
+bool AddSegment( VRML_LAYER& model, IDF_SEGMENT* seg, int icont, int iseg )
+{
+ // note: in all cases we must add all but the last point in the segment
+ // to avoid redundant points
+
+ if( seg->angle != 0.0 )
+ {
+ if( seg->IsCircle() )
+ {
+ if( iseg != 0 )
+ {
+ ERROR_IDF << "adding a circle to an existing vertex list\n";
+ return false;
+ }
+
+ return model.AppendCircle( seg->center.x, seg->center.y, seg->radius, icont );
+ }
+ else
+ {
+ return model.AppendArc( seg->center.x, seg->center.y, seg->radius,
+ seg->offsetAngle, seg->angle, icont );
+ }
+ }
+
+ if( !model.AddVertex( icont, seg->startPoint.x, seg->startPoint.y ) )
+ return false;
+
+ return true;
+}
+
+
+bool WriteTriangles( std::ofstream& file, VRML_IDS* vID, VRML_LAYER* layer, bool plane,
+ bool top, double top_z, double bottom_z, int precision, bool compact )
+{
+ if( vID == NULL || layer == NULL )
+ return false;
+
+ file << "Transform {\n";
+
+ if( compact && !vID->objectName.empty() )
+ {
+ file << "translation " << setprecision( precision ) << vID->dX;
+ file << " " << vID->dY << " ";
+
+ if( vID->bottom )
+ {
+ file << -vID->dZ << "\n";
+
+ double tx, ty;
+
+ // calculate the rotation axis and angle
+ tx = cos( M_PI2 - vID->dA / 2.0 );
+ ty = sin( M_PI2 - vID->dA / 2.0 );
+
+ file << "rotation " << setprecision( precision );
+ file << tx << " " << ty << " 0 ";
+ file << setprecision(5) << M_PI << "\n";
+ }
+ else
+ {
+ file << vID->dZ << "\n";
+ file << "rotation 0 0 1 " << setprecision(5) << vID->dA << "\n";
+ }
+
+ file << "children [\n";
+
+ if( vID->used )
+ {
+ file << "USE " << vID->objectName << "\n";
+ file << "]\n";
+ file << "}\n";
+ return true;
+ }
+
+ file << "DEF " << vID->objectName << " Transform {\n";
+
+ if( !plane && top_z <= bottom_z )
+ {
+ // the height specification is faulty; make the component
+ // a bright red to highlight it
+ vID->colorIndex = 1;
+ // we don't know the scale, but 5 units is huge in most situations
+ top_z = bottom_z + 5.0;
+ }
+
+ }
+
+ VRML_COLOR* color = &colors[vID->colorIndex];
+
+ vID->used = true;
+
+ file << "children [\n";
+ file << "Group {\n";
+ file << "children [\n";
+ file << "Shape {\n";
+ file << "appearance Appearance {\n";
+ file << "material Material {\n";
+
+ // material definition
+ file << "diffuseColor " << setprecision(3) << color->diff[0] << " ";
+ file << color->diff[1] << " " << color->diff[2] << "\n";
+ file << "specularColor " << color->spec[0] << " " << color->spec[1];
+ file << " " << color->spec[2] << "\n";
+ file << "emissiveColor " << color->emis[0] << " " << color->emis[1];
+ file << " " << color->emis[2] << "\n";
+ file << "ambientIntensity " << color->ambi << "\n";
+ file << "transparency " << color->tran << "\n";
+ file << "shininess " << color->shin << "\n";
+
+ file << "}\n";
+ file << "}\n";
+ file << "geometry IndexedFaceSet {\n";
+ file << "solid TRUE\n";
+ file << "coord Coordinate {\n";
+ file << "point [\n";
+
+ // Coordinates (vertices)
+ if( plane )
+ {
+ if( !layer->WriteVertices( top_z, file, precision ) )
+ {
+ cerr << "* errors writing planar vertices to " << vID->objectName << "\n";
+ cerr << "** " << layer->GetError() << "\n";
+ }
+ }
+ else
+ {
+ if( !layer->Write3DVertices( top_z, bottom_z, file, precision ) )
+ {
+ cerr << "* errors writing 3D vertices to " << vID->objectName << "\n";
+ cerr << "** " << layer->GetError() << "\n";
+ }
+ }
+
+ file << "\n";
+
+ file << "]\n";
+ file << "}\n";
+ file << "coordIndex [\n";
+
+ // Indices
+ if( plane )
+ layer->WriteIndices( top, file );
+ else
+ layer->Write3DIndices( file );
+
+ file << "\n";
+ file << "]\n";
+ file << "}\n";
+ file << "}\n";
+ file << "]\n";
+ file << "}\n";
+ file << "]\n";
+ file << "}\n";
+
+ if( compact && !vID->objectName.empty() )
+ {
+ file << "]\n";
+ file << "}\n";
+ }
+
+ return !file.fail();
+}
+
+inline void TransformPoint( IDF_SEGMENT& seg, double frac, bool bottom,
+ double dX, double dY, double angle )
+{
+ dX *= frac;
+ dY *= frac;
+
+ if( bottom )
+ {
+ // mirror points on the Y axis
+ seg.startPoint.x = -seg.startPoint.x;
+ seg.endPoint.x = -seg.endPoint.x;
+ seg.center.x = -seg.center.x;
+ angle = -angle;
+ }
+
+ seg.startPoint.x *= frac;
+ seg.startPoint.y *= frac;
+ seg.endPoint.x *= frac;
+ seg.endPoint.y *= frac;
+ seg.center.x *= frac;
+ seg.center.y *= frac;
+
+ double tsin = 0.0;
+ double tcos = 0.0;
+
+ if( angle > MIN_ANG || angle < -MIN_ANG )
+ {
+ double ta = angle * M_PI / 180.0;
+ double tx, ty;
+
+ tsin = sin( ta );
+ tcos = cos( ta );
+
+ tx = seg.startPoint.x * tcos - seg.startPoint.y * tsin;
+ ty = seg.startPoint.x * tsin + seg.startPoint.y * tcos;
+ seg.startPoint.x = tx;
+ seg.startPoint.y = ty;
+
+ tx = seg.endPoint.x * tcos - seg.endPoint.y * tsin;
+ ty = seg.endPoint.x * tsin + seg.endPoint.y * tcos;
+ seg.endPoint.x = tx;
+ seg.endPoint.y = ty;
+
+ if( seg.angle != 0 )
+ {
+ tx = seg.center.x * tcos - seg.center.y * tsin;
+ ty = seg.center.x * tsin + seg.center.y * tcos;
+ seg.center.x = tx;
+ seg.center.y = ty;
+ }
+ }
+
+ seg.startPoint.x += dX;
+ seg.startPoint.y += dY;
+ seg.endPoint.x += dX;
+ seg.endPoint.y += dY;
+ seg.center.x += dX;
+ seg.center.y += dY;
+
+ if( seg.angle != 0 )
+ {
+ seg.radius *= frac;
+
+ if( bottom )
+ {
+ if( !seg.IsCircle() )
+ {
+ seg.angle = -seg.angle;
+ if( seg.offsetAngle > 0.0 )
+ seg.offsetAngle = 180 - seg.offsetAngle;
+ else
+ seg.offsetAngle = -seg.offsetAngle - 180;
+ }
+ }
+
+ if( angle > MIN_ANG || angle < -MIN_ANG )
+ seg.offsetAngle += angle;
+ }
+
+ return;
+}
+
+bool MakeComponents( IDF3_BOARD& board, std::ofstream& file, bool compact )
+{
+ int cidx = 2; // color index; start at 2 since 0,1 are special (board, NOGEOM_NOPART)
+
+ VRML_LAYER vpcb;
+
+ double scale = board.GetUserScale();
+ double thick = board.GetBoardThickness() / 2.0;
+
+ // set the arc parameters according to output scale
+ int tI;
+ double tMin, tMax;
+ vpcb.GetArcParams( tI, tMin, tMax );
+ vpcb.SetArcParams( tI, tMin * scale, tMax * scale );
+
+ // Add the component outlines
+ const std::map< std::string, IDF3_COMPONENT* >*const comp = board.GetComponents();
+ std::map< std::string, IDF3_COMPONENT* >::const_iterator sc = comp->begin();
+ std::map< std::string, IDF3_COMPONENT* >::const_iterator ec = comp->end();
+
+ std::list< IDF3_COMP_OUTLINE_DATA* >::const_iterator so;
+ std::list< IDF3_COMP_OUTLINE_DATA* >::const_iterator eo;
+
+ double vX, vY, vA;
+ double tX, tY, tZ, tA;
+ double top, bot;
+ bool bottom;
+ IDF3::IDF_LAYER lyr;
+
+ boost::ptr_map< const std::string, VRML_IDS> cmap; // map colors by outline UID
+ VRML_IDS* vcp;
+ IDF3_COMP_OUTLINE* pout;
+
+ while( sc != ec )
+ {
+ sc->second->GetPosition( vX, vY, vA, lyr );
+
+ if( lyr == IDF3::LYR_BOTTOM )
+ bottom = true;
+ else
+ bottom = false;
+
+ so = sc->second->GetOutlinesData()->begin();
+ eo = sc->second->GetOutlinesData()->end();
+
+ while( so != eo )
+ {
+ if( (*so)->GetOutline()->GetThickness() < 0.00000001 && nozeroheights )
+ {
+ vpcb.Clear();
+ ++so;
+ continue;
+ }
+
+ (*so)->GetOffsets( tX, tY, tZ, tA );
+ tX += vX;
+ tY += vY;
+ tA += vA;
+
+ if( ( pout = (IDF3_COMP_OUTLINE*)((*so)->GetOutline()) ) )
+ {
+ vcp = GetColor( cmap, cidx, pout->GetUID() );
+ }
+ else
+ {
+ vpcb.Clear();
+ ++so;
+ continue;
+ }
+
+ if( !compact )
+ {
+ if( !PopulateVRML( vpcb, (*so)->GetOutline()->GetOutlines(), bottom,
+ board.GetUserScale(), tX, tY, tA ) )
+ {
+ return false;
+ }
+ }
+ else
+ {
+ if( !vcp->used && !PopulateVRML( vpcb, (*so)->GetOutline()->GetOutlines(), false,
+ board.GetUserScale() ) )
+ {
+ return false;
+ }
+
+ vcp->dX = tX * scale;
+ vcp->dY = tY * scale;
+ vcp->dZ = tZ * scale;
+ vcp->dA = tA * M_PI / 180.0;
+ }
+
+ if( !compact || !vcp->used )
+ {
+ vpcb.EnsureWinding( 0, false );
+
+ int nvcont = vpcb.GetNContours() - 1;
+
+ while( nvcont > 0 )
+ vpcb.EnsureWinding( nvcont--, true );
+
+ vpcb.Tesselate( NULL );
+ }
+
+ if( !compact )
+ {
+ if( bottom )
+ {
+ top = -thick - tZ;
+ bot = (top - (*so)->GetOutline()->GetThickness() ) * scale;
+ top *= scale;
+ }
+ else
+ {
+ bot = thick + tZ;
+ top = (bot + (*so)->GetOutline()->GetThickness() ) * scale;
+ bot *= scale;
+ }
+ }
+ else
+ {
+ bot = thick;
+ top = (bot + (*so)->GetOutline()->GetThickness() ) * scale;
+ bot *= scale;
+ }
+
+ vcp = GetColor( cmap, cidx, ((IDF3_COMP_OUTLINE*)((*so)->GetOutline()))->GetUID() );
+ vcp->bottom = bottom;
+
+ // note: this can happen because IDF allows some negative heights/thicknesses
+ if( bot > top )
+ std::swap( bot, top );
+
+ WriteTriangles( file, vcp, &vpcb, false,
+ false, top, bot, board.GetUserPrecision(), compact );
+
+ vpcb.Clear();
+ ++so;
+ }
+
+ ++sc;
+ }
+
+ return true;
+}
+
+
+VRML_IDS* GetColor( boost::ptr_map<const std::string, VRML_IDS>& cmap, int& index, const std::string& uid )
+{
+ static int refnum = 0;
+
+ if( index < 2 )
+ index = 2; // 0 and 1 are special (BOARD, UID=NOGEOM_NOPART)
+
+ boost::ptr_map<const std::string, VRML_IDS>::iterator cit = cmap.find( uid );
+
+ if( cit == cmap.end() )
+ {
+ VRML_IDS* id = new VRML_IDS;
+
+ if( !uid.compare( "NOGEOM_NOPART" ) )
+ id->colorIndex = 1;
+ else
+ id->colorIndex = index++;
+
+ std::ostringstream ostr;
+ ostr << "OBJECTn" << refnum++;
+ id->objectName = ostr.str();
+
+ if( showObjectMapping )
+ cout << "* " << ostr.str() << " = '" << uid << "'\n";
+
+ cmap.insert( uid, id );
+
+ if( index >= NCOLORS )
+ index = 2;
+
+ return id;
+ }
+
+ return cit->second;
+}
+
+
+bool MakeOtherOutlines( IDF3_BOARD& board, std::ofstream& file )
+{
+ int cidx = 2; // color index; start at 2 since 0,1 are special (board, NOGEOM_NOPART)
+
+ VRML_LAYER vpcb;
+
+ double scale = board.GetUserScale();
+ double thick = board.GetBoardThickness() / 2.0;
+
+ // set the arc parameters according to output scale
+ int tI;
+ double tMin, tMax;
+ vpcb.GetArcParams( tI, tMin, tMax );
+ vpcb.SetArcParams( tI, tMin * scale, tMax * scale );
+
+ // Add the component outlines
+ const std::map< std::string, OTHER_OUTLINE* >*const comp = board.GetOtherOutlines();
+ std::map< std::string, OTHER_OUTLINE* >::const_iterator sc = comp->begin();
+ std::map< std::string, OTHER_OUTLINE* >::const_iterator ec = comp->end();
+
+ double top, bot;
+ bool bottom;
+ int nvcont;
+
+ boost::ptr_map< const std::string, VRML_IDS> cmap; // map colors by outline UID
+ VRML_IDS* vcp;
+ OTHER_OUTLINE* pout;
+
+ while( sc != ec )
+ {
+ pout = sc->second;
+
+ if( pout->GetThickness() < 0.00000001 && nozeroheights )
+ {
+ vpcb.Clear();
+ ++sc;
+ continue;
+ }
+
+ vcp = GetColor( cmap, cidx, pout->GetOutlineIdentifier() );
+
+ if( !PopulateVRML( vpcb, pout->GetOutlines(), false,
+ board.GetUserScale(), 0, 0, 0 ) )
+ {
+ return false;
+ }
+
+ vpcb.EnsureWinding( 0, false );
+
+ nvcont = vpcb.GetNContours() - 1;
+
+ while( nvcont > 0 )
+ vpcb.EnsureWinding( nvcont--, true );
+
+ vpcb.Tesselate( NULL );
+
+ if( pout->GetSide() == IDF3::LYR_BOTTOM )
+ bottom = true;
+ else
+ bottom = false;
+
+ if( bottom )
+ {
+ top = -thick;
+ bot = ( top - pout->GetThickness() ) * scale;
+ top *= scale;
+ }
+ else
+ {
+ bot = thick;
+ top = (bot + pout->GetThickness() ) * scale;
+ bot *= scale;
+ }
+
+ // note: this can happen because IDF allows some negative heights/thicknesses
+ if( bot > top )
+ std::swap( bot, top );
+
+ vcp->bottom = bottom;
+ WriteTriangles( file, vcp, &vpcb, false,
+ false, top, bot, board.GetUserPrecision(), false );
+
+ vpcb.Clear();
+ ++sc;
+ }
+
+ return true;
+}
diff --git a/utils/idftools/idf_common.cpp b/utils/idftools/idf_common.cpp
new file mode 100644
index 0000000..8eedfa6
--- /dev/null
+++ b/utils/idftools/idf_common.cpp
@@ -0,0 +1,1387 @@
+/**
+ * file: idf_common.cpp
+ *
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2013-2014 Cirilo Bernardo
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+
+#include <list>
+#include <string>
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <iomanip>
+#include <cerrno>
+#include <cstdio>
+#include <cmath>
+#include <idf_common.h>
+#include <idf_helpers.h>
+
+using namespace IDF3;
+using namespace std;
+
+
+std::string source;
+std::string message;
+
+IDF_ERROR::IDF_ERROR( const char* aSourceFile,
+ const char* aSourceMethod,
+ int aSourceLine,
+ const std::string& aMessage ) throw()
+{
+ ostringstream ostr;
+
+ if( aSourceFile )
+ ostr << "* " << aSourceFile << ":";
+ else
+ ostr << "* [BUG: No Source File]:";
+
+ ostr << aSourceLine << ":";
+
+ if( aSourceMethod )
+ ostr << aSourceMethod << "(): ";
+ else
+ ostr << "[BUG: No Source Method]:\n* ";
+
+ ostr << aMessage;
+ message = ostr.str();
+
+ return;
+}
+
+
+IDF_ERROR::~IDF_ERROR() throw()
+{
+ return;
+}
+
+
+const char* IDF_ERROR::what() const throw()
+{
+ return message.c_str();
+}
+
+
+IDF_NOTE::IDF_NOTE()
+{
+ xpos = 0.0;
+ ypos = 0.0;
+ height = 0.0;
+ length = 0.0;
+}
+
+
+bool IDF_NOTE::readNote( std::ifstream& aBoardFile, IDF3::FILE_STATE& aBoardState,
+ IDF3::IDF_UNIT aBoardUnit )
+{
+ std::string iline; // the input line
+ bool isComment; // true if a line just read in is a comment line
+ std::streampos pos;
+ int idx = 0;
+ bool quoted = false;
+ std::string token;
+
+ // RECORD 2: X, Y, text Height, text Length, "TEXT"
+ while( !FetchIDFLine( aBoardFile, iline, isComment, pos ) && aBoardFile.good() );
+
+ if( ( !aBoardFile.good() && !aBoardFile.eof() ) || iline.empty() )
+ {
+ aBoardState = IDF3::FILE_INVALID;
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "problems reading board notes" ) );
+ }
+
+ if( isComment )
+ {
+ aBoardState = IDF3::FILE_INVALID;
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDFv3 file\n"
+ "* Violation of specification: comment within a section (NOTES)" ) );
+ }
+
+ idx = 0;
+ GetIDFString( iline, token, quoted, idx );
+
+ if( quoted )
+ {
+ aBoardState = IDF3::FILE_INVALID;
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDFv3 file\n"
+ "* Violation of specification: X position in NOTES section must not be in quotes" ) );
+ }
+
+ if( CompareToken( ".END_NOTES", token ) )
+ return false;
+
+ istringstream istr;
+ istr.str( token );
+
+ istr >> xpos;
+ if( istr.fail() )
+ {
+ aBoardState = IDF3::FILE_INVALID;
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDFv3 file\n"
+ "* Violation of specification: X position in NOTES section is not numeric" ) );
+ }
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ aBoardState = IDF3::FILE_INVALID;
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDFv3 file\n"
+ "* Violation of specification: Y position in NOTES section is missing" ) );
+ }
+
+ if( quoted )
+ {
+ aBoardState = IDF3::FILE_INVALID;
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDFv3 file\n"
+ "* Violation of specification: Y position in NOTES section must not be in quotes" ) );
+ }
+
+ istr.clear();
+ istr.str( token );
+
+ istr >> ypos;
+ if( istr.fail() )
+ {
+ aBoardState = IDF3::FILE_INVALID;
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDFv3 file\n"
+ "* Violation of specification: Y position in NOTES section is not numeric" ) );
+ }
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ aBoardState = IDF3::FILE_INVALID;
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDFv3 file\n"
+ "* Violation of specification: text height in NOTES section is missing" ) );
+ }
+
+ if( quoted )
+ {
+ aBoardState = IDF3::FILE_INVALID;
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDFv3 file\n"
+ "* Violation of specification: text height in NOTES section must not be in quotes" ) );
+ }
+
+ istr.clear();
+ istr.str( token );
+
+ istr >> height;
+ if( istr.fail() )
+ {
+ aBoardState = IDF3::FILE_INVALID;
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDFv3 file\n"
+ "* Violation of specification: text height in NOTES section is not numeric" ) );
+ }
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ aBoardState = IDF3::FILE_INVALID;
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDFv3 file\n"
+ "* Violation of specification: text length in NOTES section is missing" ) );
+ }
+
+ if( quoted )
+ {
+ aBoardState = IDF3::FILE_INVALID;
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDFv3 file\n"
+ "* Violation of specification: text length in NOTES section must not be in quotes" ) );
+ }
+
+ istr.clear();
+ istr.str( token );
+
+ istr >> length;
+ if( istr.fail() )
+ {
+ aBoardState = IDF3::FILE_INVALID;
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDFv3 file\n"
+ "* Violation of specification: text length in NOTES section is not numeric" ) );
+ }
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ aBoardState = IDF3::FILE_INVALID;
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDFv3 file\n"
+ "* Violation of specification: text value in NOTES section is missing" ) );
+ }
+
+ text = token;
+
+ if( aBoardUnit == UNIT_THOU )
+ {
+ xpos *= IDF_THOU_TO_MM;
+ ypos *= IDF_THOU_TO_MM;
+ height *= IDF_THOU_TO_MM;
+ length *= IDF_THOU_TO_MM;
+ }
+
+ return true;
+}
+
+
+bool IDF_NOTE::writeNote( std::ofstream& aBoardFile, IDF3::IDF_UNIT aBoardUnit )
+{
+ if( aBoardUnit == UNIT_THOU )
+ {
+ aBoardFile << setiosflags(ios::fixed) << setprecision(1)
+ << (xpos / IDF_THOU_TO_MM) << " "
+ << (ypos / IDF_THOU_TO_MM) << " "
+ << (height / IDF_THOU_TO_MM) << " "
+ << (length / IDF_THOU_TO_MM) << " ";
+ }
+ else
+ {
+ aBoardFile << setiosflags(ios::fixed) << setprecision(5)
+ << xpos << " " << ypos << " " << height << " " << length << " ";
+ }
+
+ aBoardFile << "\"" << text << "\"\n";
+
+ return !aBoardFile.bad();
+}
+
+
+void IDF_NOTE::SetText( const std::string& aText )
+{
+ text = aText;
+ return;
+}
+
+void IDF_NOTE::SetPosition( double aXpos, double aYpos )
+{
+ xpos = aXpos;
+ ypos = aYpos;
+ return;
+}
+
+void IDF_NOTE::SetSize( double aHeight, double aLength )
+{
+ height = aHeight;
+ length = aLength;
+ return;
+}
+
+const std::string& IDF_NOTE::GetText( void )
+{
+ return text;
+}
+
+void IDF_NOTE::GetPosition( double& aXpos, double& aYpos )
+{
+ aXpos = xpos;
+ aYpos = ypos;
+ return;
+}
+
+void IDF_NOTE::GetSize( double& aHeight, double& aLength )
+{
+ aHeight = height;
+ aLength = length;
+ return;
+}
+
+
+/*
+ * CLASS: IDF_DRILL_DATA
+ */
+IDF_DRILL_DATA::IDF_DRILL_DATA()
+{
+ dia = 0.0;
+ x = 0.0;
+ y = 0.0;
+ plating = NPTH;
+ kref = NOREFDES;
+ khole = MTG;
+ owner = UNOWNED;
+
+ return;
+}
+
+
+IDF_DRILL_DATA::IDF_DRILL_DATA( double aDrillDia, double aPosX, double aPosY,
+ IDF3::KEY_PLATING aPlating,
+ const std::string aRefDes,
+ const std::string aHoleType,
+ IDF3::KEY_OWNER aOwner )
+{
+ if( aDrillDia < 0.3 )
+ dia = 0.3;
+ else
+ dia = aDrillDia;
+
+ x = aPosX;
+ y = aPosY;
+ plating = aPlating;
+
+ if( !aRefDes.compare( "BOARD" ) )
+ {
+ kref = BOARD;
+ }
+ else if( aRefDes.empty() || !aRefDes.compare( "NOREFDES" ) )
+ {
+ kref = NOREFDES;
+ }
+ else if( !aRefDes.compare( "PANEL" ) )
+ {
+ kref = PANEL;
+ }
+ else
+ {
+ kref = REFDES;
+ refdes = aRefDes;
+ }
+
+ if( !aHoleType.compare( "PIN" ) )
+ {
+ khole = PIN;
+ }
+ else if( !aHoleType.compare( "VIA" ) )
+ {
+ khole = VIA;
+ }
+ else if( aHoleType.empty() || !aHoleType.compare( "MTG" ) )
+ {
+ khole = MTG;
+ }
+ else if( !aHoleType.compare( "TOOL" ) )
+ {
+ khole = TOOL;
+ }
+ else
+ {
+ khole = OTHER;
+ holetype = aHoleType;
+ }
+
+ owner = aOwner;
+} // IDF_DRILL_DATA::IDF_DRILL_DATA( ... )
+
+bool IDF_DRILL_DATA::Matches( double aDrillDia, double aPosX, double aPosY )
+{
+ double ddia = aDrillDia - dia;
+ IDF_POINT p1, p2;
+
+ p1.x = x;
+ p1.y = y;
+ p2.x = aPosX;
+ p2.y = aPosY;
+
+ if( ddia > -0.00001 && ddia < 0.00001 && p1.Matches( p2, 0.00001 ) )
+ return true;
+
+ return false;
+}
+
+bool IDF_DRILL_DATA::read( std::ifstream& aBoardFile, IDF3::IDF_UNIT aBoardUnit,
+ IDF3::FILE_STATE aBoardState, IDF3::IDF_VERSION aIdfVersion )
+{
+ std::string iline; // the input line
+ bool isComment; // true if a line just read in is a comment line
+ std::streampos pos;
+ int idx = 0;
+ bool quoted = false;
+ std::string token;
+
+ // RECORD 2: DIA, X, Y, Plating Style, REFDES, HOLE TYPE, HOLE OWNER
+ while( !FetchIDFLine( aBoardFile, iline, isComment, pos ) && aBoardFile.good() );
+
+ if( ( !aBoardFile.good() && !aBoardFile.eof() ) || iline.empty() )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "problems reading board drilled holes" ) );
+
+ if( isComment )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: comment within a section (DRILLED HOLES)" ) );
+
+ idx = 0;
+ GetIDFString( iline, token, quoted, idx );
+
+ if( quoted )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: drill diameter must not be in quotes" ) );
+
+ if( CompareToken( ".END_DRILLED_HOLES", token ) )
+ return false;
+
+ istringstream istr;
+ istr.str( token );
+
+ istr >> dia;
+ if( istr.fail() )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: drill diameter is not numeric" ) );
+
+ if( ( aBoardUnit == UNIT_MM && dia < IDF_MIN_DIA_MM )
+ || ( aBoardUnit == UNIT_THOU && dia < IDF_MIN_DIA_THOU )
+ || ( aBoardUnit == UNIT_TNM && dia < IDF_MIN_DIA_TNM ) )
+ {
+ ostringstream ostr;
+ ostr << "invalid IDF file\n";
+ ostr << "* Invalid drill diameter (too small): '" << token << "'";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: missing X position for drilled hole" ) );
+
+ if( quoted )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: X position in DRILLED HOLES section must not be in quotes" ) );
+
+ istr.clear();
+ istr.str( token );
+
+ istr >> x;
+ if( istr.fail() )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: X position in DRILLED HOLES section is not numeric" ) );
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: missing Y position for drilled hole" ) );
+
+ if( quoted )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: Y position in DRILLED HOLES section must not be in quotes" ) );
+
+ istr.clear();
+ istr.str( token );
+
+ istr >> y;
+ if( istr.fail() )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: Y position in DRILLED HOLES section is not numeric" ) );
+
+ if( aIdfVersion > IDF_V2 )
+ {
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDFv3 file\n"
+ "* Violation of specification: missing PLATING for drilled hole" ) );
+
+ if( CompareToken( "PTH", token ) )
+ {
+ plating = IDF3::PTH;
+ }
+ else if( CompareToken( "NPTH", token ) )
+ {
+ plating = IDF3::NPTH;
+ }
+ else
+ {
+ ostringstream ostr;
+ ostr << "invalid IDFv3 file\n";
+ ostr << "* Violation of specification: invalid PLATING type ('" << token << "')";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+ }
+ else
+ {
+ plating = IDF3::PTH;
+ }
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ if( aIdfVersion > IDF_V2 )
+ {
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDFv3 file\n"
+ "* Violation of specification: missing REFDES for drilled hole" ) );
+ }
+ else
+ {
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDFv2 file\n"
+ "* Violation of specification: missing HOLE TYPE for drilled hole" ) );
+ }
+ }
+
+ std::string tok1 = token;
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ if( aIdfVersion > IDF_V2 )
+ {
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDFv3 file\n"
+ "* Violation of specification: missing HOLE TYPE for drilled hole" ) );
+ }
+ else
+ {
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDFv2 file\n"
+ "* Violation of specification: missing REFDES for drilled hole" ) );
+ }
+ }
+
+ std::string tok2 = token;
+
+ if( aIdfVersion > IDF_V2 )
+ token = tok1;
+
+ if( CompareToken( "BOARD", token ) )
+ {
+ kref = IDF3::BOARD;
+ }
+ else if( CompareToken( "NOREFDES", token ) )
+ {
+ kref = IDF3::NOREFDES;
+ }
+ else if( CompareToken( "PANEL", token ) )
+ {
+ kref = IDF3::PANEL;
+ }
+ else
+ {
+ kref = IDF3::REFDES;
+ refdes = token;
+ }
+
+ if( aIdfVersion > IDF_V2 )
+ token = tok2;
+ else
+ token = tok1;
+
+ if( CompareToken( "PIN", token ) )
+ {
+ khole = IDF3::PIN;
+ }
+ else if( CompareToken( "VIA", token ) )
+ {
+ khole = IDF3::VIA;
+ }
+ else if( CompareToken( "MTG", token ) )
+ {
+ khole = IDF3::MTG;
+ }
+ else if( CompareToken( "TOOL", token ) )
+ {
+ khole = IDF3::TOOL;
+ }
+ else
+ {
+ khole = IDF3::OTHER;
+ holetype = token;
+ }
+
+ if( aIdfVersion > IDF_V2 )
+ {
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDFv3 file\n"
+ "* Violation of specification: missing OWNER for drilled hole" ) );
+
+ if( !ParseOwner( token, owner ) )
+ {
+ ostringstream ostr;
+ ostr << "invalid IDFv3 file\n";
+ ostr << "* Violation of specification: invalid OWNER for drilled hole ('" << token << "')";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+ }
+ else
+ {
+ owner = IDF3::UNOWNED;
+ }
+
+ if( aBoardUnit == UNIT_THOU )
+ {
+ dia *= IDF_THOU_TO_MM;
+ x *= IDF_THOU_TO_MM;
+ y *= IDF_THOU_TO_MM;
+ }
+ else if( ( aIdfVersion == IDF_V2 ) && ( aBoardUnit == UNIT_TNM ) )
+ {
+ dia *= IDF_TNM_TO_MM;
+ x *= IDF_TNM_TO_MM;
+ y *= IDF_TNM_TO_MM;
+ }
+ else if( aBoardUnit != UNIT_MM )
+ {
+ ostringstream ostr;
+ ostr << "\n* BUG: invalid UNIT type: " << aBoardUnit;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ return true;
+}
+
+void IDF_DRILL_DATA::write( std::ofstream& aBoardFile, IDF3::IDF_UNIT aBoardUnit )
+{
+ std::string holestr;
+ std::string refstr;
+ std::string ownstr;
+ std::string pltstr;
+
+ switch( khole )
+ {
+ case PIN:
+ holestr = "PIN";
+ break;
+
+ case VIA:
+ holestr = "VIA";
+ break;
+
+ case TOOL:
+ holestr = "TOOL";
+ break;
+
+ case OTHER:
+ holestr = "\"" + holetype + "\"";
+ break;
+
+ default:
+ holestr = "MTG";
+ break;
+ }
+
+ switch( kref )
+ {
+ case BOARD:
+ refstr = "BOARD";
+ break;
+
+ case PANEL:
+ refstr = "PANEL";
+ break;
+
+ case REFDES:
+ refstr = "\"" + refdes + "\"";
+ break;
+
+ default:
+ refstr = "NOREFDES";
+ break;
+ }
+
+ if( plating == PTH )
+ pltstr = "PTH";
+ else
+ pltstr = "NPTH";
+
+ switch( owner )
+ {
+ case MCAD:
+ ownstr = "MCAD";
+ break;
+
+ case ECAD:
+ ownstr = "ECAD";
+ break;
+
+ default:
+ ownstr = "UNOWNED";
+ break;
+ }
+
+ if( aBoardUnit == UNIT_MM )
+ {
+ aBoardFile << std::setiosflags( std::ios::fixed ) << std::setprecision( 3 ) << dia << " "
+ << std::setprecision( 5 ) << x << " " << y << " "
+ << pltstr.c_str() << " " << refstr.c_str() << " "
+ << holestr.c_str() << " " << ownstr.c_str() << "\n";
+ }
+ else
+ {
+ aBoardFile << std::setiosflags( std::ios::fixed ) << std::setprecision( 1 ) << (dia / IDF_THOU_TO_MM) << " "
+ << std::setprecision( 1 ) << (x / IDF_THOU_TO_MM) << " " << (y / IDF_THOU_TO_MM) << " "
+ << pltstr.c_str() << " " << refstr.c_str() << " "
+ << holestr.c_str() << " " << ownstr.c_str() << "\n";
+ }
+
+ return;
+} // IDF_DRILL_DATA::Write( aBoardFile, unitMM )
+
+
+double IDF_DRILL_DATA::GetDrillDia()
+{
+ return dia;
+}
+
+double IDF_DRILL_DATA::GetDrillXPos()
+{
+ return x;
+}
+
+double IDF_DRILL_DATA::GetDrillYPos()
+{
+ return y;
+}
+
+IDF3::KEY_PLATING IDF_DRILL_DATA::GetDrillPlating()
+{
+ return plating;
+}
+
+const std::string& IDF_DRILL_DATA::GetDrillRefDes()
+{
+ switch( kref )
+ {
+ case BOARD:
+ refdes = "BOARD";
+ break;
+
+ case PANEL:
+ refdes = "PANEL";
+ break;
+
+ case REFDES:
+ break;
+
+ default:
+ refdes = "NOREFDES";
+ break;
+ }
+
+ return refdes;
+}
+
+const std::string& IDF_DRILL_DATA::GetDrillHoleType()
+{
+ switch( khole )
+ {
+ case PIN:
+ holetype = "PIN";
+ break;
+
+ case VIA:
+ holetype = "VIA";
+ break;
+
+ case TOOL:
+ holetype = "TOOL";
+ break;
+
+ case OTHER:
+ break;
+
+ default:
+ holetype = "MTG";
+ break;
+ }
+
+ return holetype;
+}
+
+
+#ifdef DEBUG_IDF
+void IDF3::PrintSeg( IDF_SEGMENT* aSegment )
+{
+ if( aSegment->IsCircle() )
+ {
+ fprintf(stdout, "printSeg(): CIRCLE: C(%.3f, %.3f) P(%.3f, %.3f) rad. %.3f\n",
+ aSegment->startPoint.x, aSegment->startPoint.y,
+ aSegment->endPoint.x, aSegment->endPoint.y,
+ aSegment->radius );
+ return;
+ }
+
+ if( aSegment->angle < -MIN_ANG || aSegment->angle > MIN_ANG )
+ {
+ fprintf(stdout, "printSeg(): ARC: p1(%.3f, %.3f) p2(%.3f, %.3f) ang. %.3f\n",
+ aSegment->startPoint.x, aSegment->startPoint.y,
+ aSegment->endPoint.x, aSegment->endPoint.y,
+ aSegment->angle );
+ return;
+ }
+
+ fprintf(stdout, "printSeg(): LINE: p1(%.3f, %.3f) p2(%.3f, %.3f)\n",
+ aSegment->startPoint.x, aSegment->startPoint.y,
+ aSegment->endPoint.x, aSegment->endPoint.y );
+
+ return;
+}
+#endif
+
+
+bool IDF_POINT::Matches( const IDF_POINT& aPoint, double aRadius )
+{
+ double dx = x - aPoint.x;
+ double dy = y - aPoint.y;
+
+ double d2 = dx * dx + dy * dy;
+
+ if( d2 <= aRadius * aRadius )
+ return true;
+
+ return false;
+}
+
+
+double IDF_POINT::CalcDistance( const IDF_POINT& aPoint ) const
+{
+ double dx = aPoint.x - x;
+ double dy = aPoint.y - y;
+ double dist = sqrt( dx * dx + dy * dy );
+
+ return dist;
+}
+
+
+double IDF3::CalcAngleRad( const IDF_POINT& aStartPoint, const IDF_POINT& aEndPoint )
+{
+ return atan2( aEndPoint.y - aStartPoint.y, aEndPoint.x - aStartPoint.x );
+}
+
+
+double IDF3::CalcAngleDeg( const IDF_POINT& aStartPoint, const IDF_POINT& aEndPoint )
+{
+ double ang = CalcAngleRad( aStartPoint, aEndPoint );
+
+ // round to thousandths of a degree
+ int iang = int (ang / M_PI * 1800000.0);
+
+ ang = iang / 10000.0;
+
+ return ang;
+}
+
+
+void IDF3::GetOutline( std::list<IDF_SEGMENT*>& aLines,
+ IDF_OUTLINE& aOutline )
+{
+ aOutline.Clear();
+
+ // NOTE: To tell if the point order is CCW or CW,
+ // sum all: (endPoint.X[n] - startPoint.X[n])*(endPoint[n] + startPoint.Y[n])
+ // If the result is >0, the direction is CW, otherwise
+ // it is CCW. Note that the result cannot be 0 unless
+ // we have a bounded area of 0.
+
+ // First we find the segment with the leftmost point
+ std::list<IDF_SEGMENT*>::iterator bl = aLines.begin();
+ std::list<IDF_SEGMENT*>::iterator el = aLines.end();
+ std::list<IDF_SEGMENT*>::iterator idx = bl++; // iterator for the object with minX
+
+ double minx = (*idx)->GetMinX();
+ double curx;
+
+ while( bl != el )
+ {
+ curx = (*bl)->GetMinX();
+
+ if( curx < minx )
+ {
+ minx = curx;
+ idx = bl;
+ }
+
+ ++bl;
+ }
+
+ aOutline.push( *idx );
+#ifdef DEBUG_IDF
+ PrintSeg( *idx );
+#endif
+ aLines.erase( idx );
+
+ // If the item is a circle then we're done
+ if( aOutline.front()->IsCircle() )
+ return;
+
+ // Assemble the loop
+ bool complete = false; // set if loop is complete
+ bool matched; // set if a segment's end point was matched
+
+ while( !complete )
+ {
+ matched = false;
+ bl = aLines.begin();
+ el = aLines.end();
+
+ while( bl != el && !matched )
+ {
+ if( (*bl)->MatchesStart( aOutline.back()->endPoint ) )
+ {
+ if( (*bl)->IsCircle() )
+ {
+ // a circle on the perimeter is pathological but we just ignore it
+ ++bl;
+ }
+ else
+ {
+ matched = true;
+#ifdef DEBUG_IDF
+ PrintSeg( *bl );
+#endif
+ aOutline.push( *bl );
+ bl = aLines.erase( bl );
+ }
+
+ continue;
+ }
+
+ ++bl;
+ }
+
+ if( !matched )
+ {
+ // attempt to match the end points
+ bl = aLines.begin();
+ el = aLines.end();
+
+ while( bl != el && !matched )
+ {
+ if( (*bl)->MatchesEnd( aOutline.back()->endPoint ) )
+ {
+ if( (*bl)->IsCircle() )
+ {
+ // a circle on the perimeter is pathological but we just ignore it
+ ++bl;
+ }
+ else
+ {
+ matched = true;
+ (*bl)->SwapEnds();
+#ifdef DEBUG_IDF
+ printSeg( *bl );
+#endif
+ aOutline.push( *bl );
+ bl = aLines.erase( bl );
+ }
+
+ continue;
+ }
+
+ ++bl;
+ }
+ }
+
+ if( !matched )
+ {
+ // still no match - attempt to close the loop
+ if( (aOutline.size() > 1) || ( aOutline.front()->angle < -MIN_ANG )
+ || ( aOutline.front()->angle > MIN_ANG ) )
+ {
+ // close the loop
+ IDF_SEGMENT* seg = new IDF_SEGMENT( aOutline.back()->endPoint,
+ aOutline.front()->startPoint );
+
+ if( seg )
+ {
+ complete = true;
+#ifdef DEBUG_IDF
+ printSeg( seg );
+#endif
+ aOutline.push( seg );
+ break;
+ }
+ }
+
+ // the outline is bad; drop the segments
+ aOutline.Clear();
+
+ return;
+ }
+
+ // check if the loop is complete
+ if( aOutline.front()->MatchesStart( aOutline.back()->endPoint ) )
+ {
+ complete = true;
+ break;
+ }
+ }
+}
+
+
+IDF_SEGMENT::IDF_SEGMENT()
+{
+ angle = 0.0;
+ offsetAngle = 0.0;
+ radius = 0.0;
+}
+
+
+IDF_SEGMENT::IDF_SEGMENT( const IDF_POINT& aStartPoint, const IDF_POINT& aEndPoint )
+{
+ angle = 0.0;
+ offsetAngle = 0.0;
+ radius = 0.0;
+ startPoint = aStartPoint;
+ endPoint = aEndPoint;
+}
+
+
+IDF_SEGMENT::IDF_SEGMENT( const IDF_POINT& aStartPoint,
+ const IDF_POINT& aEndPoint,
+ double aAngle,
+ bool aFromKicad )
+{
+ double diff = abs( aAngle ) - 360.0;
+
+ if( ( diff < MIN_ANG
+ && diff > -MIN_ANG ) || ( aAngle < MIN_ANG && aAngle > -MIN_ANG ) || (!aFromKicad) )
+ {
+ angle = 0.0;
+ startPoint = aStartPoint;
+ endPoint = aEndPoint;
+
+ if( diff < MIN_ANG && diff > -MIN_ANG )
+ {
+ angle = 360.0;
+ center = aStartPoint;
+ offsetAngle = 0.0;
+ radius = aStartPoint.CalcDistance( aEndPoint );
+ }
+ else if( aAngle > MIN_ANG || aAngle < -MIN_ANG )
+ {
+ angle = aAngle;
+ CalcCenterAndRadius();
+ }
+
+ return;
+ }
+
+ // we need to convert from the KiCad arc convention
+ angle = aAngle;
+
+ center = aStartPoint;
+
+ offsetAngle = IDF3::CalcAngleDeg( aStartPoint, aEndPoint );
+
+ radius = aStartPoint.CalcDistance( aEndPoint );
+
+ startPoint = aEndPoint;
+
+ double ang = offsetAngle + aAngle;
+ ang = (ang / 180.0) * M_PI;
+
+ endPoint.x = ( radius * cos( ang ) ) + center.x;
+ endPoint.y = ( radius * sin( ang ) ) + center.y;
+}
+
+
+bool IDF_SEGMENT::MatchesStart( const IDF_POINT& aPoint, double aRadius )
+{
+ return startPoint.Matches( aPoint, aRadius );
+}
+
+
+bool IDF_SEGMENT::MatchesEnd( const IDF_POINT& aPoint, double aRadius )
+{
+ return endPoint.Matches( aPoint, aRadius );
+}
+
+
+void IDF_SEGMENT::CalcCenterAndRadius( void )
+{
+ // NOTE: this routine does not check if the points are the same
+ // or too close to be sensible in a production setting.
+
+ double offAng = IDF3::CalcAngleRad( startPoint, endPoint );
+ double d = startPoint.CalcDistance( endPoint ) / 2.0;
+ double xm = ( startPoint.x + endPoint.x ) * 0.5;
+ double ym = ( startPoint.y + endPoint.y ) * 0.5;
+
+ radius = d / sin( angle * M_PI / 360.0 );
+
+ if( radius < 0.0 )
+ {
+ radius = -radius;
+ }
+
+ // calculate the height of the triangle with base d and hypotenuse r
+ double dh2 = radius * radius - d * d;
+
+ if( dh2 < 0 )
+ {
+ // this should only ever happen due to rounding errors when r == d
+ dh2 = 0;
+ }
+
+ double h = sqrt( dh2 );
+
+ if( angle > 0.0 )
+ offAng += M_PI_2;
+ else
+ offAng -= M_PI_2;
+
+ if( angle < -180.0 )
+ offAng += M_PI;
+ else if( angle > 180 )
+ offAng -= M_PI;
+
+ center.x = h * cos( offAng ) + xm;
+ center.y = h * sin( offAng ) + ym;
+
+ offsetAngle = IDF3::CalcAngleDeg( center, startPoint );
+}
+
+
+bool IDF_SEGMENT::IsCircle( void )
+{
+ double diff = abs( angle ) - 360.0;
+
+ if( ( diff < MIN_ANG ) && ( diff > -MIN_ANG ) )
+ return true;
+
+ return false;
+}
+
+
+double IDF_SEGMENT::GetMinX( void )
+{
+ if( angle == 0.0 )
+ return std::min( startPoint.x, endPoint.x );
+
+ // Calculate the leftmost point of the circle or arc
+
+ if( IsCircle() )
+ {
+ // if only everything were this easy
+ return center.x - radius;
+ }
+
+ // cases:
+ // 1. CCW arc: if offset + included angle >= 180 deg then
+ // MinX = center.x - radius, otherwise MinX is the
+ // same as for the case of a line.
+ // 2. CW arc: if offset + included angle <= -180 deg then
+ // MinX = center.x - radius, otherwise MinX is the
+ // same as for the case of a line.
+
+ if( angle > 0 )
+ {
+ // CCW case
+ if( ( offsetAngle + angle ) >= 180.0 )
+ {
+ return center.x - radius;
+ }
+ else
+ {
+ return std::min( startPoint.x, endPoint.x );
+ }
+ }
+
+ // CW case
+ if( ( offsetAngle + angle ) <= -180.0 )
+ {
+ return center.x - radius;
+ }
+
+ return std::min( startPoint.x, endPoint.x );
+}
+
+
+void IDF_SEGMENT::SwapEnds( void )
+{
+ if( IsCircle() )
+ {
+ // reverse the direction
+ angle = -angle;
+ return;
+ }
+
+ IDF_POINT tmp = startPoint;
+ startPoint = endPoint;
+ endPoint = tmp;
+
+ if( ( angle < MIN_ANG ) && ( angle > -MIN_ANG ) )
+ return; // nothing more to do
+
+ // change the direction of the arc
+ angle = -angle;
+ // calculate the new offset angle
+ offsetAngle = IDF3::CalcAngleDeg( center, startPoint );
+}
+
+
+bool IDF_OUTLINE::IsCCW( void )
+{
+ // note: when outlines are not valid, 'false' is returned
+ switch( outline.size() )
+ {
+ case 0:
+ // no outline
+ return false;
+ break;
+
+ case 1:
+ // circles are always reported as CCW
+ if( outline.front()->IsCircle() )
+ return true;
+ else
+ return false;
+ break;
+
+ case 2:
+ // we may have a closed outline consisting of:
+ // 1. arc and line, winding depends on the arc
+ // 2. 2 arcs, winding depends on larger arc
+ {
+ double a1 = outline.front()->angle;
+ double a2 = outline.back()->angle;
+
+ if( ( a1 < -MIN_ANG || a1 > MIN_ANG )
+ && ( a2 < -MIN_ANG || a2 > MIN_ANG ) )
+ {
+ // we have 2 arcs; the winding is determined by
+ // the longer cord. although the angles are in
+ // degrees, there is no need to convert to radians
+ // to determine the longer cord.
+ if( abs( a1 * outline.front()->radius ) >=
+ abs( a2 * outline.back()->radius ) )
+ {
+ // winding depends on a1
+ if( a1 < 0.0 )
+ return false;
+ else
+ return true;
+ }
+ else
+ {
+ if( a2 < 0.0 )
+ return false;
+ else
+ return true;
+ }
+ }
+
+ // we may have a line + arc (or 2 lines)
+ if( a1 < -MIN_ANG )
+ return false;
+
+ if( a1 > MIN_ANG )
+ return true;
+
+ if( a2 < -MIN_ANG )
+ return false;
+
+ if( a2 > MIN_ANG )
+ return true;
+
+ // we have 2 lines (invalid outline)
+ return false;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ double winding = dir + ( outline.front()->startPoint.x - outline.back()->endPoint.x )
+ * ( outline.front()->startPoint.y + outline.back()->endPoint.y );
+
+ if( winding > 0.0 )
+ return false;
+
+ return true;
+}
+
+
+// returns true if the outline is a circle
+bool IDF_OUTLINE::IsCircle( void )
+{
+ if( outline.front()->IsCircle() )
+ return true;
+
+ return false;
+}
+
+
+bool IDF_OUTLINE::push( IDF_SEGMENT* item )
+{
+ if( !outline.empty() )
+ {
+ if( item->IsCircle() )
+ {
+ // not allowed
+ ERROR_IDF << "INVALID GEOMETRY\n";
+ cerr << "* a circle is being added to a non-empty outline\n";
+ return false;
+ }
+ else
+ {
+ if( outline.back()->IsCircle() )
+ {
+ // we can't add lines to a circle
+ ERROR_IDF << "INVALID GEOMETRY\n";
+ cerr << "* a line is being added to a circular outline\n";
+ return false;
+ }
+ else if( !item->MatchesStart( outline.back()->endPoint ) )
+ {
+ // startPoint[N] != endPoint[N -1]
+ ERROR_IDF << "INVALID GEOMETRY\n";
+ cerr << "* disjoint segments (current start point != last end point)\n";
+ cerr << "* start point: " << item->startPoint.x << ", " << item->startPoint.y << "\n";
+ cerr << "* end point: " << outline.back()->endPoint.x << ", " << outline.back()->endPoint.y << "\n";
+ return false;
+ }
+ }
+ }
+
+ outline.push_back( item );
+
+ double ang = outline.back()->angle;
+ double oang = outline.back()->offsetAngle;
+ double radius = outline.back()->radius;
+
+ if( ang < -MIN_ANG || ang > MIN_ANG )
+ {
+ // arcs require special consideration since the winding depends on
+ // the arc length; the arc length is adequately represented by
+ // taking 2 cords from the endpoints to the midpoint of the arc.
+ oang = (oang + ang / 2.0) * M_PI / 180.0;
+ double midx = outline.back()->center.x + radius * cos( oang );
+ double midy = outline.back()->center.y + radius * sin( oang );
+
+ dir += ( outline.back()->endPoint.x - midx )
+ * ( outline.back()->endPoint.y + midy );
+
+ dir += ( midx - outline.back()->startPoint.x )
+ * ( midy + outline.back()->startPoint.y );
+ }
+ else
+ {
+ dir += ( outline.back()->endPoint.x - outline.back()->startPoint.x )
+ * ( outline.back()->endPoint.y + outline.back()->startPoint.y );
+ }
+
+ return true;
+}
diff --git a/utils/idftools/idf_common.h b/utils/idftools/idf_common.h
new file mode 100644
index 0000000..398f8f8
--- /dev/null
+++ b/utils/idftools/idf_common.h
@@ -0,0 +1,711 @@
+/**
+ * @file idf_common.h
+ */
+
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2013-2014 Cirilo Bernardo
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifndef IDF_COMMON_H
+#define IDF_COMMON_H
+
+#include <list>
+#include <fstream>
+#include <exception>
+#include <string>
+#include <cmath>
+
+// differences in angle smaller than MIN_ANG are considered equal
+#define MIN_ANG (0.01)
+
+class IDF_POINT;
+class IDF_SEGMENT;
+class IDF_DRILL_DATA;
+class IDF_OUTLINE;
+class IDF_LIB;
+
+
+struct IDF_ERROR : std::exception
+{
+ std::string message;
+
+ IDF_ERROR( const char* aSourceFile,
+ const char* aSourceMethod,
+ int aSourceLine,
+ const std::string& aMessage ) throw();
+
+ virtual ~IDF_ERROR() throw();
+
+ virtual const char* what() const throw();
+};
+
+
+namespace IDF3 {
+
+ /**
+ * ENUM FILE_STATE
+ * represents state values for the IDF parser's input
+ */
+ enum FILE_STATE
+ {
+ FILE_START = 0, // no data has been read; expecting .HEADER
+ FILE_HEADER, // header has been read; expecting .BOARD_OUTLINE
+ FILE_OUTLINE, // board outline has been read; most sections can be accepted
+ FILE_PLACEMENT, // placement has been read; no further sections can be accepted
+ FILE_INVALID, // file is invalid
+ FILE_ERROR // other errors while processing the file
+ };
+
+ /**
+ * ENUM IDF_VERSION
+ * represents the supported IDF versions (3.0 and 2.0 ONLY)
+ */
+ enum IDF_VERSION
+ {
+ IDF_V2 = 0, // version 2 has read support only; files written as IDFv3
+ IDF_V3 // version 3 has full read/write support
+ };
+
+ /**
+ * ENUM KEY_OWNER
+ * represents the type of CAD which has ownership an object
+ */
+ enum KEY_OWNER
+ {
+ UNOWNED = 0, //< either MCAD or ECAD may modify a feature
+ MCAD, //< only MCAD may modify a feature
+ ECAD //< only ECAD may modify a feature
+ };
+
+ /**
+ * ENUM KEY_HOLETYPE
+ * represents the purpose of an IDF hole
+ */
+ enum KEY_HOLETYPE
+ {
+ PIN = 0, //< drill hole is for a pin
+ VIA, //< drill hole is for a via
+ MTG, //< drill hole is for mounting
+ TOOL, //< drill hole is for tooling
+ OTHER //< user has specified a custom type
+ };
+
+ /**
+ * ENUM KEY_PLATING
+ * represents the plating condition of a hole
+ */
+ enum KEY_PLATING
+ {
+ PTH = 0, //< Plate-Through Hole
+ NPTH //< Non-Plate-Through Hole
+ };
+
+ /**
+ * ENUM KEY_REFDES
+ * represents a component's Reference Designator
+ */
+ enum KEY_REFDES
+ {
+ BOARD = 0, //< feature is associated with the board
+ NOREFDES, //< feature is associated with a component with no RefDes
+ PANEL, //< feature is associated with an IDF panel
+ REFDES //< reference designator as assigned by the CAD software
+ };
+
+ /**
+ * ENUM CAD_TYPE
+ * represents the class of CAD program which is opening or modifying a file
+ */
+ enum CAD_TYPE
+ {
+ CAD_ELEC = 0, //< An Electrical CAD is opening/modifying the file
+ CAD_MECH, //< A Mechanical CAD is opening/modifying the file
+ CAD_INVALID
+ };
+
+ /**
+ * ENUM IDF_LAYER
+ * represents the various IDF layer classes and groupings
+ */
+ enum IDF_LAYER
+ {
+ LYR_TOP = 0,
+ LYR_BOTTOM,
+ LYR_BOTH,
+ LYR_INNER,
+ LYR_ALL,
+ LYR_INVALID
+ };
+
+ /**
+ * ENUM OUTLINE_TYPE
+ * identifies the class of outline
+ */
+ enum OUTLINE_TYPE
+ {
+ OTLN_BOARD = 0,
+ OTLN_OTHER,
+ OTLN_PLACE,
+ OTLN_ROUTE,
+ OTLN_PLACE_KEEPOUT,
+ OTLN_ROUTE_KEEPOUT,
+ OTLN_VIA_KEEPOUT,
+ OTLN_GROUP_PLACE,
+ OTLN_COMPONENT,
+ OTLN_INVALID
+ };
+
+ /**
+ * ENUM COMP_TYPE
+ * identifies whether a component is a mechanical or electrical part
+ */
+ enum COMP_TYPE
+ {
+ COMP_ELEC = 0, //< Component library object is an electrical part
+ COMP_MECH, //< Component library object is a mechanical part
+ COMP_INVALID
+ };
+
+ /**
+ * ENUM IDF_UNIT
+ * represents the native unit of the board and of component outlines
+ */
+ enum IDF_UNIT
+ {
+ UNIT_MM = 0, //< Units in the file are in millimeters
+ UNIT_THOU, //< Units in the file are in mils (aka thou)
+ UNIT_TNM, //< Deprecated Ten Nanometer Units from IDFv2
+ UNIT_INVALID
+ };
+
+ /**
+ * ENUM IDF_PLACEMENT
+ * represents the placement status of a component
+ */
+ enum IDF_PLACEMENT
+ {
+ PS_UNPLACED = 0, //< component location on the board has not been specified
+ PS_PLACED, //< component location has been specified and may be modified by ECAD or MCAD
+ PS_MCAD, //< component location has been specified and may only be modified by MCAD
+ PS_ECAD, //< component location has been specified and may only be modified by ECAD
+ PS_INVALID
+ };
+
+ /**
+ * Function CalcAngleRad
+ * calculates the angle (radians) between the horizon and the segment aStartPoint to aEndPoint
+ *
+ * @param aStartPoint is the start point of a line segment
+ * @param aEndPoint is the end point of a line segment
+ *
+ * @return double: the angle in radians
+ */
+ double CalcAngleRad( const IDF_POINT& aStartPoint, const IDF_POINT& aEndPoint );
+
+
+ /**
+ * Function CalcAngleDeg
+ * calculates the angle (degrees) between the horizon and the segment aStartPoint to aEndPoint
+ *
+ * @param aStartPoint is the start point of a line segment
+ * @param aEndPoint is the end point of a line segment
+ *
+ * @return double: the angle in degrees
+ */
+ double CalcAngleDeg( const IDF_POINT& aStartPoint, const IDF_POINT& aEndPoint );
+
+ /**
+ * Function GetOutline
+ * takes contiguous elements from 'aLines' and stuffs them into 'aOutline'; elements put
+ * into the outline are deleted from aLines. This function is useful for sorting the jumbled
+ * mess of line segments and arcs which represent a board outline and cutouts in KiCad.
+ * The function will determine which segment element within aLines contains the leftmost
+ * point and retrieve the outline of which that segment is part.
+ *
+ * @param aLines (input/output) is a list of IDF segments which comprise an outline and
+ * cutouts.
+ * @param aOutline (output) is the ordered set of segments
+ */
+ void GetOutline( std::list<IDF_SEGMENT*>& aLines,
+ IDF_OUTLINE& aOutline );
+
+#ifdef DEBUG_IDF
+ // prints out segment information for debug purposes
+ void PrintSeg( IDF_SEGMENT* aSegment );
+#endif
+}
+
+
+/**
+ * Class IDF_NOTE
+ * represents an entry in the NOTE section of an IDF file
+ */
+class IDF_NOTE
+{
+friend class IDF3_BOARD;
+private:
+ std::string text; // note text as per IDFv3
+ double xpos; // text X position as per IDFv3
+ double ypos; // text Y position as per IDFv3
+ double height; // text height as per IDFv3
+ double length; // text length as per IDFv3
+
+ /**
+ * Function readNote
+ * reads a note entry from an IDFv3 file
+ *
+ * @param aBoardFile is an open BOARD file; the file position must be set to the start of a NOTE entry
+ * @param aBoardState is the parser's current state value
+ * @param aBoardUnit is the BOARD file's native units (MM or THOU)
+ *
+ * @return bool: true if a note item was read, false otherwise. In case of unrecoverable errors
+ * an exception is thrown
+ */
+ bool readNote( std::ifstream& aBoardFile, IDF3::FILE_STATE& aBoardState, IDF3::IDF_UNIT aBoardUnit );
+
+ /**
+ * Function writeNote
+ * writes a note entry to an IDFv3 file
+ *
+ * @param aBoardFile is an open BOARD file; the file position must be within a NOTE section
+ * @param aBoardUnit is the BOARD file's native units (MM or THOU)
+ *
+ * @return bool: true if the item was successfully written, false otherwise. In case of
+ * unrecoverable errors an exception is thrown
+ */
+ bool writeNote( std::ofstream& aBoardFile, IDF3::IDF_UNIT aBoardUnit );
+
+public:
+ IDF_NOTE();
+
+ /**
+ * Function SetText
+ * sets the text to be stored as a NOTE entry
+ */
+ void SetText( const std::string& aText );
+
+ /**
+ * Function SetPosition
+ * sets the position (mm) of the NOTE entry
+ */
+ void SetPosition( double aXpos, double aYpos );
+
+ /**
+ * Function SetSize
+ * sets the height and length (mm) of the NOTE entry
+ */
+ void SetSize( double aHeight, double aLength );
+
+ /**
+ * Function GetText
+ * returns the string stored in the note entry
+ */
+ const std::string& GetText( void );
+
+ /**
+ * Function GetText
+ * returns the position (mm) of the note entry
+ */
+ void GetPosition( double& aXpos, double& aYpos );
+
+ /**
+ * Function GetText
+ * returns the height and length (mm) of the note entry
+ */
+ void GetSize( double& aHeight, double& aLength );
+};
+
+
+/**
+ * @Class IDF_DRILL_DATA
+ * contains information describing a drilled hole and is responsible for
+ * writing this information to a file in compliance with the IDFv3 specification.
+ */
+class IDF_DRILL_DATA
+{
+friend class IDF3_BOARD;
+friend class IDF3_COMPONENT;
+private:
+ double dia;
+ double x;
+ double y;
+ IDF3::KEY_PLATING plating;
+ IDF3::KEY_REFDES kref;
+ IDF3::KEY_HOLETYPE khole;
+ std::string refdes;
+ std::string holetype;
+ IDF3::KEY_OWNER owner;
+
+ /**
+ * Function read
+ * read a drill entry from an IDFv3 file
+ *
+ * @param aBoardFile is an open IDFv3 file; the file position must be within the DRILLED_HOLES section
+ * @param aBoardUnit is the board file's native unit (MM or THOU)
+ * @param aBoardState is the state value of the parser
+ *
+ * @return bool: true if data was successfully read, otherwise false. In case of an
+ * unrecoverable error an exception is thrown
+ */
+ bool read( std::ifstream& aBoardFile, IDF3::IDF_UNIT aBoardUnit, IDF3::FILE_STATE aBoardState,
+ IDF3::IDF_VERSION aIdfVersion );
+
+ /**
+ * Function write
+ * writes a single line representing a hole within a .DRILLED_HOLES section
+ * In case of an unrecoverable error an exception is thrown.
+ *
+ * @param aBoardFile is an open BOARD file
+ * @param aBoardUnit is the native unit of the output file
+ */
+ void write( std::ofstream& aBoardFile, IDF3::IDF_UNIT aBoardUnit );
+
+public:
+ /**
+ * Constructor IDF_DRILL_DATA
+ * creates an empty drill entry which can be populated by the
+ * read() function
+ */
+ IDF_DRILL_DATA();
+
+ /**
+ * Constructor IDF_DRILL_DATA
+ * creates a drill entry with information compliant with the
+ * IDFv3 specifications.
+ * @param aDrillDia : drill diameter
+ * @param aPosX : X coordinate of the drill center
+ * @param aPosY : Y coordinate of the drill center
+ * @param aPlating : flag, PTH or NPTH
+ * @param aRefDes : component Reference Designator
+ * @param aHoleType : purpose of hole
+ * @param aOwner : one of MCAD, ECAD, UNOWNED
+ */
+ IDF_DRILL_DATA( double aDrillDia, double aPosX, double aPosY,
+ IDF3::KEY_PLATING aPlating,
+ const std::string aRefDes,
+ const std::string aHoleType,
+ IDF3::KEY_OWNER aOwner );
+
+ /**
+ * Function Matches
+ * returns true if the given drill diameter and location
+ * matches the diameter and location of this IDF_DRILL_DATA object
+ *
+ * @param aDrillDia is the drill diameter (mm)
+ * @param aPosX is the X position (mm) of the drilled hole
+ * @param aPosY is the Y position (mm) of the drilled hole
+ *
+ * @return bool: true if the diameter and position match this object
+ */
+ bool Matches( double aDrillDia, double aPosX, double aPosY );
+
+ /**
+ * Function GettDrillDia
+ * returns the drill diameter in mm
+ */
+ double GetDrillDia();
+
+ /**
+ * Function GettDrillXPos
+ * returns the drill's X position in mm
+ */
+ double GetDrillXPos();
+
+ /**
+ * Function GettDrillYPos
+ * returns the drill's Y position in mm
+ */
+ double GetDrillYPos();
+
+ /**
+ * Function GetDrillPlating
+ * returns the plating value (PTH, NPTH)
+ */
+ IDF3::KEY_PLATING GetDrillPlating();
+
+ /**
+ * Function GetDrillRefDes
+ * returns the reference designator of the hole; this
+ * may be a component reference designator, BOARD, or
+ * NOREFDES as per IDFv3.
+ */
+ const std::string& GetDrillRefDes();
+
+ /**
+ * Function GetDrillHoleType
+ * returns the classification of the hole; this may be one of
+ * PIN, VIA, MTG, TOOL, or a user-specified string
+ */
+ const std::string& GetDrillHoleType();
+
+ IDF3::KEY_OWNER GetDrillOwner( void )
+ {
+ return owner;
+ }
+};
+
+
+/**
+ * @Class IDF_POINT
+ * represents a point as used by the various IDF related classes
+ */
+class IDF_POINT
+{
+public:
+ double x; // < X coordinate
+ double y; // < Y coordinate
+
+ IDF_POINT()
+ {
+ x = 0.0;
+ y = 0.0;
+ }
+
+ /**
+ * Function Matches()
+ * returns true if the given coordinate point is within the given radius
+ * of the point.
+ *
+ * @param aPoint : coordinates of the point being compared
+ * @param aRadius : radius (mm) within which the points are considered the same
+ *
+ * @return bool: true if this point matches the given point
+ */
+ bool Matches( const IDF_POINT& aPoint, double aRadius = 1e-5 );
+
+ /**
+ * Function CalcDistance()
+ * returns the Euclidean distance between this point and the given point
+ *
+ * @param aPoint : coordinates of the point whose distance is to be determined
+ *
+ * @return double: distance between this point and aPoint
+ */
+ double CalcDistance( const IDF_POINT& aPoint ) const;
+};
+
+
+/**
+ * @Class IDF_SEGMENT
+ * represents a geometry segment as used in IDFv3 outlines; it may be any of
+ * an arc, line segment, or circle
+ */
+class IDF_SEGMENT
+{
+private:
+ /**
+ * Function CalcCenterAndRadius()
+ * Calculates the center, radius, and angle between center and start point given the
+ * IDF compliant points and included angle.
+ *
+ * @var startPoint, @var endPoint, and @var angle must be set prior as per IDFv3
+ */
+ void CalcCenterAndRadius( void );
+
+public:
+ IDF_POINT startPoint; ///< starting point coordinates in mm
+ IDF_POINT endPoint; ///< end point coordinates in mm
+ IDF_POINT center; ///< center of an arc or circle; internally calculated and not to be set by the user
+ double angle; ///< included angle (degrees) according to IDFv3 specification
+ double offsetAngle; ///< angle between center and start of arc; internally calculated
+ double radius; ///< radius of the arc or circle; internally calculated
+
+ /**
+ * Constructor IDF_SEGMENT
+ * initializes the internal variables
+ */
+ IDF_SEGMENT();
+
+ /**
+ * Function IDF_SEGMENT
+ * creates a straight segment
+ */
+ IDF_SEGMENT( const IDF_POINT& aStartPoint, const IDF_POINT& aEndPoint );
+
+ /**
+ * Constructor IDF_SEGMENT
+ * creates a straight segment, arc, or circle depending on the angle
+ *
+ * @param aStartPoint : start point (center if using KiCad convention, otherwise IDF convention)
+ * @param aEndPoint : end point (start of arc if using KiCad convention, otherwise IDF convention)
+ * @param aAngle : included angle; the KiCad convention is equivalent to the IDF convention
+ * @param fromKicad : set true if we need to convert from KiCad to IDF convention
+ */
+ IDF_SEGMENT( const IDF_POINT& aStartPoint,
+ const IDF_POINT& aEndPoint,
+ double aAngle,
+ bool aFromKicad );
+
+ /**
+ * Function MatchesStart
+ * returns true if the given coordinate is within a radius 'rad'
+ * of the start point.
+ *
+ * @param aPoint : coordinates of the point (mm) being compared
+ * @param aRadius : radius (mm) within which the points are considered the same
+ *
+ * @return bool: true if the given point matches the start point of this segment
+ */
+ bool MatchesStart( const IDF_POINT& aPoint, double aRadius = 1e-3 );
+
+ /**
+ * Function MatchesEnd
+ * returns true if the given coordinate is within a radius 'rad'
+ * of the end point.
+ *
+ * @param aPoint : coordinates (mm) of the point being compared
+ * @param aRadius : radius (mm) within which the points are considered the same
+ *
+ * @return bool: true if the given point matches the end point of this segment
+ */
+ bool MatchesEnd( const IDF_POINT& aPoint, double aRadius = 1e-3 );
+
+ /**
+ * Function IsCircle
+ * returns true if this segment is a circle
+ */
+ bool IsCircle( void );
+
+ /**
+ * Function GetMinX()
+ * returns the minimum X coordinate of this segment
+ */
+ double GetMinX( void );
+
+ /**
+ * Function SwapEnds()
+ * Swaps the start and end points and alters internal
+ * variables as necessary for arcs
+ */
+ void SwapEnds( void );
+};
+
+
+/**
+ * @Class IDF_OUTLINE
+ * contains segment and winding information for an IDF outline
+ */
+class IDF_OUTLINE
+{
+private:
+ double dir; // accumulator to help determine winding direction
+ std::list<IDF_SEGMENT*> outline; // sequential segments comprising an outline
+
+public:
+ IDF_OUTLINE() { dir = 0.0; }
+ ~IDF_OUTLINE() { Clear(); }
+
+ /**
+ * Function IsCCW
+ * returns true if the current list of points represents a counterclockwise winding
+ */
+ bool IsCCW( void );
+
+ /**
+ * Function IsCircle
+ * returns true if this outline is a circle
+ */
+ bool IsCircle( void );
+
+ /**
+ * Function Clear
+ * clears the internal list of outline segments
+ */
+ void Clear( void )
+ {
+ dir = 0.0;
+
+ while( !outline.empty() )
+ {
+ delete outline.front();
+ outline.pop_front();
+ }
+ }
+
+ /**
+ * Function size
+ * returns the size of the internal segment list
+ */
+ size_t size( void )
+ {
+ return outline.size();
+ }
+
+ /**
+ * Function empty
+ * returns true if the internal segment list is empty
+ */
+ bool empty( void )
+ {
+ return outline.empty();
+ }
+
+ /**
+ * Function front
+ * returns the front() iterator of the internal segment list
+ */
+ IDF_SEGMENT*& front( void )
+ {
+ return outline.front();
+ }
+
+ /**
+ * Function back
+ * returns the back() iterator of the internal segment list
+ */
+ IDF_SEGMENT*& back( void )
+ {
+ return outline.back();
+ }
+
+ /**
+ * Function begin
+ * returns the begin() iterator of the internal segment list
+ */
+ std::list<IDF_SEGMENT*>::iterator begin( void )
+ {
+ return outline.begin();
+ }
+
+ /**
+ * Function end
+ * returns the end() iterator of the internal segment list
+ */
+ std::list<IDF_SEGMENT*>::iterator end( void )
+ {
+ return outline.end();
+ }
+
+ /**
+ * Function push
+ * adds a segment to the internal segment list; segments must be added
+ * in order so that startPoint[N] == endPoint[N - 1]
+ *
+ * @param item is a pointer to the segment to add to the outline
+ *
+ * @return bool: true if the segment was added, otherwise false
+ * (outline restrictions have been violated)
+ */
+ bool push( IDF_SEGMENT* item );
+};
+
+#endif // IDF_COMMON_H
diff --git a/utils/idftools/idf_cylinder.cpp b/utils/idftools/idf_cylinder.cpp
new file mode 100644
index 0000000..e7870d4
--- /dev/null
+++ b/utils/idftools/idf_cylinder.cpp
@@ -0,0 +1,681 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2014 Cirilo Bernardo
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/*
+ * This program creates an outline for a horizontal or vertically
+ * oriented axial or radial leaded cylinder with dimensions based
+ * on the user's input.
+ */
+
+#include <iostream>
+#include <fstream>
+#include <string>
+#include <sstream>
+#include <cmath>
+#include <cstdio>
+#include <list>
+#include <utility>
+#include <clocale>
+
+using namespace std;
+
+void make_vcyl( bool inch, bool axial, double dia, double length,
+ double z, double wireDia );
+
+void make_hcyl( bool inch, bool axial, double dia, double length,
+ double z, double wireDia );
+
+void writeAxialCyl( FILE* fp, bool inch, double dia, double length, double wireDia, double pitch );
+
+void writeRadialCyl( FILE* fp, bool inch, double dia, double length, double wireDia,
+ double pitch, double lead );
+
+int main( int argc, char **argv )
+{
+ // IDF implicitly requires the C locale
+ setlocale( LC_ALL, "C" );
+
+ if( argc == 1 )
+ {
+ cout << "idfcyl: This program generates an outline for a cylindrical component.\n";
+ cout << " The cylinder may be horizontal or vertical.\n";
+ cout << " A horizontal cylinder may have wires at one or both ends.\n";
+ cout << " A vertical cylinder may have at most one wire which may be\n";
+ cout << " placed on the left or right side.\n\n";
+ cout << "Input:\n";
+ cout << " Unit: mm, in (millimeters or inches)\n";
+ cout << " Orientation: V (vertical)\n";
+ cout << " Lead type: X, R (axial, radial)\n";
+ cout << " Diameter of body\n";
+ cout << " Length of body\n";
+ cout << " Board offset\n";
+ cout << " * Wire diameter\n";
+ cout << " * Pitch\n";
+ cout << " ** Wire side: L, R (left, right)\n";
+ cout << " *** Lead length\n";
+ cout << " File name (must end in *.idf)\n\n";
+ cout << " NOTES:\n";
+ cout << " * only required for horizontal orientation or\n";
+ cout << " vertical orientation with axial leads\n\n";
+ cout << " ** only required for vertical orientation with axial leads\n\n";
+ cout << " *** only required for horizontal orientation with radial leads\n\n";
+ }
+
+ char orientation = '\0';
+ bool inch = false; // default mm
+ double dia = 0.0;
+ double length = 0.0;
+ double extraZ = 0.0;
+ double wireDia = 0.0;
+ bool axial = false;
+
+ stringstream tstr;
+ string line;
+
+ line.clear();
+ while( line.compare( "mm" ) && line.compare( "in" ) )
+ {
+ cout << "* Units (mm,in): ";
+ line.clear();
+ std::getline( cin, line );
+ }
+
+ if( line.compare( "mm" ) )
+ inch = true;
+
+ line.clear();
+ while( line.compare( "H" ) && line.compare( "h" )
+ && line.compare( "V" ) && line.compare( "v" ) )
+ {
+ cout << "* Orientation (H,V): ";
+ line.clear();
+ std::getline( cin, line );
+ }
+
+ if( line.compare( "H" ) && line.compare( "h" ) )
+ orientation = 'v';
+ else
+ orientation = 'h';
+
+ bool ok = false;
+
+ while( !ok )
+ {
+ cout << "* Axial or Radial (X,R): ";
+
+ line.clear();
+ std::getline( cin, line );
+
+ if( !line.compare( "x" ) || !line.compare( "X" ) )
+ {
+ axial = true;
+ ok = true;
+ }
+ else if( !line.compare( "r" ) || !line.compare( "R" ) )
+ {
+ axial = false;
+ ok = true;
+ }
+ }
+
+ // cylinder dimensions
+ ok = false;
+ while( !ok )
+ {
+ cout << "* Diameter: ";
+
+ line.clear();
+ std::getline( cin, line );
+
+ tstr.clear();
+ tstr.str( line );
+
+ tstr >> dia;
+ if( !tstr.fail() && dia > 0.0 )
+ ok = true;
+ }
+
+ ok = false;
+ while( !ok )
+ {
+ cout << "* Length: ";
+
+ line.clear();
+ std::getline( cin, line );
+
+ tstr.clear();
+ tstr.str( line );
+
+ tstr >> length;
+ if( !tstr.fail() && length > 0.0 )
+ ok = true;
+ }
+
+ ok = false;
+ while( !ok )
+ {
+ cout << "* Board offset: ";
+
+ line.clear();
+ std::getline( cin, line );
+
+ tstr.clear();
+ tstr.str( line );
+
+ tstr >> extraZ;
+ if( !tstr.fail() && extraZ >= 0.0 )
+ ok = true;
+ }
+
+ ok = false;
+ while( ( axial || orientation == 'h' ) && !ok )
+ {
+ cout << "* Wire diameter: ";
+
+ line.clear();
+ std::getline( cin, line );
+
+ tstr.clear();
+ tstr.str( line );
+
+ tstr >> wireDia;
+ if( !tstr.fail() && wireDia > 0.0 )
+ {
+ if( wireDia < dia )
+ ok = true;
+ else
+ cout << "* WARNING: wire diameter must be < cylinder diameter\n";
+ }
+ }
+
+ switch( orientation )
+ {
+ case 'v':
+ make_vcyl( inch, axial, dia, length, extraZ, wireDia );
+ break;
+ case 'h':
+ make_hcyl( inch, axial, dia, length, extraZ, wireDia );
+ break;
+ default:
+ break;
+ }
+
+ setlocale( LC_ALL, "" );
+ return 0;
+}
+
+
+void make_vcyl( bool inch, bool axial, double dia, double length,
+ double z, double wireDia )
+{
+ bool ok = false;
+ bool left = false;
+ stringstream tstr;
+ string line;
+
+ double pitch = 0.0;
+
+ while( axial && !ok )
+ {
+ cout << "* Pitch: ";
+
+ line.clear();
+ std::getline( cin, line );
+
+ tstr.clear();
+ tstr.str( line );
+
+ tstr >> pitch;
+ if( !tstr.fail() && pitch > 0.0 )
+ {
+ if( (pitch - wireDia) <= (dia / 2.0) )
+ {
+ cout << "* WARNING: Pitch must be > dia/2 + wireDia\n";
+ }
+ else
+ {
+ ok = true;
+ }
+ }
+ }
+
+ ok = false;
+ while( axial && !ok )
+ {
+ cout << "* Pin side (L,R): ";
+
+ line.clear();
+ std::getline( cin, line );
+
+ if( !line.compare( "l" ) || !line.compare( "L" ) )
+ {
+ left = true;
+ ok = true;
+ }
+ else if( !line.compare( "r" ) || !line.compare( "R" ) )
+ ok = true;
+ }
+
+ line.clear();
+ while( line.empty() || line.find( ".idf" ) == string::npos )
+ {
+ cout << "* File name (*.idf): ";
+
+ line.clear();
+ std::getline( cin, line );
+ }
+
+ FILE* fp = fopen( line.c_str(), "w" );
+
+ if( !fp )
+ {
+ cerr << "Could not open output file: " << line << "\n";
+ return;
+ }
+
+ fprintf( fp, "# cylindrical outline, vertical, " );
+
+ if( !axial )
+ fprintf( fp, "radial leads\n" );
+ else
+ fprintf( fp, "axial lead on %s\n", left ? "left" : "right" );
+
+ fprintf( fp, "# file: \"%s\"\n", line.c_str() );
+
+ if( inch )
+ {
+ fprintf( fp, "# dia: %d THOU\n", (int) (dia * 1000) );
+ fprintf( fp, "# length: %d THOU\n", (int) (length * 1000) );
+ fprintf( fp, "# board offset: %d THOU\n", (int) (z * 1000) );
+
+ if( axial )
+ {
+ fprintf( fp, "# wire dia: %d THOU\n", (int) (wireDia * 1000) );
+ fprintf( fp, "# pitch: %d THOU\n", (int) (pitch * 1000) );
+ }
+ }
+ else
+ {
+ fprintf( fp, "# dia: %.3f mm\n", dia );
+ fprintf( fp, "# length: %.3f mm\n", length );
+ fprintf( fp, "# board offset: %.3f mm\n", z );
+
+ if( axial )
+ {
+ fprintf( fp, "# wire dia: %.3f mm\n", wireDia );
+ fprintf( fp, "# pitch: %.3f mm\n", pitch );
+ }
+ }
+
+ fprintf( fp, ".ELECTRICAL\n" );
+
+ if( !axial )
+ {
+ fprintf( fp, "\"CYLV_%s_RAD\" \"D%.3f_H%.3f_Z%.3f\" ", inch ? "IN" : "MM",
+ dia, length, z );
+ }
+ else
+ {
+ fprintf( fp, "\"CYLV_%s_AX%s\" \"D%.3f_H%.3f_Z%.3f_WD%.3f_P%.3f\" ", inch ? "IN" : "MM",
+ left ? "L" : "R", dia, length, z, wireDia, pitch );
+ }
+
+ if( inch )
+ fprintf( fp, "THOU %d\n", (int) ((length + z) * 1000) );
+ else
+ fprintf( fp, "MM %.3f\n", length + z );
+
+ if( !axial )
+ {
+ fprintf( fp, "0 0 0 0\n" );
+
+ if( inch )
+ fprintf( fp, "0 %d 0 360\n", (int) (dia * 500) );
+ else
+ fprintf( fp, "0 %.3f 0 360\n", dia / 2.0 );
+
+ fprintf( fp, ".END_ELECTRICAL\n" );
+ fclose( fp );
+ return;
+ }
+
+ double px[4], py[4];
+
+ // points are:
+ // [0] = upper point on cylinder perimeter
+ // [1] = lower point on cylinder perimeter
+ // [2] = point beneath wire center
+ // [3] = point above wire center
+
+ if( inch )
+ {
+ dia *= 1000.0;
+ pitch *= 1000.0;
+ wireDia *= 1000.0;
+ }
+
+ double ang = asin( wireDia / dia );
+ px[0] = dia * cos( ang ) / 2.0 - pitch / 2.0;
+ px[1] = px[0];
+ px[2] = pitch / 2.0;
+ px[3] = px[2];
+
+ py[0] = wireDia / 2.0;
+ py[1] = -py[0];
+ py[2] = py[1];
+ py[3] = py[0];
+
+ char li = '0';
+
+ double fullAng = 360.0;
+
+ if( left )
+ {
+ li = '1';
+ fullAng = -360.0;
+ for( int i = 0; i < 4; ++i ) px[i] = -px[i];
+ }
+
+
+ if( inch )
+ {
+ fprintf( fp, "%c %d %d 0\n", li, (int) px[0], (int) py[0] );
+ fprintf( fp, "%c %d %d %.3f\n", li, (int) px[1], (int) py[1],
+ fullAng * ( 1 - ang / M_PI ) );
+ fprintf( fp, "%c %d %d 0\n", li, (int) px[2], (int) py[2] );
+ fprintf( fp, "%c %d %d %s\n", li, (int) px[3], (int) py[3],
+ left ? "-180" : "180" );
+ fprintf( fp, "%c %d %d 0\n", li, (int) px[0], (int) py[0] );
+ }
+ else
+ {
+ fprintf( fp, "%c %.3f %.3f 0\n", li, px[0], py[0] );
+ fprintf( fp, "%c %.3f %.3f %.3f\n", li, px[1], py[1], fullAng * ( 1 - ang / M_PI ) );
+ fprintf( fp, "%c %.3f %.3f 0\n", li, px[2], py[2] );
+ fprintf( fp, "%c %.3f %.3f %s\n", li, px[3], py[3],
+ left ? "-180" : "180" );
+ fprintf( fp, "%c %.3f %.3f 0\n", li, px[0], py[0] );
+ }
+
+ fprintf( fp, ".END_ELECTRICAL\n" );
+ fclose( fp );
+ return;
+}
+
+
+void make_hcyl( bool inch, bool axial, double dia, double length,
+ double z, double wireDia )
+{
+ stringstream tstr;
+ string line;
+
+ double pitch = 0.0;
+ double lead = 0.0; // lead length for radial leads
+
+ bool ok = false;
+ while( !ok )
+ {
+ if( axial )
+ cout << "* Axial pitch: ";
+ else
+ cout << "* Radial pitch: ";
+
+ line.clear();
+ std::getline( cin, line );
+
+ tstr.clear();
+ tstr.str( line );
+
+ tstr >> pitch;
+ if( !tstr.fail() && pitch > 0.0 )
+ {
+ if( axial )
+ {
+ if( (pitch - wireDia) <= length )
+ {
+ cout << "* WARNING: Axial pitch must be > length + wireDia\n";
+ }
+ else
+ {
+ ok = true;
+ }
+ }
+ else
+ {
+ if( (pitch + wireDia) >= dia )
+ {
+ cout << "* WARNING: Radial pitch must be < dia - wireDia\n";
+ }
+ else if( pitch <= wireDia )
+ {
+ cout << "* WARNING: Radial pitch must be > wireDia\n";
+ }
+ else
+ {
+ ok = true;
+ }
+ }
+ }
+ }
+
+ ok = false;
+ while( !axial && !ok )
+ {
+ cout << "* Lead length: ";
+
+ line.clear();
+ std::getline( cin, line );
+
+ tstr.clear();
+ tstr.str( line );
+
+ tstr >> lead;
+ if( !tstr.fail() && lead > 0.0 )
+ {
+ if( lead < wireDia )
+ cout << "* WARNING: lead length must be >= wireDia\n";
+ else
+ ok = true;
+ }
+ }
+
+ line.clear();
+ while( line.empty() || line.find( ".idf" ) == string::npos )
+ {
+ cout << "* File name (*.idf): ";
+
+ line.clear();
+ std::getline( cin, line );
+ }
+
+ FILE* fp = fopen( line.c_str(), "w" );
+
+ if( !fp )
+ {
+ cerr << "Could not open output file: " << line << "\n";
+ return;
+ }
+
+ fprintf( fp, "# cylindrical outline, horiz., " );
+
+ fprintf( fp, "%s pins\n", axial ? "axial" : "radial" );
+
+ fprintf( fp, "# file: \"%s\"\n", line.c_str() );
+
+ if( inch )
+ {
+ fprintf( fp, "# dia: %d THOU\n", (int) (dia * 1000) );
+ fprintf( fp, "# length: %d THOU\n", (int) (length * 1000) );
+ fprintf( fp, "# extra height: %d THOU\n", (int) (z * 1000) );
+ fprintf( fp, "# wire dia: %d THOU\n", (int) (wireDia * 1000) );
+ fprintf( fp, "# pitch: %d THOU\n", (int) (pitch * 1000) );
+ if( !axial )
+ fprintf( fp, "# lead: %d THOU\n", (int) (lead * 1000) );
+ }
+ else
+ {
+ fprintf( fp, "# dia: %.3f mm\n", dia );
+ fprintf( fp, "# length: %.3f mm\n", length );
+ fprintf( fp, "# extra height: %.3f mm\n", z );
+ fprintf( fp, "# wire dia: %.3f mm\n", wireDia );
+ fprintf( fp, "# pitch: %.3f mm\n", pitch );
+ if( !axial )
+ fprintf( fp, "# lead: %.3f mm\n", lead );
+ }
+
+ fprintf( fp, ".ELECTRICAL\n" );
+
+ if( axial )
+ {
+ fprintf( fp, "\"CYLH_%s_AXI\" \"D%.3f_H%.3f_Z%.3f_WD%.3f_P%.3f\" ",
+ inch ? "IN" : "MM", dia, length, z, wireDia, pitch );
+ }
+ else
+ {
+ fprintf( fp, "\"CYLH_%s_RAD\" \"D%.3f_H%.3f_Z%.3f_WD%.3f_P%.3f_L%.3f\" ",
+ inch ? "IN" : "MM", dia, length, z, wireDia, pitch, lead );
+ }
+
+ if( inch )
+ {
+ fprintf( fp, "THOU %d\n", (int) ((dia + z) * 1000) );
+ dia *= 1000.0;
+ length *= 1000.0;
+ wireDia *= 1000.0;
+ pitch *= 1000.0;
+ if( !axial )
+ lead *= 1000.0;
+ }
+ else
+ {
+ fprintf( fp, "MM %.3f\n", dia + z );
+ }
+
+ if( axial )
+ writeAxialCyl( fp, inch, dia, length, wireDia, pitch );
+ else
+ writeRadialCyl( fp, inch, dia, length, wireDia, pitch, lead );
+
+ fprintf( fp, ".END_ELECTRICAL\n" );
+ fclose( fp );
+ return;
+ return;
+}
+
+void writeAxialCyl( FILE* fp, bool inch, double dia, double length,
+ double wireDia, double pitch )
+{
+ double x1, y1;
+ double x2, y2;
+
+ x1 = -length / 2.0;
+ x2 = -pitch / 2.0;
+ y1 = dia / 2.0;
+ y2 = wireDia / 2.0;
+
+ if( inch )
+ {
+ fprintf( fp, "0 %d %d 0\n", (int) x1, (int) y1 );
+ fprintf( fp, "0 %d %d 0\n", (int) x1, (int) y2 );
+ fprintf( fp, "0 %d %d 0\n", (int) x2, (int) y2 );
+ fprintf( fp, "0 %d %d 180\n", (int) x2, (int) -y2 );
+ fprintf( fp, "0 %d %d 0\n", (int) x1, (int) -y2 );
+ fprintf( fp, "0 %d %d 0\n", (int) x1, (int) -y1 );
+ fprintf( fp, "0 %d %d 0\n", (int) -x1, (int) -y1 );
+ fprintf( fp, "0 %d %d 0\n", (int) -x1, (int) -y2 );
+ fprintf( fp, "0 %d %d 0\n", (int) -x2, (int) -y2 );
+ fprintf( fp, "0 %d %d 180\n", (int) -x2, (int) y2 );
+ fprintf( fp, "0 %d %d 0\n", (int) -x1, (int) y2 );
+ fprintf( fp, "0 %d %d 0\n", (int) -x1, (int) y1 );
+ fprintf( fp, "0 %d %d 0\n", (int) x1, (int) y1 );
+ }
+ else
+ {
+ fprintf( fp, "0 %.3f %.3f 0\n", x1, y1 );
+ fprintf( fp, "0 %.3f %.3f 0\n", x1, y2 );
+ fprintf( fp, "0 %.3f %.3f 0\n", x2, y2 );
+ fprintf( fp, "0 %.3f %.3f 180\n", x2, -y2 );
+ fprintf( fp, "0 %.3f %.3f 0\n", x1, -y2 );
+ fprintf( fp, "0 %.3f %.3f 0\n", x1, -y1 );
+ fprintf( fp, "0 %.3f %.3f 0\n", -x1, -y1 );
+ fprintf( fp, "0 %.3f %.3f 0\n", -x1, -y2 );
+ fprintf( fp, "0 %.3f %.3f 0\n", -x2, -y2 );
+ fprintf( fp, "0 %.3f %.3f 180\n", -x2, y2 );
+ fprintf( fp, "0 %.3f %.3f 0\n", -x1, y2 );
+ fprintf( fp, "0 %.3f %.3f 0\n", -x1, y1 );
+ fprintf( fp, "0 %.3f %.3f 0\n", x1, y1 );
+ }
+
+ return;
+}
+
+void writeRadialCyl( FILE* fp, bool inch, double dia, double length,
+ double wireDia, double pitch, double lead )
+{
+ double x1, y1;
+ double x2, y2;
+ double x3;
+
+ // center is between the mounting holes
+ // which are on a horizontal line
+ y1 = lead + length;
+ y2 = lead;
+ x1 = dia / 2.0;
+ x2 = ( pitch + wireDia ) /2.0;
+ x3 = x2 - wireDia;
+
+ if( inch )
+ {
+ fprintf( fp, "0 %d %d 0\n", (int) -x1, (int) y1 );
+ fprintf( fp, "0 %d %d 0\n", (int) -x1, (int) y2 );
+ fprintf( fp, "0 %d %d 0\n", (int) -x2, (int) y2 );
+ fprintf( fp, "0 %d 0 0\n", (int) -x2 );
+ fprintf( fp, "0 %d 0 180\n", (int) -x3 );
+ fprintf( fp, "0 %d %d 0\n", (int) -x3, (int) y2 );
+ fprintf( fp, "0 %d %d 0\n", (int) x3, (int) y2 );
+ fprintf( fp, "0 %d 0 0\n", (int) x3 );
+ fprintf( fp, "0 %d 0 180\n", (int) x2 );
+ fprintf( fp, "0 %d %d 0\n", (int) x2, (int) y2 );
+ fprintf( fp, "0 %d %d 0\n", (int) x1, (int) y2 );
+ fprintf( fp, "0 %d %d 0\n", (int) x1, (int) y1 );
+ fprintf( fp, "0 %d %d 0\n", (int) -x1, (int) y1 );
+ }
+ else
+ {
+ fprintf( fp, "0 %.3f %.3f 0\n", -x1, y1 );
+ fprintf( fp, "0 %.3f %.3f 0\n", -x1, y2 );
+ fprintf( fp, "0 %.3f %.3f 0\n", -x2, y2 );
+ fprintf( fp, "0 %.3f 0 0\n", -x2 );
+ fprintf( fp, "0 %.3f 0 180\n", -x3 );
+ fprintf( fp, "0 %.3f %.3f 0\n", -x3, y2 );
+ fprintf( fp, "0 %.3f %.3f 0\n", x3, y2 );
+ fprintf( fp, "0 %.3f 0 0\n", x3 );
+ fprintf( fp, "0 %.3f 0 180\n", x2 );
+ fprintf( fp, "0 %.3f %.3f 0\n", x2, y2 );
+ fprintf( fp, "0 %.3f %.3f 0\n", x1, y2 );
+ fprintf( fp, "0 %.3f %.3f 0\n", x1, y1 );
+ fprintf( fp, "0 %.3f %.3f 0\n", -x1, y1 );
+ }
+
+ return;
+}
diff --git a/utils/idftools/idf_examples/Arduino_MEGA_2560-Rev3.emn b/utils/idftools/idf_examples/Arduino_MEGA_2560-Rev3.emn
new file mode 100644
index 0000000..4c21583
--- /dev/null
+++ b/utils/idftools/idf_examples/Arduino_MEGA_2560-Rev3.emn
@@ -0,0 +1,149 @@
+.HEADER
+BOARD_FILE 3.0 "Created by KiCad (2014-01-21 BZR 4629)-product" 2014/01/23.09:19:46 1
+"Arduino_MEGA_2560-Rev3.kicad_pcb" MM
+.END_HEADER
+
+.BOARD_OUTLINE ECAD
+1.60000
+0 246.20220 -25.67000 0
+0 247.20220 -26.67000 0
+0 344.26220 -26.67000 0
+0 345.26220 -25.67000 0
+0 345.26220 -25.40000 0
+0 347.80220 -22.86000 0
+0 347.80220 11.43000 0
+0 345.26220 13.97000 0
+0 345.26220 24.13000 0
+0 342.72220 26.67000 0
+0 247.20220 26.67000 0
+0 246.20220 25.67000 0
+0 246.20220 -25.67000 0
+.END_BOARD_OUTLINE
+
+.DRILLED_HOLES
+3.200 342.72220 -24.13000 NPTH "@HOLE0" MTG ECAD
+3.200 261.44220 24.13000 NPTH "@HOLE1" MTG ECAD
+3.200 336.37220 24.13000 NPTH "@HOLE2" MTG ECAD
+3.200 260.17220 -24.13000 NPTH "@HOLE3" MTG ECAD
+3.200 312.24220 8.89000 NPTH "@HOLE4" MTG ECAD
+3.200 312.24220 -19.05000 NPTH "@HOLE5" MTG ECAD
+0.950 309.82920 3.81000 PTH "ICSP" PIN ECAD
+0.950 312.36920 3.81000 PTH "ICSP" PIN ECAD
+0.950 309.82920 1.27000 PTH "ICSP" PIN ECAD
+0.950 312.36920 1.27000 PTH "ICSP" PIN ECAD
+0.950 309.82920 -1.27000 PTH "ICSP" PIN ECAD
+0.950 312.36920 -1.27000 PTH "ICSP" PIN ECAD
+0.850 309.70220 24.13000 PTH "PWML" PIN ECAD
+0.850 307.16220 24.13000 PTH "PWML" PIN ECAD
+0.850 304.62220 24.13000 PTH "PWML" PIN ECAD
+0.850 302.08220 24.13000 PTH "PWML" PIN ECAD
+0.850 299.54220 24.13000 PTH "PWML" PIN ECAD
+0.850 297.00220 24.13000 PTH "PWML" PIN ECAD
+0.850 294.46220 24.13000 PTH "PWML" PIN ECAD
+0.850 291.92220 24.13000 PTH "PWML" PIN ECAD
+1.400 254.88900 -23.36800 PTH "X1" PIN ECAD
+1.400 257.88620 -18.36420 PTH "X1" PIN ECAD
+1.400 251.89180 -18.36420 PTH "X1" PIN ECAD
+1.400 255.65100 -23.36800 PTH "X1" PIN ECAD
+1.400 254.12700 -23.36800 PTH "X1" PIN ECAD
+1.400 251.89180 -19.12620 PTH "X1" PIN ECAD
+1.400 251.89180 -17.60220 PTH "X1" PIN ECAD
+1.400 257.88620 -19.12620 PTH "X1" PIN ECAD
+1.400 257.88620 -17.60220 PTH "X1" PIN ECAD
+0.850 297.00220 -24.13000 PTH "ADCL" PIN ECAD
+0.850 299.54220 -24.13000 PTH "ADCL" PIN ECAD
+0.850 302.08220 -24.13000 PTH "ADCL" PIN ECAD
+0.850 304.62220 -24.13000 PTH "ADCL" PIN ECAD
+0.850 307.16220 -24.13000 PTH "ADCL" PIN ECAD
+0.850 309.70220 -24.13000 PTH "ADCL" PIN ECAD
+0.850 312.24220 -24.13000 PTH "ADCL" PIN ECAD
+0.850 314.78220 -24.13000 PTH "ADCL" PIN ECAD
+0.850 332.56220 24.13000 PTH "COMMUNICATION" PIN ECAD
+0.850 330.02220 24.13000 PTH "COMMUNICATION" PIN ECAD
+0.850 327.48220 24.13000 PTH "COMMUNICATION" PIN ECAD
+0.850 324.94220 24.13000 PTH "COMMUNICATION" PIN ECAD
+0.850 322.40220 24.13000 PTH "COMMUNICATION" PIN ECAD
+0.850 319.86220 24.13000 PTH "COMMUNICATION" PIN ECAD
+0.850 317.32220 24.13000 PTH "COMMUNICATION" PIN ECAD
+0.850 314.78220 24.13000 PTH "COMMUNICATION" PIN ECAD
+0.850 319.86220 -24.13000 PTH "ADCH" PIN ECAD
+0.850 322.40220 -24.13000 PTH "ADCH" PIN ECAD
+0.850 324.94220 -24.13000 PTH "ADCH" PIN ECAD
+0.850 327.48220 -24.13000 PTH "ADCH" PIN ECAD
+0.850 330.02220 -24.13000 PTH "ADCH" PIN ECAD
+0.850 332.56220 -24.13000 PTH "ADCH" PIN ECAD
+0.850 335.10220 -24.13000 PTH "ADCH" PIN ECAD
+0.850 337.64220 -24.13000 PTH "ADCH" PIN ECAD
+0.950 254.72220 10.18000 PTH "X2" PIN ECAD
+0.950 254.72220 12.68000 PTH "X2" PIN ECAD
+0.950 252.72220 12.68000 PTH "X2" PIN ECAD
+0.950 252.72220 10.18000 PTH "X2" PIN ECAD
+2.200 250.01220 17.43000 PTH "X2" PIN ECAD
+2.200 250.01220 5.43000 PTH "X2" PIN ECAD
+0.950 267.03020 20.82800 PTH "ICSP1" PIN ECAD
+0.950 267.03020 18.28800 PTH "ICSP1" PIN ECAD
+0.950 264.49020 20.82800 PTH "ICSP1" PIN ECAD
+0.950 264.49020 18.28800 PTH "ICSP1" PIN ECAD
+0.950 261.95020 20.82800 PTH "ICSP1" PIN ECAD
+0.950 261.95020 18.28800 PTH "ICSP1" PIN ECAD
+0.850 267.15720 -0.63500 PTH "Y2" PIN ECAD
+0.850 262.07720 -0.63500 PTH "Y2" PIN ECAD
+0.950 267.03020 13.20800 PTH "JP5" PIN ECAD
+0.950 264.49020 13.20800 PTH "JP5" PIN ECAD
+0.950 267.03020 15.74800 PTH "JP5" PIN ECAD
+0.950 264.49020 15.74800 PTH "JP5" PIN ECAD
+0.950 340.18220 24.13000 PTH "XIO" PIN ECAD
+0.950 342.72220 24.13000 PTH "XIO" PIN ECAD
+0.950 340.18220 21.59000 PTH "XIO" PIN ECAD
+0.950 342.72220 21.59000 PTH "XIO" PIN ECAD
+0.950 340.18220 19.05000 PTH "XIO" PIN ECAD
+0.950 342.72220 19.05000 PTH "XIO" PIN ECAD
+0.950 340.18220 16.51000 PTH "XIO" PIN ECAD
+0.950 342.72220 16.51000 PTH "XIO" PIN ECAD
+0.950 340.18220 13.97000 PTH "XIO" PIN ECAD
+0.950 342.72220 13.97000 PTH "XIO" PIN ECAD
+0.950 340.18220 11.43000 PTH "XIO" PIN ECAD
+0.950 342.72220 11.43000 PTH "XIO" PIN ECAD
+0.950 340.18220 8.89000 PTH "XIO" PIN ECAD
+0.950 342.72220 8.89000 PTH "XIO" PIN ECAD
+0.950 340.18220 6.35000 PTH "XIO" PIN ECAD
+0.950 342.72220 6.35000 PTH "XIO" PIN ECAD
+0.950 340.18220 3.81000 PTH "XIO" PIN ECAD
+0.950 342.72220 3.81000 PTH "XIO" PIN ECAD
+0.950 340.18220 1.27000 PTH "XIO" PIN ECAD
+0.950 342.72220 1.27000 PTH "XIO" PIN ECAD
+0.950 340.18220 -1.27000 PTH "XIO" PIN ECAD
+0.950 342.72220 -1.27000 PTH "XIO" PIN ECAD
+0.950 340.18220 -3.81000 PTH "XIO" PIN ECAD
+0.950 342.72220 -3.81000 PTH "XIO" PIN ECAD
+0.950 340.18220 -6.35000 PTH "XIO" PIN ECAD
+0.950 342.72220 -6.35000 PTH "XIO" PIN ECAD
+0.950 340.18220 -8.89000 PTH "XIO" PIN ECAD
+0.950 342.72220 -8.89000 PTH "XIO" PIN ECAD
+0.950 340.18220 -11.43000 PTH "XIO" PIN ECAD
+0.950 342.72220 -11.43000 PTH "XIO" PIN ECAD
+0.950 340.18220 -13.97000 PTH "XIO" PIN ECAD
+0.950 342.72220 -13.97000 PTH "XIO" PIN ECAD
+0.950 340.18220 -16.51000 PTH "XIO" PIN ECAD
+0.950 342.72220 -16.51000 PTH "XIO" PIN ECAD
+0.950 340.18220 -19.05000 PTH "XIO" PIN ECAD
+0.950 342.72220 -19.05000 PTH "XIO" PIN ECAD
+0.850 264.99820 24.13000 PTH "JP6" PIN ECAD
+0.850 267.53820 24.13000 PTH "JP6" PIN ECAD
+0.850 270.07820 24.13000 PTH "JP6" PIN ECAD
+0.850 272.61820 24.13000 PTH "JP6" PIN ECAD
+0.850 275.15820 24.13000 PTH "JP6" PIN ECAD
+0.850 277.69820 24.13000 PTH "JP6" PIN ECAD
+0.850 280.23820 24.13000 PTH "JP6" PIN ECAD
+0.850 282.77820 24.13000 PTH "JP6" PIN ECAD
+0.850 285.31820 24.13000 PTH "JP6" PIN ECAD
+0.850 287.85820 24.13000 PTH "JP6" PIN ECAD
+0.850 274.14220 -24.13000 PTH "POWER" PIN ECAD
+0.850 276.68220 -24.13000 PTH "POWER" PIN ECAD
+0.850 279.22220 -24.13000 PTH "POWER" PIN ECAD
+0.850 281.76220 -24.13000 PTH "POWER" PIN ECAD
+0.850 284.30220 -24.13000 PTH "POWER" PIN ECAD
+0.850 286.84220 -24.13000 PTH "POWER" PIN ECAD
+0.850 289.38220 -24.13000 PTH "POWER" PIN ECAD
+0.850 291.92220 -24.13000 PTH "POWER" PIN ECAD
+.END_DRILLED_HOLES
diff --git a/utils/idftools/idf_examples/Arduino_MEGA_2560-Rev3.emp b/utils/idftools/idf_examples/Arduino_MEGA_2560-Rev3.emp
new file mode 100644
index 0000000..693ae28
--- /dev/null
+++ b/utils/idftools/idf_examples/Arduino_MEGA_2560-Rev3.emp
@@ -0,0 +1,4 @@
+.HEADER
+LIBRARY_FILE 3.0 "Created by KiCad (2014-01-21 BZR 4629)-product" 2014/01/23.09:19:46 1
+.END_HEADER
+
diff --git a/utils/idftools/idf_examples/idf_example.emn b/utils/idftools/idf_examples/idf_example.emn
new file mode 100644
index 0000000..417e5dd
--- /dev/null
+++ b/utils/idftools/idf_examples/idf_example.emn
@@ -0,0 +1,269 @@
+.HEADER
+BOARD_FILE 3.0 "Sample File Generator" 10/22/96.16:02:44 1
+sample_board THOU
+.END_HEADER
+
+# This is the first BOARD section
+# SEC1-0
+# SEC1-1
+.BOARD_OUTLINE MCAD
+62.0
+0 5030.5 -120.0 0.0
+0 5187.5 -120.0 0.0
+0 5187.5 2130.0 0.0
+0 5155.0 2130.0 0.0
+0 5155.0 2550.0 -180.0
+0 5187.5 2550.0 0.0
+0 5187.5 4935.0 0.0
+0 4945.0 5145.0 0.0
+0 4945.0 5420.0 0.0
+0 4865.0 5500.0 0.0
+0 210.0 5500.0 0.0
+0 130.0 5420.0 0.0
+0 130.0 5145.0 0.0
+0 -112.5 4935.0 0.0
+0 -112.5 2550.0 0.0
+0 -80.0 2550.0 0.0
+0 -80.0 2130.0 -180.0
+0 -112.5 2130.0 0.0
+0 -112.5 -140.0 0.0
+0 45.5 -140.0 0.0
+0 45.5 -400.0 0.0
+0 2442.5 -400.0 0.0
+0 2442.5 -140.0 0.0
+0 2631.5 -140.0 0.0
+0 2631.5 -400.0 0.0
+0 5030.5 -400.0 0.0
+0 5030.5 -120.0 0.0
+1 2650.0 2350.0 0.0
+1 3000.0 2350.0 360.0
+.END_BOARD_OUTLINE
+
+
+# This is the second BOARD section
+# SEC2-0
+# SEC2-1
+# NOT SEC1-1
+.ROUTE_OUTLINE ECAD
+ALL
+0 5112.5 150.0 0.0
+0 5112.5 2058.2 0.0
+0 5112.5 2621.8 -162.9
+0 5112.5 4863.2 0.0
+0 4878.8 5075.0 0.0
+0 226.4 5075.0 0.0
+0 138.0 4910.3 0.0
+0 138.0 4800.0 0.0
+0 -37.5 4662.5 0.0
+0 -37.5 2621.8 0.0
+0 -37.5 2058.2 -162.9
+0 -37.5 150.0 0.0
+0 162.5 0.0 0.0
+0 4912.5 0.0 0.0
+0 5112.5 150.0 0.0
+.END_ROUTE_OUTLINE
+
+
+# This is the third BOARD section
+# SEC3-0
+# SEC3-1
+.PLACE_OUTLINE MCAD
+TOP 1000.0
+0 5080.0 2034.9 0.0
+0 5080.0 2645.1 -152.9
+0 5080.0 4837.3 0.0
+0 4855.3 5042.5 0.0
+0 252.9 5042.5 0.0
+0 170.5 4896.9 0.0
+0 170.5 4798.4 0.0
+0 -5.0 4659.0 0.0
+0 -5.0 2645.1 0.0
+0 -5.0 2034.9 -152.9
+0 -5.0 182.5 0.0
+0 192.0 32.5 0.0
+0 4883.1 32.5 0.0
+0 5080.0 182.5 0.0
+0 5080.0 2034.9 0.0
+.END_PLACE_OUTLINE
+
+# This is the fourth BOARD section
+# SEC4-0
+# SEC4-1
+.PLACE_OUTLINE UNOWNED
+BOTTOM 200.0
+0 300.0 200.0 0.0
+0 4800.0 200.0 0.0
+0 4800.0 4800.0 0.0
+0 300.0 4800.0 0.0
+0 300.0 200.0 0.0
+.END_PLACE_OUTLINE
+
+
+# This is the fifth BOARD section
+# SEC5-0
+# SEC5-1
+.ROUTE_KEEPOUT ECAD
+ALL
+0 2650.0 2350.0 0.0
+0 3100.0 2350.0 360.0
+.END_ROUTE_KEEPOUT
+
+# This is the sixth BOARD section
+# SEC6-0
+# SEC6-1
+.PLACE_KEEPOUT MCAD
+BOTH 0.0
+0 2650.0 2350.0 0.0
+0 3100.0 2350.0 360.0
+.END_PLACE_KEEPOUT
+
+# This is the seventh BOARD section
+# SEC7-0
+# SEC7-1
+.PLACE_KEEPOUT MCAD
+TOP 300.0
+0 3700.0 5000.0 0.0
+0 3700.0 4300.0 0.0
+0 4000.0 4300.0 0.0
+0 4000.0 3700.0 0.0
+0 5000.0 3700.0 0.0
+0 5000.0 4800.0 0.0
+0 4800.0 5000.0 0.0
+0 3700.0 5000.0 0.0
+.END_PLACE_KEEPOUT
+
+
+# This is the eighth BOARD section
+# SEC8-0
+# SEC8-1
+.DRILLED_HOLES
+30.0 1800.0 100.0 PTH J1 PIN ECAD
+30.0 1700.0 100.0 PTH J1 PIN ECAD
+30.0 1600.0 100.0 PTH J1 PIN ECAD
+30.0 1500.0 100.0 PTH J1 PIN ECAD
+30.0 1400.0 100.0 PTH J1 PIN ECAD
+30.0 1300.0 100.0 PTH J1 PIN ECAD
+30.0 1200.0 100.0 PTH J1 PIN ECAD
+30.0 1100.0 100.0 PTH J1 PIN ECAD
+30.0 1000.0 100.0 PTH J1 PIN ECAD
+30.0 0900.0 100.0 PTH J1 PIN ECAD
+30.0 0800.0 100.0 PTH J1 PIN ECAD
+30.0 0700.0 100.0 PTH J1 PIN ECAD
+30.0 0700.0 200.0 PTH J1 PIN ECAD
+30.0 0800.0 200.0 PTH J1 PIN ECAD
+30.0 0900.0 200.0 PTH J1 PIN ECAD
+30.0 1000.0 200.0 PTH J1 PIN ECAD
+30.0 1100.0 200.0 PTH J1 PIN ECAD
+30.0 1200.0 200.0 PTH J1 PIN ECAD
+30.0 1300.0 200.0 PTH J1 PIN ECAD
+30.0 1400.0 200.0 PTH J1 PIN ECAD
+30.0 1500.0 200.0 PTH J1 PIN ECAD
+30 1600 200 PTH J1 PIN ECAD
+30 1700 200 PTH J1 PIN ECAD
+30 1800 200 PTH J1 PIN ECAD
+30 4400 100 PTH J2 PIN ECAD
+30 4300 100 PTH J2 PIN ECAD
+30 4200 100 PTH J2 PIN ECAD
+30 4100 100 PTH J2 PIN ECAD
+30 4000 100 PTH J2 PIN ECAD
+30 3900 100 PTH J2 PIN ECAD
+30 3800 100 PTH J2 PIN ECAD
+30 3700 100 PTH J2 PIN ECAD
+30 3600 100 PTH J2 PIN ECAD
+30 3500 100 PTH J2 PIN ECAD
+30 3400 100 PTH J2 PIN ECAD
+30 3300 100 PTH J2 PIN ECAD
+30 3300 200 PTH J2 PIN ECAD
+30 3400 200 PTH J2 PIN ECAD
+30 3500 200 PTH J2 PIN ECAD
+30 3600 200 PTH J2 PIN ECAD
+30 3700 200 PTH J2 PIN ECAD
+30 3800 200 PTH J2 PIN ECAD
+30 3900 200 PTH J2 PIN ECAD
+30 4000 200 PTH J2 PIN ECAD
+30 4100 200 PTH J2 PIN ECAD
+30 4200 200 PTH J2 PIN ECAD
+30 4300 200 PTH J2 PIN ECAD
+30 4400 200 PTH J2 PIN ECAD
+30 3000 3300 PTH U3 PIN ECAD
+30 3024.2 3203 PTH U3 PIN ECAD
+30 3048.4 3105.9 PTH U3 PIN ECAD
+30 3072.6 3008.9 PTH U3 PIN ECAD
+30 3096.8 2911.9 PTH U3 PIN ECAD
+30 3121 2814.9 PTH U3 PIN ECAD
+30 3145.2 2717.8 PTH U3 PIN ECAD
+30 3436.2 2790.4 PTH U3 PIN ECAD
+30 3412.1 2887.4 PTH U3 PIN ECAD
+30 3387.9 2984.5 PTH U3 PIN ECAD
+30 3363.7 3081.5 PTH U3 PIN ECAD
+30 3339.5 3178.5 PTH U3 PIN ECAD
+30 3315.3 3275.6 PTH U3 PIN ECAD
+30 3291.1 3372.6 PTH U3 PIN ECAD
+30 2200 2500 PTH U4 PIN ECAD
+30 2100 2500 PTH U4 PIN ECAD
+30 2000 2500 PTH U4 PIN ECAD
+30 1900 2500 PTH U4 PIN ECAD
+30 1800 2500 PTH U4 PIN ECAD
+30 1700 2500 PTH U4 PIN ECAD
+30 1600 2500 PTH U4 PIN ECAD
+30 1600 2200 PTH U4 PIN ECAD
+30 1700 2200 PTH U4 PIN ECAD
+30 1800 2200 PTH U4 PIN ECAD
+30 1900 2200 PTH U4 PIN ECAD
+30 2000 2200 PTH U4 PIN ECAD
+30 2100 2200 PTH U4 PIN ECAD
+30 2200 2200 PTH U4 PIN ECAD
+20 2500 3100 PTH BOARD VIA ECAD
+20 2500 3200 PTH BOARD VIA ECAD
+20 2500 3300 PTH BOARD VIA ECAD
+20 2000 1600 PTH BOARD VIA ECAD
+20 1100 900 PTH BOARD VIA ECAD
+20 1200 1600 PTH BOARD VIA ECAD
+20 3900 3800 PTH BOARD VIA ECAD
+20 3900 2300 PTH BOARD VIA ECAD
+100.0 3100.0 -50.0 NPTH J2 MTG ECAD
+100.0 4600.0 -50.0 NPTH J2 MTG ECAD
+100.0 500.0 -50.0 NPTH J1 MTG ECAD
+100.0 2000.0 -50.0 NPTH J1 MTG ECAD
+93.0 5075.0 0.0 PTH BOARD MTG UNOWNED
+93.0 0.0 4800.0 NPTH BOARD TOOL MCAD
+93.0 0.0 0.0 PTH BOARD MTG UNOWNED
+.END_DRILLED_HOLES
+
+
+# This is the ninth BOARD section
+# SEC9-0
+# SEC9-1
+.NOTES
+3500.0 3300.0 75.0 2500.0 "This component rotated 14 degrees"
+400.0 4400.0 75.0 3200.0 "Component height limited by enclosure latch"
+1800.0 300.0 75.0 1700.0 "Do not move connectors!"
+.END_NOTES
+
+# This is the tenth and ALWAYS FINAL BOARD section
+# SEC10-0
+# SEC10-1
+.PLACEMENT
+cs13_a pn-cap C1
+4000.0 1000.0 100.0 0.0 TOP PLACED
+cc1210 pn-cc1210 C2
+3000.0 3500.0 0.0 0.0 TOP PLACED
+cc1210 pn-cc1210 C3
+3200.0 1800.0 0.0 0.0 BOTTOM PLACED
+cc1210 pn-cc1210 C4
+1400.0 2300.0 0.0 270.0 TOP PLACED
+cc1210 pn-cc1210 C5
+1799.5 3518.1 0.0 0.0 BOTTOM PLACED
+conn_din24 connector J1
+1800.0 100.0 0.0 0.0 TOP MCAD
+conn_din24 connector J2
+4400.0 100.0 0.0 0.0 TOP MCAD
+plcc_20 pn-pal16l8-plcc U1
+1800.0 3200.0 0.0 0.0 BOTTOM ECAD
+plcc_20 pn-pal16l8-plcc U2
+3200.0 1800.0 0.0 0.0 TOP PLACED
+dip_14w pn-hs346-dip U3
+3000.0 3300.0 0.0 14.0 TOP PLACED
+dip_14w pn-hs346-dip U4
+2200.0 2500.0 0.0 270.0 TOP PLACED
+.END_PLACEMENT
diff --git a/utils/idftools/idf_examples/idf_example.emp b/utils/idftools/idf_examples/idf_example.emp
new file mode 100644
index 0000000..b25b000
--- /dev/null
+++ b/utils/idftools/idf_examples/idf_example.emp
@@ -0,0 +1,69 @@
+.HEADER
+LIBRARY_file 3.0 "Sample File Generator" 10/22/96.16:41:37 1
+.END_HEADER
+
+# Component #1/5
+.ELECTRICAL
+cs13_a pn-cap THOU 150.0
+0 -55.0 55.0 0.0
+0 -55.0 -55.0 0.0
+0 135.0 -55.0 0.0
+0 135.0 -80.0 0.0
+0 565.0 -80.0 0.0
+0 565.0 -55.0 0.0
+0 755.0 -55.0 0.0
+0 755.0 55.0 0.0
+0 565.0 55.0 0.0
+0 565.0 80.0 0.0
+0 135.0 80.0 0.0
+0 135.0 55.0 0.0
+0 -55.0 55.0 0.0
+PROP CAPACITANCE 100.0
+PROP TOLERANCE 5.0
+.END_ELECTRICAL
+
+
+# Component #2/5
+.ELECTRICAL
+cc1210 pn-cc1210 THOU 67.0
+0 -40.0 56.0 0.0
+0 -40.0 -56.0 0.0
+0 182.0 -56.0 0.0
+0 182.0 56.0 0.0
+0 -40.0 56.0 0.0
+PROP CAPACITANCE 0.1
+PROP TOLERANCE 5.0
+.END_ELECTRICAL
+
+# Component #3/5
+.ELECTRICAL
+conn_din24 connector THOU 435.0
+0 -1400.0 -500.0 0.0
+0 300.0 -500.0 0.0
+0 300.0 150.0 0.0
+0 -1400.0 150.0 0.0
+0 -1400.0 -500.0 0.0
+.END_ELECTRICAL
+
+
+# Component #4/5
+.ELECTRICAL
+dip_14w pn-hs346-dip THOU 200.0
+0 350.0 50.0 0.0
+0 -50.0 50.0 0.0
+0 -50.0 -650.0 0.0
+0 350.0 -650.0 0.0
+0 350.0 50.0 0.0
+.END_ELECTRICAL
+
+
+# Component #5/5
+.ELECTRICAL
+plcc_20 pn-pal16l8-plcc THOU 14.0
+0 -200.0 240.0 0.0
+0 -240.0 200.0 0.0
+0 -240.0 -240.0 0.0
+0 240.0 -240.0 0.0
+0 240.0 240.0 0.0
+0 -200.0 240.0 0.0
+.END_ELECTRICAL
diff --git a/utils/idftools/idf_examples/test_donut.emn b/utils/idftools/idf_examples/test_donut.emn
new file mode 100644
index 0000000..fd5b87f
--- /dev/null
+++ b/utils/idftools/idf_examples/test_donut.emn
@@ -0,0 +1,45 @@
+.HEADER
+BOARD_FILE 3.0 "Created by some software" 2014/02/01.15:09:15 1
+"test_donut" MM
+.END_HEADER
+
+# The board outline is a simple square with a small hole in it
+.BOARD_OUTLINE ECAD
+1.60000
+0 -100 100 0
+0 -100 -100 0
+0 100 -100 0
+0 100 100 0
+0 -100 100 0
+1 0 0 0
+1 5 0 360
+.END_BOARD_OUTLINE
+
+# This OTHER OUTLINE is a square toroid
+.OTHER_OUTLINE UNOWNED
+MY_DONUT 30 TOP
+0 0 0 0
+0 75 0 360
+1 0 0 0
+1 30 0 360
+.END_OTHER_OUTLINE
+
+# This OTHER OUTLINE is a square with a hole
+.OTHER_OUTLINE UNOWNED
+MY_NOT_DONUT 2 BOTTOM
+0 -50 50 0
+0 -50 -50 0
+0 50 -50 0
+0 50 50 0
+0 -50 50 0
+1 0 0 0
+1 10 0 360
+2 0 50 0
+2 0 75 360
+3 50 0 0
+3 75 0 360
+4 0 -50 0
+4 0 -75 360
+5 -50 0 0
+5 -75 0 360
+.END_OTHER_OUTLINE
diff --git a/utils/idftools/idf_examples/test_donut.emp b/utils/idftools/idf_examples/test_donut.emp
new file mode 100644
index 0000000..d3c09b7
--- /dev/null
+++ b/utils/idftools/idf_examples/test_donut.emp
@@ -0,0 +1,5 @@
+.HEADER
+LIBRARY_FILE 3.0 "Created by some software" 2014/02/01.15:09:15 1
+.END_HEADER
+
+# This file contains no component outlines \ No newline at end of file
diff --git a/utils/idftools/idf_examples/test_idf2.emn b/utils/idftools/idf_examples/test_idf2.emn
new file mode 100644
index 0000000..b317a00
--- /dev/null
+++ b/utils/idftools/idf_examples/test_idf2.emn
@@ -0,0 +1,71 @@
+.HEADER
+BOARD_FILE 3.0 "Created by KiCad (2014-01-25 BZR 4633)-product" 2014/02/01.15:09:15 1
+"test_idf2.kicad_pcb" MM
+.END_HEADER
+
+.BOARD_OUTLINE ECAD
+1.60000
+0 -86.00000 42.00000 0
+0 -86.00000 -42.00000 0
+0 86.00000 -42.00000 0
+0 86.00000 42.00000 0
+0 -86.00000 42.00000 0
+.END_BOARD_OUTLINE
+
+.DRILLED_HOLES
+0.800 -74.00000 16.00000 PTH BOARD PIN ECAD
+0.800 -74.00000 -28.00000 PTH BOARD PIN ECAD
+0.850 -55.75000 16.00000 PTH BOARD PIN ECAD
+0.850 -52.25000 16.00000 PTH BOARD PIN ECAD
+0.850 -35.75000 16.00000 PTH BOARD PIN ECAD
+0.850 -32.25000 16.00000 PTH BOARD PIN ECAD
+1.575 -57.17500 -28.00000 PTH BOARD PIN ECAD
+1.575 -50.82500 -28.00000 PTH BOARD PIN ECAD
+1.575 -37.17500 -28.00000 PTH BOARD PIN ECAD
+1.575 -30.82500 -28.00000 PTH BOARD PIN ECAD
+0.800 -14.00000 16.00000 PTH BOARD PIN ECAD
+0.800 -14.00000 -28.00000 PTH BOARD PIN ECAD
+0.800 6.00000 16.00000 PTH BOARD PIN ECAD
+0.800 6.00000 -28.00000 PTH BOARD PIN ECAD
+0.800 26.00000 16.00000 PTH BOARD PIN ECAD
+0.800 26.00000 -28.00000 PTH BOARD PIN ECAD
+0.800 46.00000 16.00000 PTH BOARD PIN ECAD
+0.800 46.00000 -28.00000 PTH BOARD PIN ECAD
+0.800 66.00000 16.00000 PTH BOARD PIN ECAD
+0.800 66.00000 -28.00000 PTH BOARD PIN ECAD
+.END_DRILLED_HOLES
+
+.PLACEMENT
+"CYLV_MM" "D5.000_H8.000_Z3.000" "NOREFDES_0"
+-74.000000 16.000000 0.000000 0.000 TOP ECAD
+"CYLV_IN" "D0.250_H0.250_Z0.127" "NOREFDES_1"
+-74.000000 -28.000000 0.000000 0.000 TOP ECAD
+"CYLV_MM_L" "D5.000_H8.000_Z3.000_WD0.800_P3.500" "NOREFDES_2"
+-54.000000 16.000000 0.000000 0.000 TOP ECAD
+"CYLV_MM_R" "D5.000_H8.000_Z3.000_WD0.800_P3.500" "NOREFDES_3"
+-34.000000 16.000000 0.000000 0.000 TOP ECAD
+"CYLV_IN_L" "D0.250_H0.250_Z0.127_WD0.062_P0.250" "NOREFDES_4"
+-54.000000 -28.000000 0.000000 0.000 TOP ECAD
+"CYLV_IN_R" "D0.250_H0.250_Z0.127_WD0.062_P0.250" "NOREFDES_5"
+-34.000000 -28.000000 0.000000 0.000 TOP ECAD
+"CYLH_MM_AXI" "D2.500_H4.000_Z0.500_WD0.600_P8.000" "NOREFDES_6"
+-14.000000 16.000000 0.000000 0.000 TOP ECAD
+"CYLH_IN_AXI" "D0.098_H0.157_Z0.020_WD0.024_P0.315" "NOREFDES_7"
+-14.000000 -28.000000 0.000000 0.000 TOP ECAD
+"CYLH_MM_RAD" "D5.000_H6.000_Z0.200_WD0.600_P2.500_L3.000" "NOREFDES_8"
+6.000000 16.000000 0.000000 0.000 TOP ECAD
+"CYLH_IN_RAD" "D0.197_H0.236_Z0.008_WD0.024_P0.098_L0.118" "NOREFDES_9"
+6.000000 -28.000000 0.000000 0.000 TOP ECAD
+"RECTMM" "W10.000_L10.000_H6.000_C0.000" "NOREFDES_10"
+26.000000 16.000000 0.000000 0.000 TOP ECAD
+"RECTIN" "W393_L393_H236_C0" "NOREFDES_11"
+26.000000 -28.000000 0.000000 0.000 TOP ECAD
+"RECTMM" "W10.000_L10.000_H2.000_C0.500" "NOREFDES_12"
+46.000000 16.000000 0.000000 0.000 TOP ECAD
+"RECTIN" "W393_L393_H78_C19" "NOREFDES_13"
+46.000000 -28.000000 0.000000 0.000 TOP ECAD
+"RECTLMM" "W10.000_L10.000_H12.000_D0.800_P6.000" "NOREFDES_14"
+66.000000 16.000000 0.000000 0.000 TOP ECAD
+"RECTLIN" "W393_L393_H472_D31_P236" "NOREFDES_15"
+66.000000 -28.000000 0.000000 0.000 TOP ECAD
+.END_PLACEMENT
diff --git a/utils/idftools/idf_examples/test_idf2.emp b/utils/idftools/idf_examples/test_idf2.emp
new file mode 100644
index 0000000..9777025
--- /dev/null
+++ b/utils/idftools/idf_examples/test_idf2.emp
@@ -0,0 +1,290 @@
+.HEADER
+LIBRARY_FILE 3.0 "Created by KiCad (2014-01-25 BZR 4633)-product" 2014/02/01.15:09:15 1
+.END_HEADER
+
+# cylindrical outline, vertical, no pins
+# file: "cylvmm_0_D5_L8_Z3.idf"
+# dia: 5.000 mm
+# length: 8.000 mm
+# extra height: 3.000 mm
+.ELECTRICAL
+"CYLV_MM" "D5.000_H8.000_Z3.000" MM 11.000
+0 0 0 0
+0 5.000 0 360
+.END_ELECTRICAL
+
+# cylindrical outline, vertical, no pins
+# file: "cylvin_0_D0.25_L0.25_Z0.127.idf"
+# dia: 250 THOU
+# length: 250 THOU
+# extra height: 127 THOU
+.ELECTRICAL
+"CYLV_IN" "D0.250_H0.250_Z0.127" THOU 377
+0 0 0 0
+0 250 0 360
+.END_ELECTRICAL
+
+# cylindrical outline, vertical, 1 pin on left
+# file: "cylvmm_1L_D5_L8_Z3_WD0.8_P3.5.idf"
+# dia: 5.000 mm
+# length: 8.000 mm
+# extra height: 3.000 mm
+# wire dia: 0.800 mm
+# pitch: 3.500 mm
+.ELECTRICAL
+"CYLV_MM_L" "D5.000_H8.000_Z3.000_WD0.800_P3.500" MM 11.000
+1 -0.718 0.400 0
+1 -0.718 -0.400 -341.586
+1 -1.750 -0.400 0
+1 -1.750 0.400 -180
+1 -0.718 0.400 0
+.END_ELECTRICAL
+
+# cylindrical outline, vertical, 1 pin on right
+# file: "cylvmm_1R_D5_L8_Z3_WD0.8_P3.5.idf"
+# dia: 5.000 mm
+# length: 8.000 mm
+# extra height: 3.000 mm
+# wire dia: 0.800 mm
+# pitch: 3.500 mm
+.ELECTRICAL
+"CYLV_MM_R" "D5.000_H8.000_Z3.000_WD0.800_P3.500" MM 11.000
+0 0.718 0.400 0
+0 0.718 -0.400 341.586
+0 1.750 -0.400 0
+0 1.750 0.400 180
+0 0.718 0.400 0
+.END_ELECTRICAL
+
+# cylindrical outline, vertical, 1 pin on left
+# file: "cylvin_1L_D0.25_L0.25_Z0.127_WD0.062_P0.25.idf"
+# dia: 250 THOU
+# length: 250 THOU
+# extra height: 127 THOU
+# wire dia: 62 THOU
+# pitch: 250 THOU
+.ELECTRICAL
+"CYLV_IN_L" "D0.250_H0.250_Z0.127_WD0.062_P0.250" THOU 377
+1 3 31 0
+1 3 -31 -331.282
+1 -125 -31 0
+1 -125 31 -180
+1 3 31 0
+.END_ELECTRICAL
+
+# cylindrical outline, vertical, 1 pin on right
+# file: "cylvin_1R_D0.25_L0.25_Z0.127_WD0.062_P0.25.idf"
+# dia: 250 THOU
+# length: 250 THOU
+# extra height: 127 THOU
+# wire dia: 62 THOU
+# pitch: 250 THOU
+.ELECTRICAL
+"CYLV_IN_R" "D0.250_H0.250_Z0.127_WD0.062_P0.250" THOU 377
+0 -3 31 0
+0 -3 -31 331.282
+0 125 -31 0
+0 125 31 180
+0 -3 31 0
+.END_ELECTRICAL
+
+# cylindrical outline, horiz., axial pins
+# file: "resistor.idf"
+# dia: 2.500 mm
+# length: 4.000 mm
+# extra height: 0.500 mm
+# wire dia: 0.600 mm
+# pitch: 8.000 mm
+.ELECTRICAL
+"CYLH_MM_AXI" "D2.500_H4.000_Z0.500_WD0.600_P8.000" MM 3.000
+0 -2.000 1.250 0
+0 -2.000 0.300 0
+0 -4.000 0.300 0
+0 -4.000 -0.300 180
+0 -2.000 -0.300 0
+0 -2.000 -1.250 0
+0 2.000 -1.250 0
+0 2.000 -0.300 0
+0 4.000 -0.300 0
+0 4.000 0.300 180
+0 2.000 0.300 0
+0 2.000 1.250 0
+0 -2.000 1.250 0
+.END_ELECTRICAL
+
+# cylindrical outline, horiz., axial pins
+# file: "resistor_in.idf"
+# dia: 98 THOU
+# length: 157 THOU
+# extra height: 20 THOU
+# wire dia: 24 THOU
+# pitch: 315 THOU
+.ELECTRICAL
+"CYLH_IN_AXI" "D0.098_H0.157_Z0.020_WD0.024_P0.315" THOU 118
+0 -78 49 0
+0 -78 12 0
+0 -157 12 0
+0 -157 -12 180
+0 -78 -12 0
+0 -78 -49 0
+0 78 -49 0
+0 78 -12 0
+0 157 -12 0
+0 157 12 180
+0 78 12 0
+0 78 49 0
+0 -78 49 0
+.END_ELECTRICAL
+
+# cylindrical outline, horiz., radial pins
+# file: "capacitor.idf"
+# dia: 5.000 mm
+# length: 6.000 mm
+# extra height: 0.200 mm
+# wire dia: 0.600 mm
+# pitch: 2.500 mm
+# lead: 3.000 mm
+.ELECTRICAL
+"CYLH_MM_RAD" "D5.000_H6.000_Z0.200_WD0.600_P2.500_L3.000" MM 5.200
+0 -2.500 9.000 0
+0 -2.500 3.000 0
+0 -1.550 3.000 0
+0 -1.550 0 0
+0 -0.950 0 180
+0 -0.950 3.000 0
+0 0.950 3.000 0
+0 0.950 0 0
+0 1.550 0 180
+0 1.550 3.000 0
+0 2.500 3.000 0
+0 2.500 9.000 0
+0 -2.500 9.000 0
+.END_ELECTRICAL
+
+# cylindrical outline, horiz., radial pins
+# file: "capacitor_in.idf"
+# dia: 197 THOU
+# length: 236 THOU
+# extra height: 8 THOU
+# wire dia: 24 THOU
+# pitch: 98 THOU
+# lead: 118 THOU
+.ELECTRICAL
+"CYLH_IN_RAD" "D0.197_H0.236_Z0.008_WD0.024_P0.098_L0.118" THOU 205
+0 -98 354 0
+0 -98 118 0
+0 -61 118 0
+0 -61 0 0
+0 -37 0 180
+0 -37 118 0
+0 37 118 0
+0 37 0 0
+0 61 0 180
+0 61 118 0
+0 98 118 0
+0 98 354 0
+0 -98 354 0
+.END_ELECTRICAL
+
+# rectangular outline
+# file: "rectMM_10x10x6_C0.idf"
+# width: 10.000 mm
+# length: 10.000 mm
+# height: 6.000 mm
+# chamfer: 0.000 mm
+.ELECTRICAL
+"RECTMM" "W10.000_L10.000_H6.000_C0.000" MM 6.000
+0 5.000 5.000 0
+0 -5.000 5.000 0
+0 -5.000 -5.000 0
+0 5.000 -5.000 0
+0 5.000 5.000 0
+.END_ELECTRICAL
+
+# rectangular outline
+# file: "rectIN_10x10x6mm_C0mm.idf"
+# width: 393 THOU
+# length: 393 THOU
+# height: 236 THOU
+# chamfer: 0 THOU
+.ELECTRICAL
+"RECTIN" "W393_L393_H236_C0" THOU 236
+0 196 196 0
+0 -196 196 0
+0 -196 -196 0
+0 196 -196 0
+0 196 196 0
+.END_ELECTRICAL
+
+# rectangular outline
+# file: "rectMM_10x10x2_C0.5.idf"
+# width: 10.000 mm
+# length: 10.000 mm
+# height: 2.000 mm
+# chamfer: 0.500 mm
+.ELECTRICAL
+"RECTMM" "W10.000_L10.000_H2.000_C0.500" MM 2.000
+0 5.000 5.000 0
+0 -4.500 5.000 0
+0 -5.000 4.500 0
+0 -5.000 -5.000 0
+0 5.000 -5.000 0
+0 5.000 5.000 0
+.END_ELECTRICAL
+
+# rectangular outline
+# file: "rectIN_10x10x2mm_C0.5mm.idf"
+# width: 393 THOU
+# length: 393 THOU
+# height: 78 THOU
+# chamfer: 19 THOU
+.ELECTRICAL
+"RECTIN" "W393_L393_H78_C19" THOU 78
+0 196 196 0
+0 -176 196 0
+0 -196 176 0
+0 -196 -196 0
+0 196 -196 0
+0 196 196 0
+.END_ELECTRICAL
+
+# rectangular outline, leaded
+# file: "rectLMM_10x10x12_D0.8_P6.0.idf"
+# width: 10.000 mm
+# length: 10.000 mm
+# height: 12.000 mm
+# wire dia: 0.800 mm
+# pitch: 6.000 mm
+.ELECTRICAL
+"RECTLMM" "W10.000_L10.000_H12.000_D0.800_P6.000" MM 12.000
+0 3.000 0.400 0
+0 2.000 0.400 0
+0 2.000 5.000 0
+0 -8.000 5.000 0
+0 -8.000 -5.000 0
+0 2.000 -5.000 0
+0 2.000 -0.400 0
+0 3.000 -0.400 0
+0 3.000 0.400 180
+.END_ELECTRICAL
+
+# rectangular outline, leaded
+# file: "rectLIN_10x10x12mm_D0.8mm_P6.0mm.idf"
+# width: 393 THOU
+# length: 393 THOU
+# height: 472 THOU
+# wire dia: 31 THOU
+# pitch: 236 THOU
+.ELECTRICAL
+"RECTLIN" "W393_L393_H472_D31_P236" THOU 472
+0 118 15 0
+0 78 15 0
+0 78 196 0
+0 -315 196 0
+0 -315 -196 0
+0 78 -196 0
+0 78 -15 0
+0 118 -15 0
+0 118 15 180
+.END_ELECTRICAL
+
diff --git a/utils/idftools/idf_helpers.cpp b/utils/idftools/idf_helpers.cpp
new file mode 100644
index 0000000..cad1852
--- /dev/null
+++ b/utils/idftools/idf_helpers.cpp
@@ -0,0 +1,323 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2014 Cirilo Bernardo
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <cctype>
+#include <iostream>
+#include <sstream>
+
+#include <idf_common.h>
+#include <idf_helpers.h>
+
+using namespace std;
+using namespace IDF3;
+
+// fetch a line from the given input file and trim the ends
+bool IDF3::FetchIDFLine( std::ifstream& aModel, std::string& aLine, bool& isComment, std::streampos& aFilePos )
+{
+ aLine = "";
+ aFilePos = aModel.tellg();
+
+ if( aFilePos == -1 )
+ return false;
+
+ std::getline( aModel, aLine );
+
+ isComment = false;
+
+ // A comment begins with a '#' and must be the first character on the line
+ if( aLine[0] == '#' )
+ {
+ // opening '#' is stripped
+ isComment = true;
+ aLine.erase( aLine.begin() );
+ }
+
+ // strip leading and trailing spaces
+ while( !aLine.empty() && isspace( *aLine.begin() ) )
+ aLine.erase( aLine.begin() );
+
+ while( !aLine.empty() && isspace( *aLine.rbegin() ) )
+ aLine.erase( --aLine.end() );
+
+ // a comment line may be empty to improve human readability
+ if( aLine.empty() && !isComment )
+ return false;
+
+ return true;
+}
+
+
+// extract an IDF string and move the index to point to the character after the substring
+bool IDF3::GetIDFString( const std::string& aLine, std::string& aIDFString,
+ bool& hasQuotes, int& aIndex )
+{
+ // 1. drop all leading spaces
+ // 2. if the first character is '"', read until the next '"',
+ // otherwise read until the next space or EOL.
+
+ std::ostringstream ostr;
+
+ int len = aLine.length();
+ int idx = aIndex;
+
+ if( idx < 0 || idx >= len )
+ return false;
+
+ while( isspace( aLine[idx] ) && idx < len ) ++idx;
+
+ if( idx == len )
+ {
+ aIndex = idx;
+ return false;
+ }
+
+ if( aLine[idx] == '"' )
+ {
+ hasQuotes = true;
+ ++idx;
+ while( aLine[idx] != '"' && idx < len )
+ ostr << aLine[idx++];
+
+ if( idx == len )
+ {
+ ERROR_IDF << "unterminated quote mark in line:\n";
+ std::cerr << "LINE: " << aLine << "\n";
+ aIndex = idx;
+ return false;
+ }
+
+ ++idx;
+ }
+ else
+ {
+ hasQuotes = false;
+
+ while( !isspace( aLine[idx] ) && idx < len )
+ ostr << aLine[idx++];
+
+ }
+
+ aIDFString = ostr.str();
+ aIndex = idx;
+
+ return true;
+}
+
+
+// perform a comparison between a fixed token string and an input string.
+// the token is assumed to be an upper case IDF token and the input string
+// is data from an IDF file. Since IDF tokens are case-insensitive, we cannot
+// assume anything about the case of the input string.
+bool IDF3::CompareToken( const char* aTokenString, const std::string& aInputString )
+{
+ std::string::size_type i, j;
+ std::string bigToken = aInputString;
+ j = aInputString.length();
+
+ for( i = 0; i < j; ++i )
+ bigToken[i] = std::toupper( bigToken[i] );
+
+ if( !bigToken.compare( aTokenString ) )
+ return true;
+
+ return false;
+}
+
+
+// parse a string for an IDF3::KEY_OWNER
+bool IDF3::ParseOwner( const std::string& aToken, IDF3::KEY_OWNER& aOwner )
+{
+ if( CompareToken( "UNOWNED", aToken ) )
+ {
+ aOwner = UNOWNED;
+ return true;
+ }
+ else if( CompareToken( "ECAD", aToken ) )
+ {
+ aOwner = ECAD;
+ return true;
+ }
+ else if( CompareToken( "MCAD", aToken ) )
+ {
+ aOwner = MCAD;
+ return true;
+ }
+
+ ERROR_IDF << "unrecognized IDF OWNER: '" << aToken << "'\n";
+
+ return false;
+}
+
+
+bool IDF3::ParseIDFLayer( const std::string& aToken, IDF3::IDF_LAYER& aLayer )
+{
+ if( CompareToken( "TOP", aToken ) )
+ {
+ aLayer = LYR_TOP;
+ return true;
+ }
+ else if( CompareToken( "BOTTOM", aToken ) )
+ {
+ aLayer = LYR_BOTTOM;
+ return true;
+ }
+ else if( CompareToken( "BOTH", aToken ) )
+ {
+ aLayer = LYR_BOTH;
+ return true;
+ }
+ else if( CompareToken( "INNER", aToken ) )
+ {
+ aLayer = LYR_INNER;
+ return true;
+ }
+ else if( CompareToken( "ALL", aToken ) )
+ {
+ aLayer = LYR_ALL;
+ return true;
+ }
+
+ ERROR_IDF << "unrecognized IDF LAYER: '" << aToken << "'\n";
+
+ aLayer = LYR_INVALID;
+ return false;
+}
+
+
+bool IDF3::WriteLayersText( std::ofstream& aBoardFile, IDF3::IDF_LAYER aLayer )
+{
+ switch( aLayer )
+ {
+ case LYR_TOP:
+ aBoardFile << "TOP";
+ break;
+
+ case LYR_BOTTOM:
+ aBoardFile << "BOTTOM";
+ break;
+
+ case LYR_BOTH:
+ aBoardFile << "BOTH";
+ break;
+
+ case LYR_INNER:
+ aBoardFile << "INNER";
+ break;
+
+ case LYR_ALL:
+ aBoardFile << "ALL";
+ break;
+
+ default:
+ do{
+ std::ostringstream ostr;
+ ostr << "invalid IDF layer: " << aLayer;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ } while( 0 );
+
+ break;
+ }
+
+ return !aBoardFile.fail();
+}
+
+
+std::string IDF3::GetPlacementString( IDF3::IDF_PLACEMENT aPlacement )
+{
+ switch( aPlacement )
+ {
+ case PS_UNPLACED:
+ return "UNPLACED";
+
+ case PS_PLACED:
+ return "PLACED";
+
+ case PS_MCAD:
+ return "MCAD";
+
+ case PS_ECAD:
+ return "ECAD";
+
+ default:
+ break;
+ }
+
+ std::ostringstream ostr;
+ ostr << "[INVALID PLACEMENT VALUE]:" << aPlacement;
+
+ return ostr.str();
+}
+
+
+std::string IDF3::GetLayerString( IDF3::IDF_LAYER aLayer )
+{
+ switch( aLayer )
+ {
+ case LYR_TOP:
+ return "TOP";
+
+ case LYR_BOTTOM:
+ return "BOTTOM";
+
+ case LYR_BOTH:
+ return "BOTH";
+
+ case LYR_INNER:
+ return "INNER";
+
+ case LYR_ALL:
+ return "ALL";
+
+ default:
+ break;
+ }
+
+ std::ostringstream ostr;
+ ostr << "[INVALID LAYER VALUE]:" << aLayer;
+
+ return ostr.str();
+}
+
+std::string IDF3::GetOwnerString( IDF3::KEY_OWNER aOwner )
+{
+ switch( aOwner )
+ {
+ case IDF3::UNOWNED:
+ return "UNOWNED";
+
+ case IDF3::MCAD:
+ return "MCAD";
+
+ case IDF3::ECAD:
+ return "ECAD";
+
+ default:
+ break;
+ }
+
+ ostringstream ostr;
+ ostr << "UNKNOWN: " << aOwner;
+
+ return ostr.str();
+}
diff --git a/utils/idftools/idf_helpers.h b/utils/idftools/idf_helpers.h
new file mode 100644
index 0000000..9b0c33f
--- /dev/null
+++ b/utils/idftools/idf_helpers.h
@@ -0,0 +1,175 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2014 Cirilo Bernardo
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifndef IDF_HELPERS_H
+#define IDF_HELPERS_H
+
+#include <wx/wx.h>
+#include <fstream>
+#include <string>
+#include <idf_common.h>
+
+/**
+ * Macro TO_UTF8
+ * converts a wxString to a UTF8 encoded C string for all wxWidgets build modes.
+ * wxstring is a wxString, not a wxT() or _(). The scope of the return value
+ * is very limited and volatile, but can be used with printf() style functions well.
+ * NOTE: Taken from KiCad include/macros.h
+ */
+#define TO_UTF8( wxstring ) ( (const char*) (wxstring).utf8_str() )
+
+/**
+ * function FROM_UTF8
+ * converts a UTF8 encoded C string to a wxString for all wxWidgets build modes.
+ * NOTE: Taken from KiCad include/macros.h
+ */
+static inline wxString FROM_UTF8( const char* cstring )
+{
+ wxString line = wxString::FromUTF8( cstring );
+
+ if( line.IsEmpty() ) // happens when cstring is not a valid UTF8 sequence
+ line = wxConvCurrent->cMB2WC( cstring ); // try to use locale conversion
+
+ return line;
+}
+
+
+#define ERROR_IDF std::cerr << "* " << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): "
+
+// minimum drill diameters / slot widths to be represented in the IDF output
+#define IDF_MIN_DIA_MM ( 0.001 )
+#define IDF_MIN_DIA_THOU ( 0.00039 )
+#define IDF_MIN_DIA_TNM ( 100 )
+
+// conversion from thou to mm
+#define IDF_THOU_TO_MM 0.0254
+// conversion from TNM to mm
+#define IDF_TNM_TO_MM 0.00001
+
+namespace IDF3
+{
+
+/**
+ * Function FetchIDFLine
+ * retrieves a single line from an IDF file and performs minimal processing. If a comment symbol
+ * is encountered then it is removed and a single leading space is removed if present; all trailing
+ * spaces are removed. If the line is not a comment then all leading and trailing spaces are stripped.
+ *
+ * @param aModel is an open IDFv3 file
+ * @param aLine (output) is the line retrieved from the file
+ * @param isComment (output) is set to true if the line is a comment
+ * @param aFilePos (output) is set to the beginning of the line in case the file needs to be rewound
+ *
+ * @return bool: true if a line was read and was not empty; otherwise false
+ */
+bool FetchIDFLine( std::ifstream& aModel, std::string& aLine, bool& isComment, std::streampos& aFilePos );
+
+
+/**
+ * Function GetIDFString
+ * parses a line retrieved via FetchIDFLine() and returns the first IDF string found from the starting
+ * point aIndex
+ *
+ * @param aLine is the line to parse
+ * @param aIDFString (output) is the IDF string retrieved
+ * @param hasQuotes (output) is true if the string was in quotation marks
+ * @param aIndex (input/output) is the index into the input line
+ *
+ * @return bool: true if a string was retrieved, otherwise false
+ */
+bool GetIDFString( const std::string& aLine, std::string& aIDFString,
+ bool& hasQuotes, int& aIndex );
+
+/**
+ * Function CompareToken
+ * performs a case-insensitive comparison of a token string and an input string
+ *
+ * @param aToken is an IDF token such as ".HEADER"
+ * @param aInputString is a string typically retrieved via GetIDFString
+ *
+ * @return bool: true if the token and input string match
+ */
+bool CompareToken( const char* aTokenString, const std::string& aInputString );
+
+
+/**
+ * Function ParseOwner
+ * parses the input string for a valid IDF Owner type
+ *
+ * @param aToken is the string to be parsed
+ * @param aOwner (output) is the IDF Owner class
+ *
+ * @return bool: true if a valid OWNER was found, otherwise false
+ */
+bool ParseOwner( const std::string& aToken, IDF3::KEY_OWNER& aOwner );
+
+
+/**
+ * Function ParseIDFLayer
+ * parses an input string for a valid IDF layer specification
+ *
+ * @param aToken is the string to be parsed
+ * @param aLayer (output) is the IDF Layer type or group
+ *
+ * @return bool: true if a valid IDF Layer type was found, otherwise false
+ */
+bool ParseIDFLayer( const std::string& aToken, IDF3::IDF_LAYER& aLayer );
+
+
+/**
+ * Function WriteLayersText
+ * writes out text corresponding to the given IDF Layer type
+ *
+ * @param aBoardFile is an IDFv3 file open for output
+ * @param aLayer is the IDF Layer type
+ *
+ * @return bool: true if the data was successfully written, otherwise false
+ */
+bool WriteLayersText( std::ofstream& aBoardFile, IDF3::IDF_LAYER aLayer );
+
+
+/**
+ * Function GetPlacementString
+ * returns a string representing the given IDF Placement type
+ *
+ * @param aPlacement is the IDF placement type to encode as a string
+ *
+ * @return string: the string representation of aPlacement
+ */
+std::string GetPlacementString( IDF3::IDF_PLACEMENT aPlacement );
+
+
+/**
+ * Function GetLayerString
+ * returns a string representing the given IDF Layer type
+ *
+ * @param aLayer is the IDF layer type to encode as a string
+ *
+ * @return string: the string representation of aLayer
+ */
+std::string GetLayerString( IDF3::IDF_LAYER aLayer );
+
+std::string GetOwnerString( IDF3::KEY_OWNER aOwner );
+}
+
+#endif // IDF_HELPERS_H
diff --git a/utils/idftools/idf_outlines.cpp b/utils/idftools/idf_outlines.cpp
new file mode 100644
index 0000000..f1541d2
--- /dev/null
+++ b/utils/idftools/idf_outlines.cpp
@@ -0,0 +1,3614 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2014 Cirilo Bernardo
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <iostream>
+#include <iomanip>
+#include <sstream>
+#include <cmath>
+
+#include <idf_helpers.h>
+#include <idf_outlines.h>
+#include <idf_parser.h>
+
+using namespace IDF3;
+using namespace std;
+
+
+static std::string GetOutlineTypeString( IDF3::OUTLINE_TYPE aOutlineType )
+{
+ switch( aOutlineType )
+ {
+ case OTLN_BOARD:
+ return ".BOARD_OUTLINE";
+
+ case OTLN_OTHER:
+ return ".OTHER_OUTLINE";
+
+ case OTLN_PLACE:
+ return ".PLACEMENT_OUTLINE";
+
+ case OTLN_ROUTE:
+ return ".ROUTE_OUTLINE";
+
+ case OTLN_PLACE_KEEPOUT:
+ return ".PLACE_KEEPOUT";
+
+ case OTLN_ROUTE_KEEPOUT:
+ return ".ROUTE_KEEPOUT";
+
+ case OTLN_VIA_KEEPOUT:
+ return ".VIA_KEEPOUT";
+
+ case OTLN_GROUP_PLACE:
+ return ".PLACE_REGION";
+
+ case OTLN_COMPONENT:
+ return "COMPONENT OUTLINE";
+
+ default:
+ break;
+ }
+
+ std::ostringstream ostr;
+ ostr << "[INVALID OUTLINE TYPE VALUE]:" << aOutlineType;
+
+ return ostr.str();
+}
+
+#ifndef DISABLE_IDF_OWNERSHIP
+static bool CheckOwnership( int aSourceLine, const char* aSourceFunc,
+ IDF3_BOARD* aParent, IDF3::KEY_OWNER aOwnerCAD,
+ IDF3::OUTLINE_TYPE aOutlineType, std::string& aErrorString )
+{
+ if( aParent == NULL )
+ {
+ ostringstream ostr;
+ ostr << "* " << __FILE__ << ":" << aSourceLine << ":" << aSourceFunc << "():\n";
+ ostr << "* BUG: outline's parent not set; cannot enforce ownership rules\n";
+ ostr << "* outline type: " << GetOutlineTypeString( aOutlineType );
+ aErrorString = ostr.str();
+
+ return false;
+ }
+
+ // note: component outlines have no owner so we don't care about
+ // who modifies them
+ if( aOwnerCAD == UNOWNED || aOutlineType == IDF3::OTLN_COMPONENT )
+ return true;
+
+ IDF3::CAD_TYPE parentCAD = aParent->GetCadType();
+
+ if( aOwnerCAD == MCAD && parentCAD == CAD_MECH )
+ return true;
+
+ if( aOwnerCAD == ECAD && parentCAD == CAD_ELEC )
+ return true;
+
+ do
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << aSourceLine << ":" << aSourceFunc << "():\n";
+ ostr << "* ownership violation; CAD type is ";
+
+ if( parentCAD == CAD_MECH )
+ ostr << "MCAD ";
+ else
+ ostr << "ECAD ";
+
+ ostr << "while outline owner is " << GetOwnerString( aOwnerCAD ) << "\n";
+ ostr << "* outline type: " << GetOutlineTypeString( aOutlineType );
+ aErrorString = ostr.str();
+
+ } while( 0 );
+
+ return false;
+}
+#endif
+
+
+/*
+ * CLASS: BOARD OUTLINE
+ */
+BOARD_OUTLINE::BOARD_OUTLINE()
+{
+ outlineType = OTLN_BOARD;
+ single = false;
+ owner = UNOWNED;
+ parent = NULL;
+ thickness = 0.0;
+ unit = UNIT_MM;
+ return;
+}
+
+BOARD_OUTLINE::~BOARD_OUTLINE()
+{
+ clear();
+ return;
+}
+
+IDF3::OUTLINE_TYPE BOARD_OUTLINE::GetOutlineType( void )
+{
+ return outlineType;
+}
+
+void BOARD_OUTLINE::readOutlines( std::ifstream& aBoardFile, IDF3::IDF_VERSION aIdfVersion )
+{
+ // reads the outline data from a file
+ double x, y, ang;
+ double dLoc = 1e-5; // distances are equal when closer than 0.1 micron
+ bool comment = false;
+ bool quoted = false;
+ bool closed = false;
+ int idx = 0;
+ int loopidx = -1;
+ int tmp = 0;
+ int npts = 0;
+ std::string iline;
+ std::string entry;
+ std::stringstream tstr;
+ IDF_OUTLINE* op = NULL;
+ IDF_SEGMENT* sp = NULL;
+ IDF_POINT prePt;
+ IDF_POINT curPt;
+ std::streampos pos;
+
+ // destroy any existing outline data
+ clearOutlines();
+
+ while( aBoardFile.good() )
+ {
+ if( !FetchIDFLine( aBoardFile, iline, comment, pos ) )
+ continue;
+
+ idx = 0;
+ GetIDFString( iline, entry, quoted, idx );
+
+ if( quoted )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: RECORD 3, FIELD 1 of " << GetOutlineTypeString( outlineType );
+ ostr << " is quoted\n";
+ ostr << "* line: '" << iline << "'";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ // check for the end of the section
+ if( entry.size() >= 5 && CompareToken( ".END_", entry.substr( 0, 5 ) ) )
+ {
+ // rewind to the start of the last line; the routine invoking
+ // this is responsible for checking that the current '.END_ ...'
+ // matches the section header.
+ if(aBoardFile.eof())
+ aBoardFile.clear();
+
+ aBoardFile.seekg( pos );
+
+ if( outlines.size() > 0 )
+ {
+ if( npts > 0 && !closed )
+ {
+ ostringstream ostr;
+ ostr << "invalid outline (not closed)\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ // verify winding
+ if( !single )
+ {
+ if( !outlines.front()->IsCCW() )
+ {
+ ERROR_IDF << "invalid IDF3 file (BOARD_OUTLINE)\n";
+ cerr << "* WARNING: first outline is not in CCW order\n";
+ return;
+ }
+
+ if( outlines.size() > 1 && outlines.back()->IsCCW() && !outlines.back()->IsCircle() )
+ {
+ ERROR_IDF << "invalid IDF3 file (BOARD_OUTLINE)\n";
+ cerr << "* WARNING: final cutout does not have points in CW order\n";
+ cerr << "* file position: " << pos << "\n";
+ return;
+ }
+ }
+ }
+
+ return;
+ }
+
+ tstr.clear();
+ tstr << entry;
+
+ tstr >> tmp;
+ if( tstr.fail() )
+ {
+ if( outlineType == OTLN_COMPONENT && CompareToken( "PROP", entry ) )
+ {
+ aBoardFile.seekg( pos );
+ return;
+ }
+
+ do{
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: RECORD 3, FIELD 1 of " << GetOutlineTypeString( outlineType );
+ ostr << " is not numeric\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+
+ } while( 0 );
+ }
+
+ if( tmp != loopidx )
+ {
+ // index change
+ if( npts > 0 && !closed )
+ {
+ ostringstream ostr;
+ ostr << "invalid outline ( outline # " << loopidx << " not closed)\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( tmp < 0 )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: RECORD 3, FIELD 1 of " << GetOutlineTypeString( outlineType );
+ ostr << " is invalid\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( loopidx == -1 )
+ {
+ // first outline
+ if( single )
+ {
+ // outline may have a Loop Index of 0 or 1
+ if( tmp == 0 || tmp == 1 )
+ {
+ op = new IDF_OUTLINE;
+
+ if( op == NULL )
+ {
+ clearOutlines();
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "memory allocation failed" ) );
+ }
+
+ outlines.push_back( op );
+ loopidx = tmp;
+ }
+ else
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: RECORD 3, FIELD 1 of " << GetOutlineTypeString( outlineType );
+ ostr << " is invalid (must be 0 or 1)\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+ }
+ else
+ {
+ // outline *MUST* have a Loop Index of 0
+ if( tmp != 0 )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: RECORD 3, FIELD 1 of " << GetOutlineTypeString( outlineType );
+ ostr << " is invalid (must be 0)\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ op = new IDF_OUTLINE;
+
+ if( op == NULL )
+ {
+ clearOutlines();
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "memory allocation failed" ) );
+ }
+
+ outlines.push_back( op );
+ loopidx = tmp;
+ }
+ // end of block for first outline
+ }
+ else
+ {
+ // outline for cutout
+ if( single )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType );
+ ostr << " section may only have one outline\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( tmp - loopidx != 1 )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType );
+ ostr << " section must have cutouts in numeric order from 1 onwards\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ // verify winding of previous outline
+ if( ( loopidx == 0 && !op->IsCCW() )
+ || ( loopidx > 0 && op->IsCCW() && !op->IsCircle() ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation of loop point order rules by Loop Index " << loopidx << "\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ op = new IDF_OUTLINE;
+
+ if( op == NULL )
+ {
+ clearOutlines();
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "memory allocation failed" ) );
+ }
+
+ outlines.push_back( op );
+ loopidx = tmp;
+ }
+ // end of index change code
+ npts = 0;
+ closed = false;
+ }
+
+ if( op == NULL )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: RECORD 3, FIELD 1 of " << GetOutlineTypeString( outlineType );
+ ostr << " is invalid\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !GetIDFString( iline, entry, quoted, idx ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: RECORD 3, FIELD 2 of ";
+ ostr << GetOutlineTypeString( outlineType ) << " does not exist\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( quoted )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: RECORD 3, FIELD 2 of ";
+ ostr << GetOutlineTypeString( outlineType ) << " must not be in quotes\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ tstr.clear();
+ tstr << entry;
+
+ tstr >> x;
+ if( tstr.fail() )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: RECORD 3, FIELD 2 of ";
+ ostr << GetOutlineTypeString( outlineType ) << " is an invalid X value\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !GetIDFString( iline, entry, quoted, idx ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: RECORD 3, FIELD 3 of ";
+ ostr << GetOutlineTypeString( outlineType ) << " does not exist\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( quoted )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: RECORD 3, FIELD 3 of ";
+ ostr << GetOutlineTypeString( outlineType ) << " must not be in quotes\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ tstr.clear();
+ tstr << entry;
+
+ tstr >> y;
+ if( tstr.fail() )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: RECORD 3, FIELD 3 of ";
+ ostr << GetOutlineTypeString( outlineType ) << " is an invalid Y value\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !GetIDFString( iline, entry, quoted, idx ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: RECORD 3, FIELD 4 of ";
+ ostr << GetOutlineTypeString( outlineType ) << " does not exist\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( quoted )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: RECORD 3, FIELD 4 of ";
+ ostr << GetOutlineTypeString( outlineType ) << " must not be in quotes\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ tstr.clear();
+ tstr << entry;
+
+ tstr >> ang;
+ if( tstr.fail() )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: RECORD 3, FIELD 4 of ";
+ ostr << GetOutlineTypeString( outlineType ) << " is not a valid angle\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ // the line was successfully read; convert to mm if necessary
+ if( unit == UNIT_THOU )
+ {
+ x *= IDF_THOU_TO_MM;
+ y *= IDF_THOU_TO_MM;
+ }
+ else if( ( aIdfVersion == IDF_V2 ) && ( unit == UNIT_TNM ) )
+ {
+ x *= IDF_TNM_TO_MM;
+ y *= IDF_TNM_TO_MM;
+ }
+ else if( unit != UNIT_MM )
+ {
+ ostringstream ostr;
+ ostr << "\n* BUG: invalid UNIT type: " << unit;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( npts++ == 0 )
+ {
+ // first point
+ prePt.x = x;
+ prePt.y = y;
+
+ // ensure that the first point is not an arc specification
+ if( ang < -MIN_ANG || ang > MIN_ANG )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: RECORD 3 of ";
+ ostr << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: first point of an outline has a non-zero angle\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+ }
+ else
+ {
+ // Nth point
+ if( closed )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: RECORD 3 of ";
+ ostr << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: adding a segment to a closed outline\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ curPt.x = x;
+ curPt.y = y;
+
+ if( ang > -MIN_ANG && ang < MIN_ANG )
+ {
+ sp = new IDF_SEGMENT( prePt, curPt );
+ }
+ else
+ {
+ sp = new IDF_SEGMENT( prePt, curPt, ang, false );
+ }
+
+ if( sp == NULL )
+ {
+ clearOutlines();
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "memory allocation failed" ) );
+ }
+
+ if( sp->IsCircle() )
+ {
+ // this is a circle; the loop is closed
+ if( op->size() != 0 )
+ {
+ delete sp;
+
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: RECORD 3 of ";
+ ostr << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: adding a circle to a non-empty outline\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ closed = true;
+ }
+ else if( op->size() != 0 )
+ {
+ if( curPt.Matches( op->front()->startPoint, dLoc ) )
+ closed = true;
+ }
+
+ op->push( sp );
+ prePt.x = x;
+ prePt.y = y;
+ }
+ } // while( aBoardFile.good() )
+
+ // NOTE:
+ // 1. ideally we would ensure that there are no arcs with a radius of 0
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "problems reading file (premature end of outline)" ) );
+
+ return;
+}
+
+bool BOARD_OUTLINE::writeComments( std::ofstream& aBoardFile )
+{
+ if( comments.empty() )
+ return true;
+
+ list< string >::const_iterator itS = comments.begin();
+ list< string >::const_iterator itE = comments.end();
+
+ while( itS != itE )
+ {
+ aBoardFile << "# " << *itS << "\n";
+ ++itS;
+ }
+
+ return !aBoardFile.fail();
+}
+
+bool BOARD_OUTLINE::writeOwner( std::ofstream& aBoardFile )
+{
+ switch( owner )
+ {
+ case ECAD:
+ aBoardFile << "ECAD\n";
+ break;
+
+ case MCAD:
+ aBoardFile << "MCAD\n";
+ break;
+
+ default:
+ aBoardFile << "UNOWNED\n";
+ break;
+ }
+
+ return !aBoardFile.fail();
+}
+
+void BOARD_OUTLINE::writeOutline( std::ofstream& aBoardFile, IDF_OUTLINE* aOutline, size_t aIndex )
+{
+ std::list<IDF_SEGMENT*>::iterator bo;
+ std::list<IDF_SEGMENT*>::iterator eo;
+
+ if( !aOutline )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "\n* BUG: NULL outline pointer" ) );
+
+ if( aOutline->size() == 1 )
+ {
+ if( !aOutline->front()->IsCircle() )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "bad outline (single segment item, not circle)" ) );
+
+ if( single )
+ aIndex = 0;
+
+ // NOTE: a circle always has an angle of 360, never -360,
+ // otherwise SolidWorks chokes on the file.
+ if( unit != UNIT_THOU )
+ {
+ aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(5)
+ << aOutline->front()->startPoint.x << " "
+ << aOutline->front()->startPoint.y << " 0\n";
+
+ aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(5)
+ << aOutline->front()->endPoint.x << " "
+ << aOutline->front()->endPoint.y << " 360\n";
+ }
+ else
+ {
+ aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(1)
+ << (aOutline->front()->startPoint.x / IDF_THOU_TO_MM) << " "
+ << (aOutline->front()->startPoint.y / IDF_THOU_TO_MM) << " 0\n";
+
+ aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(1)
+ << (aOutline->front()->endPoint.x / IDF_THOU_TO_MM) << " "
+ << (aOutline->front()->endPoint.y / IDF_THOU_TO_MM) << " 360\n";
+ }
+
+ return;
+ }
+
+ if( single )
+ {
+ // only indices 0 (CCW) and 1 (CW) are valid; set the index according to
+ // the outline's winding
+ if( aOutline->IsCCW() )
+ aIndex = 0;
+ else
+ aIndex = 1;
+ }
+
+
+ // check if we must reverse things
+ if( ( aOutline->IsCCW() && ( aIndex > 0 ) )
+ || ( ( !aOutline->IsCCW() ) && ( aIndex == 0 ) ) )
+ {
+ eo = aOutline->begin();
+ bo = aOutline->end();
+ --bo;
+
+ // ensure that the very last point is the same as the very first point
+ if( aOutline->size() > 1 )
+ {
+ std::list<IDF_SEGMENT*>::iterator to = eo;
+ ++to;
+ (*to)->startPoint = (*eo)->endPoint;
+ }
+
+ // for the first item we write out both points
+ if( unit != UNIT_THOU )
+ {
+ if( aOutline->front()->angle < MIN_ANG && aOutline->front()->angle > -MIN_ANG )
+ {
+ aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(5)
+ << aOutline->front()->endPoint.x << " "
+ << aOutline->front()->endPoint.y << " 0\n";
+
+ aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(5)
+ << aOutline->front()->startPoint.x << " "
+ << aOutline->front()->startPoint.y << " 0\n";
+ }
+ else
+ {
+ aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(5)
+ << aOutline->front()->endPoint.x << " "
+ << aOutline->front()->endPoint.y << " 0\n";
+
+ aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(5)
+ << aOutline->front()->startPoint.x << " "
+ << aOutline->front()->startPoint.y << " "
+ << setprecision(3) << -aOutline->front()->angle << "\n";
+ }
+ }
+ else
+ {
+ if( aOutline->front()->angle < MIN_ANG && aOutline->front()->angle > -MIN_ANG )
+ {
+ aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(1)
+ << (aOutline->front()->endPoint.x / IDF_THOU_TO_MM) << " "
+ << (aOutline->front()->endPoint.y / IDF_THOU_TO_MM) << " 0\n";
+
+ aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(1)
+ << (aOutline->front()->startPoint.x / IDF_THOU_TO_MM) << " "
+ << (aOutline->front()->startPoint.y / IDF_THOU_TO_MM) << " 0\n";
+ }
+ else
+ {
+ aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(1)
+ << (aOutline->front()->endPoint.x / IDF_THOU_TO_MM) << " "
+ << (aOutline->front()->endPoint.y / IDF_THOU_TO_MM) << " 0\n";
+
+ aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(1)
+ << (aOutline->front()->startPoint.x / IDF_THOU_TO_MM) << " "
+ << (aOutline->front()->startPoint.y / IDF_THOU_TO_MM) << " "
+ << setprecision(3) << -aOutline->front()->angle << "\n";
+ }
+ }
+
+ // for all other segments we only write out the start point
+ while( bo != eo )
+ {
+ if( unit != UNIT_THOU )
+ {
+ if( (*bo)->angle < MIN_ANG && (*bo)->angle > -MIN_ANG )
+ {
+ aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(5)
+ << (*bo)->startPoint.x << " "
+ << (*bo)->startPoint.y << " 0\n";
+ }
+ else
+ {
+ aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(5)
+ << (*bo)->startPoint.x << " "
+ << (*bo)->startPoint.y << " "
+ << setprecision(3) << -(*bo)->angle << "\n";
+ }
+ }
+ else
+ {
+ if( (*bo)->angle < MIN_ANG && (*bo)->angle > -MIN_ANG )
+ {
+ aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(1)
+ << ((*bo)->startPoint.x / IDF_THOU_TO_MM) << " "
+ << ((*bo)->startPoint.y / IDF_THOU_TO_MM) << " 0\n";
+ }
+ else
+ {
+ aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(1)
+ << ((*bo)->startPoint.x / IDF_THOU_TO_MM) << " "
+ << ((*bo)->startPoint.y / IDF_THOU_TO_MM) << " "
+ << setprecision(3) << -(*bo)->angle << "\n";
+ }
+ }
+
+ --bo;
+ }
+ }
+ else
+ {
+ // ensure that the very last point is the same as the very first point
+ if( aOutline->size() > 1 )
+ aOutline->back()-> endPoint = aOutline->front()->startPoint;
+
+ bo = aOutline->begin();
+ eo = aOutline->end();
+
+ // for the first item we write out both points
+ if( unit != UNIT_THOU )
+ {
+ if( (*bo)->angle < MIN_ANG && (*bo)->angle > -MIN_ANG )
+ {
+ aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(5)
+ << (*bo)->startPoint.x << " "
+ << (*bo)->startPoint.y << " 0\n";
+
+ aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(5)
+ << (*bo)->endPoint.x << " "
+ << (*bo)->endPoint.y << " 0\n";
+ }
+ else
+ {
+ aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(5)
+ << (*bo)->startPoint.x << " "
+ << (*bo)->startPoint.y << " 0\n";
+
+ aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(5)
+ << (*bo)->endPoint.x << " "
+ << (*bo)->endPoint.y << " "
+ << setprecision(3) << (*bo)->angle << "\n";
+ }
+ }
+ else
+ {
+ if( (*bo)->angle < MIN_ANG && (*bo)->angle > -MIN_ANG )
+ {
+ aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(1)
+ << ((*bo)->startPoint.x / IDF_THOU_TO_MM) << " "
+ << ((*bo)->startPoint.y / IDF_THOU_TO_MM) << " 0\n";
+
+ aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(1)
+ << ((*bo)->endPoint.x / IDF_THOU_TO_MM) << " "
+ << ((*bo)->endPoint.y / IDF_THOU_TO_MM) << " 0\n";
+ }
+ else
+ {
+ aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(1)
+ << ((*bo)->startPoint.x / IDF_THOU_TO_MM) << " "
+ << ((*bo)->startPoint.y / IDF_THOU_TO_MM) << " 0\n";
+
+ aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(1)
+ << ((*bo)->endPoint.x / IDF_THOU_TO_MM) << " "
+ << ((*bo)->endPoint.y / IDF_THOU_TO_MM) << " "
+ << setprecision(3) << (*bo)->angle << "\n";
+ }
+ }
+
+ ++bo;
+
+ // for all other segments we only write out the last point
+ while( bo != eo )
+ {
+ if( unit != UNIT_THOU )
+ {
+ if( (*bo)->angle < MIN_ANG && (*bo)->angle > -MIN_ANG )
+ {
+ aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(5)
+ << (*bo)->endPoint.x << " "
+ << (*bo)->endPoint.y << " 0\n";
+ }
+ else
+ {
+ aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(5)
+ << (*bo)->endPoint.x << " "
+ << (*bo)->endPoint.y << " "
+ << setprecision(3) << (*bo)->angle << "\n";
+ }
+ }
+ else
+ {
+ if( (*bo)->angle < MIN_ANG && (*bo)->angle > -MIN_ANG )
+ {
+ aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(1)
+ << ((*bo)->endPoint.x / IDF_THOU_TO_MM) << " "
+ << ((*bo)->endPoint.y / IDF_THOU_TO_MM) << " 0\n";
+ }
+ else
+ {
+ aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(1)
+ << ((*bo)->endPoint.x / IDF_THOU_TO_MM) << " "
+ << ((*bo)->endPoint.y / IDF_THOU_TO_MM) << " "
+ << setprecision(3) << (*bo)->angle << "\n";
+ }
+ }
+
+ ++bo;
+ }
+ }
+
+ return;
+}
+
+void BOARD_OUTLINE::writeOutlines( std::ofstream& aBoardFile )
+{
+ if( outlines.empty() )
+ return;
+
+ int idx = 0;
+ std::list< IDF_OUTLINE* >::iterator itS = outlines.begin();
+ std::list< IDF_OUTLINE* >::iterator itE = outlines.end();
+
+ while( itS != itE )
+ {
+ writeOutline( aBoardFile, *itS, idx++ );
+ ++itS;
+ }
+
+ return;
+}
+
+bool BOARD_OUTLINE::SetUnit( IDF3::IDF_UNIT aUnit )
+{
+ // note: although UNIT_TNM is accepted here without reservation,
+ // this can only affect data being read from a file.
+ if( aUnit != UNIT_MM && aUnit != UNIT_THOU && aUnit != UNIT_TNM )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* BUG: invalid IDF UNIT (must be one of UNIT_MM or UNIT_THOU): " << aUnit << "\n";
+ ostr << "* outline type: " << GetOutlineTypeString( outlineType );
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ unit = aUnit;
+ return true;
+}
+
+IDF3::IDF_UNIT BOARD_OUTLINE::GetUnit( void )
+{
+ return unit;
+}
+
+bool BOARD_OUTLINE::setThickness( double aThickness )
+{
+ if( aThickness < 0.0 )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* BUG: aThickness < 0.0\n";
+ ostr << "* outline type: " << GetOutlineTypeString( outlineType );
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ thickness = aThickness;
+ return true;
+}
+
+bool BOARD_OUTLINE::SetThickness( double aThickness )
+{
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !CheckOwnership( __LINE__, __FUNCTION__, parent, owner, outlineType, errormsg ) )
+ return false;
+#endif
+
+ return setThickness( aThickness );
+}
+
+double BOARD_OUTLINE::GetThickness( void )
+{
+ return thickness;
+}
+
+void BOARD_OUTLINE::readData( std::ifstream& aBoardFile, const std::string& aHeader,
+ IDF3::IDF_VERSION aIdfVersion )
+{
+ // BOARD_OUTLINE (PANEL_OUTLINE)
+ // .BOARD_OUTLINE [OWNER]
+ // [thickness]
+ // [outlines]
+
+ // check RECORD 1
+ std::string token;
+ bool quoted = false;
+ int idx = 0;
+ std::streampos pos;
+
+ pos = aBoardFile.tellg();
+
+ if( !GetIDFString( aHeader, token, quoted, idx ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, "invalid invocation: blank header line" ) );
+
+ if( quoted )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: section names may not be in quotes\n";
+ ostr << "* line: '" << aHeader << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !CompareToken( ".BOARD_OUTLINE", token ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: not a board outline\n";
+ ostr << "* line: '" << aHeader << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !GetIDFString( aHeader, token, quoted, idx ) )
+ {
+ if( aIdfVersion > IDF_V2 )
+ ERROR_IDF << "no OWNER; setting to UNOWNED\n";
+
+ owner = UNOWNED;
+ }
+ else
+ {
+ if( !ParseOwner( token, owner ) )
+ {
+ ERROR_IDF << "invalid OWNER (reverting to UNOWNED): " << token << "\n";
+ owner = UNOWNED;
+ }
+ }
+
+ // check RECORD 2
+ std::string iline;
+ bool comment = false;
+ while( aBoardFile.good() && !FetchIDFLine( aBoardFile, iline, comment, pos ) );
+
+ if( ( !aBoardFile.good() && !aBoardFile.eof() ) || iline.empty() )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: premature end\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ idx = 0;
+ if( comment )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: comment within .BOARD_OUTLINE section\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: no thickness specified\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ std::stringstream teststr;
+ teststr << token;
+
+ teststr >> thickness;
+ if( teststr.fail() )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: invalid RECORD 2 (thickness)\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( unit == UNIT_THOU )
+ {
+ thickness *= IDF_THOU_TO_MM;
+ }
+ else if( ( aIdfVersion == IDF_V2 ) && ( unit == UNIT_TNM ) )
+ {
+ thickness *= IDF_TNM_TO_MM;
+ }
+ else if( unit != UNIT_MM )
+ {
+ ostringstream ostr;
+ ostr << "\n* BUG: invalid UNIT type: " << unit;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ // for some unknown reason IDF allows 0 or negative thickness, but this
+ // is a problem so we fix it here
+ if( thickness <= 0.0 )
+ {
+ if( thickness == 0.0 )
+ {
+ ERROR_IDF << "\n* WARNING: setting board thickness to default 1.6mm (";
+ cerr << thickness << ")\n";
+ thickness = 1.6;
+ }
+ else
+ {
+ thickness = -thickness;
+ ERROR_IDF << "\n* WARNING: setting board thickness to positive number (";
+ cerr << thickness << ")\n";
+ }
+ }
+
+ // read RECORD 3 values
+ readOutlines( aBoardFile, aIdfVersion );
+
+ // check RECORD 4
+ while( aBoardFile.good() && !FetchIDFLine( aBoardFile, iline, comment, pos ) );
+
+ if( ( !aBoardFile.good() && aBoardFile.eof() ) || iline.empty() )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: premature end\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ idx = 0;
+ if( comment )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: comment within section\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !CompareToken( ".END_BOARD_OUTLINE", iline ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: no .END_BOARD_OUTLINE found\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ return;
+}
+
+
+void BOARD_OUTLINE::writeData( std::ofstream& aBoardFile )
+{
+ writeComments( aBoardFile );
+
+ // note: a BOARD_OUTLINE section is required, even if it is empty
+ aBoardFile << ".BOARD_OUTLINE ";
+
+ writeOwner( aBoardFile );
+
+ if( unit != UNIT_THOU )
+ aBoardFile << setiosflags(ios::fixed) << setprecision(5) << thickness << "\n";
+ else
+ aBoardFile << setiosflags(ios::fixed) << setprecision(1) << (thickness / IDF_THOU_TO_MM) << "\n";
+
+ writeOutlines( aBoardFile );
+
+ aBoardFile << ".END_BOARD_OUTLINE\n\n";
+
+ return;
+}
+
+void BOARD_OUTLINE::clear( void )
+{
+ comments.clear();
+ clearOutlines();
+
+ owner = UNOWNED;
+ return;
+}
+
+bool BOARD_OUTLINE::Clear( void )
+{
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !CheckOwnership( __LINE__, __FUNCTION__, parent, owner, outlineType, errormsg ) )
+ return false;
+#endif
+
+ clear();
+
+ return true;
+}
+
+void BOARD_OUTLINE::setParent( IDF3_BOARD* aParent )
+{
+ parent = aParent;
+}
+
+IDF3_BOARD* BOARD_OUTLINE::GetParent( void )
+{
+ return parent;
+}
+
+bool BOARD_OUTLINE::addOutline( IDF_OUTLINE* aOutline )
+{
+ std::list< IDF_OUTLINE* >::iterator itS = outlines.begin();
+ std::list< IDF_OUTLINE* >::iterator itE = outlines.end();
+
+ try
+ {
+ while( itS != itE )
+ {
+ if( *itS == aOutline )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "duplicate outline pointer" ) );
+
+ ++itS;
+ }
+
+ outlines.push_back( aOutline );
+
+ }
+ catch( const std::exception& e )
+ {
+ errormsg = e.what();
+
+ return false;
+ }
+
+ return true;
+}
+
+bool BOARD_OUTLINE::AddOutline( IDF_OUTLINE* aOutline )
+{
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !CheckOwnership( __LINE__, __FUNCTION__, parent, owner, outlineType, errormsg ) )
+ return false;
+#endif
+
+ return addOutline( aOutline );
+}
+
+bool BOARD_OUTLINE::DelOutline( IDF_OUTLINE* aOutline )
+{
+ std::list< IDF_OUTLINE* >::iterator itS = outlines.begin();
+ std::list< IDF_OUTLINE* >::iterator itE = outlines.end();
+
+ if( !aOutline )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* BUG: NULL aOutline pointer\n";
+ ostr << "* outline type: " << GetOutlineTypeString( outlineType );
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ if( outlines.empty() )
+ {
+ errormsg.clear();
+ return false;
+ }
+
+ // if there are more than 1 outlines it makes no sense to delete
+ // the first outline (board outline) since that would have the
+ // undesirable effect of substituting a cutout outline as the board outline
+ if( aOutline == outlines.front() )
+ {
+ if( outlines.size() > 1 )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* BUG: attempting to delete first outline in list\n";
+ ostr << "* outline type: " << GetOutlineTypeString( outlineType );
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ outlines.clear();
+ return true;
+ }
+
+ while( itS != itE )
+ {
+ if( *itS == aOutline )
+ {
+ outlines.erase( itS );
+ return true;
+ }
+
+ ++itS;
+ }
+
+ errormsg.clear();
+ return false;
+}
+
+
+bool BOARD_OUTLINE::DelOutline( size_t aIndex )
+{
+ std::list< IDF_OUTLINE* >::iterator itS = outlines.begin();
+
+ if( outlines.empty() )
+ {
+ errormsg.clear();
+ return false;
+ }
+
+ if( aIndex >= outlines.size() )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* BUG: index out of bounds (" << aIndex << " / " << outlines.size() << ")\n";
+ ostr << "* outline type: " << GetOutlineTypeString( outlineType );
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ if( aIndex == 0 )
+ {
+ // if there are more than 1 outlines it makes no sense to delete
+ // the first outline (board outline) since that would have the
+ // undesirable effect of substituting a cutout outline as the board outline
+ if( outlines.size() > 1 )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* BUG: attempting to delete first outline in list\n";
+ ostr << "* outline type: " << GetOutlineTypeString( outlineType );
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ delete *itS;
+ outlines.clear();
+
+ return true;
+ }
+
+ for( ; aIndex > 0; --aIndex )
+ ++itS;
+
+ delete *itS;
+ outlines.erase( itS );
+
+ return true;
+}
+
+const std::list< IDF_OUTLINE* >*const BOARD_OUTLINE::GetOutlines( void )
+{
+ return &outlines;
+}
+
+size_t BOARD_OUTLINE::OutlinesSize( void )
+{
+ return outlines.size();
+}
+
+IDF_OUTLINE* BOARD_OUTLINE::GetOutline( size_t aIndex )
+{
+ if( aIndex >= outlines.size() )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* aIndex (" << aIndex << ") is out of range (" << outlines.size() << ")";
+ errormsg = ostr.str();
+
+ return NULL;
+ }
+
+ std::list< IDF_OUTLINE* >::iterator itS = outlines.begin();
+
+ for( ; aIndex > 0; --aIndex )
+ ++itS;
+
+ return *itS;
+}
+
+IDF3::KEY_OWNER BOARD_OUTLINE::GetOwner( void )
+{
+ return owner;
+}
+
+bool BOARD_OUTLINE::SetOwner( IDF3::KEY_OWNER aOwner )
+{
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !CheckOwnership( __LINE__, __FUNCTION__, parent, owner, outlineType, errormsg ) )
+ return false;
+#endif
+
+ owner = aOwner;
+ return true;
+}
+
+bool BOARD_OUTLINE::IsSingle( void )
+{
+ return single;
+}
+
+void BOARD_OUTLINE::clearOutlines( void )
+{
+ std::list< IDF_OUTLINE* >::iterator itS = outlines.begin();
+ std::list< IDF_OUTLINE* >::iterator itE = outlines.end();
+
+ while( itS != itE )
+ {
+ delete *itS;
+ ++itS;
+ }
+
+ outlines.clear();
+ return;
+}
+
+void BOARD_OUTLINE::AddComment( const std::string& aComment )
+{
+ if( aComment.empty() )
+ return;
+
+ comments.push_back( aComment );
+ return;
+}
+
+size_t BOARD_OUTLINE::CommentsSize( void )
+{
+ return comments.size();
+}
+
+std::list< std::string >* BOARD_OUTLINE::GetComments( void )
+{
+ return &comments;
+}
+
+const std::string* BOARD_OUTLINE::GetComment( size_t aIndex )
+{
+ if( aIndex >= comments.size() )
+ return NULL;
+
+ std::list< std::string >::iterator itS = comments.begin();
+
+ for( ; aIndex > 0; --aIndex )
+ ++itS;
+
+ return &(*itS);
+}
+
+bool BOARD_OUTLINE::DeleteComment( size_t aIndex )
+{
+ if( aIndex >= comments.size() )
+ return false;
+
+ std::list< std::string >::iterator itS = comments.begin();
+
+ for( ; aIndex > 0; --aIndex )
+ ++itS;
+
+ comments.erase( itS );
+ return true;
+}
+
+void BOARD_OUTLINE::ClearComments( void )
+{
+ comments.clear();
+ return;
+}
+
+
+/*
+ * CLASS: OTHER_OUTLINE
+ */
+OTHER_OUTLINE::OTHER_OUTLINE( IDF3_BOARD* aParent )
+{
+ setParent( aParent );
+ outlineType = OTLN_OTHER;
+ side = LYR_INVALID;
+ single = false;
+
+ return;
+}
+
+bool OTHER_OUTLINE::SetOutlineIdentifier( const std::string aUniqueID )
+{
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !CheckOwnership( __LINE__, __FUNCTION__, parent, owner, outlineType, errormsg ) )
+ return false;
+#endif
+
+ uniqueID = aUniqueID;
+
+ return true;
+}
+
+const std::string& OTHER_OUTLINE::GetOutlineIdentifier( void )
+{
+ return uniqueID;
+}
+
+bool OTHER_OUTLINE::SetSide( IDF3::IDF_LAYER aSide )
+{
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !CheckOwnership( __LINE__, __FUNCTION__, parent, owner, outlineType, errormsg ) )
+ return false;
+#endif
+
+ switch( aSide )
+ {
+ case LYR_TOP:
+ case LYR_BOTTOM:
+ side = aSide;
+ break;
+
+ default:
+ do{
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* BUG: invalid side (" << aSide << "); must be one of TOP/BOTTOM\n";
+ ostr << "* outline type: " << GetOutlineTypeString( outlineType );
+ errormsg = ostr.str();
+ } while( 0 );
+
+ side = LYR_INVALID;
+ return false;
+
+ break;
+ }
+
+ return true;
+}
+
+IDF3::IDF_LAYER OTHER_OUTLINE::GetSide( void )
+{
+ return side;
+}
+
+void OTHER_OUTLINE::readData( std::ifstream& aBoardFile, const std::string& aHeader,
+ IDF3::IDF_VERSION aIdfVersion )
+{
+ // OTHER_OUTLINE/VIA_KEEPOUT
+ // .OTHER_OUTLINE [OWNER]
+ // [outline identifier] [thickness] [board side: Top/Bot] {not present in VA\IA KEEPOUT}
+ // [outline]
+
+ // check RECORD 1
+ std::string token;
+ bool quoted = false;
+ int idx = 0;
+ std::streampos pos = aBoardFile.tellg();
+
+ if( !GetIDFString( aHeader, token, quoted, idx ) )
+ {
+ ostringstream ostr;
+ ostr << "\n* BUG: invalid invocation: blank header line\n";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( quoted )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: section names must not be in quotes\n";
+ ostr << "* line: '" << aHeader << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( outlineType == OTLN_OTHER )
+ {
+ if( !CompareToken( ".OTHER_OUTLINE", token ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* BUG: not an .OTHER outline\n";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+ }
+ else
+ {
+ if( !CompareToken( ".VIA_KEEPOUT", token ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* BUG: not a .VIA_KEEPOUT outline\n";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+ }
+
+ if( !GetIDFString( aHeader, token, quoted, idx ) )
+ {
+ if( aIdfVersion > IDF_V2 )
+ ERROR_IDF << "no OWNER; setting to UNOWNED\n";
+
+ owner = UNOWNED;
+ }
+ else
+ {
+ if( !ParseOwner( token, owner ) )
+ {
+ ERROR_IDF << "invalid OWNER (reverting to UNOWNED): " << token << "\n";
+ owner = UNOWNED;
+ }
+ }
+
+ std::string iline;
+ bool comment = false;
+
+ if( outlineType == OTLN_OTHER )
+ {
+ // check RECORD 2
+ // [outline identifier] [thickness] [board side: Top/Bot]
+ while( aBoardFile.good() && !FetchIDFLine( aBoardFile, iline, comment, pos ) );
+
+ if( ( !aBoardFile.good() && aBoardFile.eof() ) || iline.empty() )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: premature end\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ idx = 0;
+ if( comment )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: comment within .OTHER_OUTLINE section\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: no outline identifier\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ uniqueID = token;
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: no thickness\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ std::stringstream teststr;
+ teststr << token;
+
+ teststr >> thickness;
+ if( teststr.fail() )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: invalid thickness\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( unit == UNIT_THOU )
+ {
+ thickness *= IDF_THOU_TO_MM;
+ }
+ else if( ( aIdfVersion == IDF_V2 ) && ( unit == UNIT_TNM ) )
+ {
+ thickness *= IDF_TNM_TO_MM;
+ }
+ else if( unit != UNIT_MM )
+ {
+ ostringstream ostr;
+ ostr << "\n* BUG: invalid UNIT type: " << unit;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( aIdfVersion == IDF_V2 )
+ {
+ side = LYR_TOP;
+ }
+ else
+ {
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: no board side\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !ParseIDFLayer( token, side ) || ( side != LYR_TOP && side != LYR_BOTTOM ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: invalid side (must be TOP or BOTTOM only)\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+ }
+
+ }
+
+ // read RECORD 3 values
+ readOutlines( aBoardFile, aIdfVersion );
+
+ // check RECORD 4
+ while( aBoardFile.good() && !FetchIDFLine( aBoardFile, iline, comment, pos ) );
+
+ if( ( !aBoardFile.good() && aBoardFile.eof() ) || iline.empty() )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: premature end\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ idx = 0;
+ if( comment )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: comment within section\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( outlineType == OTLN_OTHER )
+ {
+ if( !CompareToken( ".END_OTHER_OUTLINE", iline ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: no .END_OTHER_OUTLINE found\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+ }
+ else
+ {
+ if( !CompareToken( ".END_VIA_KEEPOUT", iline ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: no .END_VIA_KEEPOUT found\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+ }
+
+ return;
+}
+
+void OTHER_OUTLINE::writeData( std::ofstream& aBoardFile )
+{
+ // this section is optional; do not write if not required
+ if( outlines.empty() )
+ return;
+
+ writeComments( aBoardFile );
+
+ // write RECORD 1
+ if( outlineType == OTLN_OTHER )
+ aBoardFile << ".OTHER_OUTLINE ";
+ else
+ aBoardFile << ".VIA_KEEPOUT ";
+
+ writeOwner( aBoardFile );
+
+ // write RECORD 2
+ if( outlineType == OTLN_OTHER )
+ {
+ aBoardFile << "\"" << uniqueID << "\" ";
+
+ if( unit != UNIT_THOU )
+ aBoardFile << setiosflags(ios::fixed) << setprecision(5) << thickness << " ";
+ else
+ aBoardFile << setiosflags(ios::fixed) << setprecision(1) << (thickness / IDF_THOU_TO_MM) << " ";
+
+ switch( side )
+ {
+ case LYR_TOP:
+ case LYR_BOTTOM:
+ WriteLayersText( aBoardFile, side );
+ break;
+
+ default:
+ do{
+ ostringstream ostr;
+ ostr << "\n* invalid OTHER_OUTLINE side (neither top nor bottom): ";
+ ostr << side;
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ } while( 0 );
+
+ break;
+ }
+ }
+
+ // write RECORD 3
+ writeOutlines( aBoardFile );
+
+ // write RECORD 4
+ if( outlineType == OTLN_OTHER )
+ aBoardFile << ".END_OTHER_OUTLINE\n\n";
+ else
+ aBoardFile << ".END_VIA_KEEPOUT\n\n";
+
+ return;
+}
+
+
+bool OTHER_OUTLINE::Clear( void )
+{
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !CheckOwnership( __LINE__, __FUNCTION__, parent, owner, outlineType, errormsg ) )
+ return false;
+#endif
+
+ clear();
+ side = LYR_INVALID;
+ uniqueID.clear();
+
+ return true;
+}
+
+
+/*
+ * CLASS: ROUTE_OUTLINE
+ */
+ROUTE_OUTLINE::ROUTE_OUTLINE( IDF3_BOARD* aParent )
+{
+ setParent( aParent );
+ outlineType = OTLN_ROUTE;
+ single = true;
+ layers = LYR_INVALID;
+}
+
+bool ROUTE_OUTLINE::SetLayers( IDF3::IDF_LAYER aLayer )
+{
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !CheckOwnership( __LINE__, __FUNCTION__, parent, owner, outlineType, errormsg ) )
+ return false;
+#endif
+
+ layers = aLayer;
+
+ return true;
+}
+
+IDF3::IDF_LAYER ROUTE_OUTLINE::GetLayers( void )
+{
+ return layers;
+}
+
+void ROUTE_OUTLINE::readData( std::ifstream& aBoardFile, const std::string& aHeader,
+ IDF3::IDF_VERSION aIdfVersion )
+{
+ // ROUTE_OUTLINE (or ROUTE_KEEPOUT)
+ // .ROUTE_OUTLINE [OWNER]
+ // [layers]
+ // [outline]
+
+ // check RECORD 1
+ std::string token;
+ bool quoted = false;
+ int idx = 0;
+ std::streampos pos = aBoardFile.tellg();
+
+ if( !GetIDFString( aHeader, token, quoted, idx ) )
+ {
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "\n* BUG: invalid invocation; blank header line" ) );
+ }
+
+ if( quoted )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: section names must not be in quotes\n";
+ ostr << "* line: '" << aHeader << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( outlineType == OTLN_ROUTE )
+ {
+ if( !CompareToken( ".ROUTE_OUTLINE", token ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "\n* BUG: not a ROUTE outline" ) );
+ }
+ else
+ {
+ if( !CompareToken( ".ROUTE_KEEPOUT", token ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "\n* BUG: not a ROUTE KEEPOUT outline" ) );
+ }
+
+ if( !GetIDFString( aHeader, token, quoted, idx ) )
+ {
+ if( aIdfVersion > IDF_V2 )
+ ERROR_IDF << "no OWNER; setting to UNOWNED\n";
+
+ owner = UNOWNED;
+ }
+ else
+ {
+ if( !ParseOwner( token, owner ) )
+ {
+ ERROR_IDF << "invalid OWNER (reverting to UNOWNED): " << token << "\n";
+ owner = UNOWNED;
+ }
+ }
+
+ // check RECORD 2
+ // [layers: TOP, BOTTOM, BOTH, INNER, ALL]
+ std::string iline;
+ bool comment = false;
+
+ if( aIdfVersion > IDF_V2 || outlineType == OTLN_ROUTE_KEEPOUT )
+ {
+ while( aBoardFile.good() && !FetchIDFLine( aBoardFile, iline, comment, pos ) );
+
+ if( !aBoardFile.good() )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: premature end\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ idx = 0;
+ if( comment )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: comment within a section\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: no layers specification\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( quoted )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: layers specification must not be in quotes\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !ParseIDFLayer( token, layers ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: invalid layers specification\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( aIdfVersion == IDF_V2 )
+ {
+ if( layers == LYR_INNER || layers == LYR_ALL )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: IDFv2 allows only TOP/BOTTOM/BOTH; layer was '";
+ ostr << token << "'\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+ }
+
+ } // RECORD 2, conditional > IDFv2 or ROUTE_KO_OUTLINE
+ else
+ {
+ layers = LYR_ALL;
+ }
+
+ // read RECORD 3 values
+ readOutlines( aBoardFile, aIdfVersion );
+
+ // check RECORD 4
+ while( aBoardFile.good() && !FetchIDFLine( aBoardFile, iline, comment, pos ) );
+
+ if( ( !aBoardFile.good() && aBoardFile.eof() ) || iline.empty() )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: premature end\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ idx = 0;
+ if( comment )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: comment within section\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( outlineType == OTLN_ROUTE )
+ {
+ if( !CompareToken( ".END_ROUTE_OUTLINE", iline ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: no .END_ROUTE_OUTLINE found\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+ }
+ else
+ {
+ if( !CompareToken( ".END_ROUTE_KEEPOUT", iline ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: no .END_ROUTE_KEEPOUT found\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+ }
+
+ return;
+}
+
+
+void ROUTE_OUTLINE::writeData( std::ofstream& aBoardFile )
+{
+ // this section is optional; do not write if not required
+ if( outlines.empty() )
+ return;
+
+ if( layers == LYR_INVALID )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "layer not specified" ) );
+
+ writeComments( aBoardFile );
+
+ // write RECORD 1
+ if( outlineType == OTLN_ROUTE )
+ aBoardFile << ".ROUTE_OUTLINE ";
+ else
+ aBoardFile << ".ROUTE_KEEPOUT ";
+
+ writeOwner( aBoardFile );
+
+ // write RECORD 2
+ WriteLayersText( aBoardFile, layers );
+ aBoardFile << "\n";
+
+ // write RECORD 3
+ writeOutlines( aBoardFile );
+
+ // write RECORD 4
+ if( outlineType == OTLN_ROUTE )
+ aBoardFile << ".END_ROUTE_OUTLINE\n\n";
+ else
+ aBoardFile << ".END_ROUTE_KEEPOUT\n\n";
+
+ return;
+}
+
+
+bool ROUTE_OUTLINE::Clear( void )
+{
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !CheckOwnership( __LINE__, __FUNCTION__, parent, owner, outlineType, errormsg ) )
+ return false;
+#endif
+
+ clear();
+ layers = LYR_INVALID;
+
+ return true;
+}
+
+
+/*
+ * CLASS: PLACE_OUTLINE
+ */
+PLACE_OUTLINE::PLACE_OUTLINE( IDF3_BOARD* aParent )
+{
+ setParent( aParent );
+ outlineType = OTLN_PLACE;
+ single = true;
+ thickness = -1.0;
+ side = LYR_INVALID;
+}
+
+
+bool PLACE_OUTLINE::SetSide( IDF3::IDF_LAYER aSide )
+{
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !CheckOwnership( __LINE__, __FUNCTION__, parent, owner, outlineType, errormsg ) )
+ return false;
+#endif
+
+ switch( aSide )
+ {
+ case LYR_TOP:
+ case LYR_BOTTOM:
+ case LYR_BOTH:
+ side = aSide;
+ break;
+
+ default:
+ do{
+ side = LYR_INVALID;
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* BUG: invalid layer (" << aSide << "): must be one of TOP/BOTTOM/BOTH\n";
+ ostr << "* outline type: " << GetOutlineTypeString( outlineType );
+ errormsg = ostr.str();
+
+ return false;
+ } while( 0 );
+
+ break;
+ }
+
+ return true;
+}
+
+
+IDF3::IDF_LAYER PLACE_OUTLINE::GetSide( void )
+{
+ return side;
+}
+
+
+bool PLACE_OUTLINE::SetMaxHeight( double aHeight )
+{
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !CheckOwnership( __LINE__, __FUNCTION__, parent, owner, outlineType, errormsg ) )
+ return false;
+#endif
+
+ if( aHeight < 0.0 )
+ {
+ thickness = 0.0;
+
+ do{
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* BUG: invalid height (" << aHeight << "): must be >= 0.0";
+ ostr << "* outline type: " << GetOutlineTypeString( outlineType );
+ errormsg = ostr.str();
+
+ return false;
+ } while( 0 );
+ }
+
+ thickness = aHeight;
+ return true;
+}
+
+double PLACE_OUTLINE::GetMaxHeight( void )
+{
+ return thickness;
+}
+
+void PLACE_OUTLINE::readData( std::ifstream& aBoardFile, const std::string& aHeader,
+ IDF3::IDF_VERSION aIdfVersion )
+{
+ // PLACE_OUTLINE/KEEPOUT
+ // .PLACE_OUTLINE [OWNER]
+ // [board side: Top/Bot/Both] [height]
+ // [outline]
+
+ // check RECORD 1
+ std::string token;
+ bool quoted = false;
+ int idx = 0;
+ std::streampos pos = aBoardFile.tellg();
+
+ if( !GetIDFString( aHeader, token, quoted, idx ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "\n* BUG: invalid invocation: blank header line\n" ) );
+
+ if( quoted )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: section name must not be in quotes\n";
+ ostr << "* line: '" << aHeader << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( outlineType == OTLN_PLACE )
+ {
+ if( !CompareToken( ".PLACE_OUTLINE", token ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "\n* BUG: not a .PLACE_OUTLINE" ) );
+ }
+ else
+ {
+ if( !CompareToken( ".PLACE_KEEPOUT", token ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "\n* BUG: not a .PLACE_KEEPOUT" ) );
+ }
+
+ if( !GetIDFString( aHeader, token, quoted, idx ) )
+ {
+ if( aIdfVersion > IDF_V2 )
+ ERROR_IDF << "no OWNER; setting to UNOWNED\n";
+
+ owner = UNOWNED;
+ }
+ else
+ {
+ if( !ParseOwner( token, owner ) )
+ {
+ ERROR_IDF << "invalid OWNER (reverting to UNOWNED): " << token << "\n";
+ owner = UNOWNED;
+ }
+ }
+
+ // check RECORD 2
+ // [board side: Top/Bot/Both] [height]
+ std::string iline;
+ bool comment = false;
+
+ if( aIdfVersion > IDF_V2 || outlineType == OTLN_PLACE_KEEPOUT )
+ {
+ while( aBoardFile.good() && !FetchIDFLine( aBoardFile, iline, comment, pos ) );
+
+ if( !aBoardFile.good() )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: premature end\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ idx = 0;
+ if( comment )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: comment within the section\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: no board side information\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !ParseIDFLayer( token, side ) ||
+ ( side != LYR_TOP && side != LYR_BOTTOM && side != LYR_BOTH ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: invalid board side: must be one of TOP/BOTTOM/BOTH\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( GetIDFString( iline, token, quoted, idx ) )
+ {
+ std::stringstream teststr;
+ teststr << token;
+
+ teststr >> thickness;
+
+ if( teststr.fail() )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: invalid height\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( thickness < 0.0 )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: thickness < 0\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( unit == UNIT_THOU )
+ {
+ thickness *= IDF_THOU_TO_MM;
+ }
+ else if( ( aIdfVersion == IDF_V2 ) && ( unit == UNIT_TNM ) )
+ {
+ thickness *= IDF_TNM_TO_MM;
+ }
+ else if( unit != UNIT_MM )
+ {
+ ostringstream ostr;
+ ostr << "\n* BUG: invalid UNIT type: " << unit;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( thickness < 0.0 )
+ thickness = 0.0;
+
+ }
+ else
+ {
+ // for OTLN_PLACE, thickness may be omitted, but is required for OTLN_PLACE_KEEPOUT
+ if( outlineType == OTLN_PLACE_KEEPOUT )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: missing thickness\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ thickness = -1.0;
+ }
+ }
+ else
+ {
+ side = LYR_TOP;
+ thickness = 0.0;
+ }
+
+ // read RECORD 3 values
+ readOutlines( aBoardFile, aIdfVersion );
+
+ // check RECORD 4
+ while( aBoardFile.good() && !FetchIDFLine( aBoardFile, iline, comment, pos ) );
+
+ if( ( !aBoardFile.good() && aBoardFile.eof() ) || iline.empty() )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: premature end\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ idx = 0;
+ if( comment )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: comment within section\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( outlineType == OTLN_PLACE )
+ {
+ if( !GetIDFString( iline, token, quoted, idx )
+ || !CompareToken( ".END_PLACE_OUTLINE", token ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid .PLACE_OUTLINE section: no .END_PLACE_OUTLINE found" ) );
+ }
+ else
+ {
+ if( !GetIDFString( iline, token, quoted, idx )
+ || !CompareToken( ".END_PLACE_KEEPOUT", token ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid .PLACE_KEEPOUT section: no .END_PLACE_KEEPOUT found" ) );
+ }
+
+ return;
+}
+
+void PLACE_OUTLINE::writeData( std::ofstream& aBoardFile )
+{
+ // this section is optional; do not write if not required
+ if( outlines.empty() )
+ return;
+
+ writeComments( aBoardFile );
+
+ // write RECORD 1
+ if( outlineType == OTLN_PLACE )
+ aBoardFile << ".PLACE_OUTLINE ";
+ else
+ aBoardFile << ".PLACE_KEEPOUT ";
+
+ writeOwner( aBoardFile );
+
+ // write RECORD 2
+ switch( side )
+ {
+ case LYR_TOP:
+ case LYR_BOTTOM:
+ case LYR_BOTH:
+ WriteLayersText( aBoardFile, side );
+ break;
+
+ default:
+ do
+ {
+ ostringstream ostr;
+ ostr << "\n* invalid PLACE_OUTLINE/KEEPOUT side (";
+ ostr << side << "); must be one of TOP/BOTTOM/BOTH";
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ } while( 0 );
+
+ break;
+ }
+
+ // thickness is optional for OTLN_PLACE, but mandatory for OTLN_PLACE_KEEPOUT
+ if( thickness < 0.0 && outlineType == OTLN_PLACE_KEEPOUT)
+ {
+ aBoardFile << "\n";
+ }
+ else
+ {
+ aBoardFile << " ";
+
+ if( unit != UNIT_THOU )
+ aBoardFile << setiosflags(ios::fixed) << setprecision(5) << thickness << "\n";
+ else
+ aBoardFile << setiosflags(ios::fixed) << setprecision(1) << (thickness / IDF_THOU_TO_MM) << "\n";
+ }
+
+ // write RECORD 3
+ writeOutlines( aBoardFile );
+
+ // write RECORD 4
+ if( outlineType == OTLN_PLACE )
+ aBoardFile << ".END_PLACE_OUTLINE\n\n";
+ else
+ aBoardFile << ".END_PLACE_KEEPOUT\n\n";
+
+ return;
+}
+
+
+bool PLACE_OUTLINE::Clear( void )
+{
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !CheckOwnership( __LINE__, __FUNCTION__, parent, owner, outlineType, errormsg ) )
+ return false;
+#endif
+
+ clear();
+ thickness = 0.0;
+ side = LYR_INVALID;
+
+ return true;
+}
+
+
+/*
+ * CLASS: ROUTE_KEEPOUT
+ */
+ROUTE_KO_OUTLINE::ROUTE_KO_OUTLINE( IDF3_BOARD* aParent )
+ : ROUTE_OUTLINE( aParent )
+{
+ outlineType = OTLN_ROUTE_KEEPOUT;
+ return;
+}
+
+
+/*
+ * CLASS: PLACE_KEEPOUT
+ */
+PLACE_KO_OUTLINE::PLACE_KO_OUTLINE( IDF3_BOARD* aParent )
+ : PLACE_OUTLINE( aParent )
+{
+ outlineType = OTLN_PLACE_KEEPOUT;
+ return;
+}
+
+
+/*
+ * CLASS: VIA_KEEPOUT
+ */
+VIA_KO_OUTLINE::VIA_KO_OUTLINE( IDF3_BOARD* aParent )
+ : OTHER_OUTLINE( aParent )
+{
+ single = true;
+ outlineType = OTLN_VIA_KEEPOUT;
+}
+
+
+/*
+ * CLASS: PLACEMENT GROUP (PLACE_REGION)
+ */
+GROUP_OUTLINE::GROUP_OUTLINE( IDF3_BOARD* aParent )
+{
+ setParent( aParent );
+ outlineType = OTLN_GROUP_PLACE;
+ thickness = 0.0;
+ side = LYR_INVALID;
+ single = true;
+ return;
+}
+
+
+bool GROUP_OUTLINE::SetSide( IDF3::IDF_LAYER aSide )
+{
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !CheckOwnership( __LINE__, __FUNCTION__, parent, owner, outlineType, errormsg ) )
+ return false;
+#endif
+
+ switch( aSide )
+ {
+ case LYR_TOP:
+ case LYR_BOTTOM:
+ case LYR_BOTH:
+ side = aSide;
+ break;
+
+ default:
+ do{
+ ostringstream ostr;
+ ostr << "invalid side (" << aSide << "); must be one of TOP/BOTTOM/BOTH\n";
+ ostr << "* outline type: " << GetOutlineTypeString( outlineType );
+ errormsg = ostr.str();
+
+ return false;
+ } while( 0 );
+
+ break;
+ }
+
+ return true;
+}
+
+
+IDF3::IDF_LAYER GROUP_OUTLINE::GetSide( void )
+{
+ return side;
+}
+
+
+bool GROUP_OUTLINE::SetGroupName( std::string aGroupName )
+{
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !CheckOwnership( __LINE__, __FUNCTION__, parent, owner, outlineType, errormsg ) )
+ return false;
+#endif
+
+ groupName = aGroupName;
+
+ return true;
+}
+
+
+const std::string& GROUP_OUTLINE::GetGroupName( void )
+{
+ return groupName;
+}
+
+
+void GROUP_OUTLINE::readData( std::ifstream& aBoardFile, const std::string& aHeader,
+ IDF3::IDF_VERSION aIdfVersion )
+{
+ // Placement Group
+ // .PLACE_REGION [OWNER]
+ // [side: Top/Bot/Both ] [component group name]
+ // [outline]
+
+ // check RECORD 1
+ std::string token;
+ bool quoted = false;
+ int idx = 0;
+ std::streampos pos = aBoardFile.tellg();
+
+ if( !GetIDFString( aHeader, token, quoted, idx ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "\n* BUG: invalid invocation: blank header line" ) );
+
+ if( quoted )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: section name must not be in quotes\n";
+ ostr << "* line: '" << aHeader << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !CompareToken( ".PLACE_REGION", token ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "\n* BUG: not a .PLACE_REGION" ) );
+
+ if( !GetIDFString( aHeader, token, quoted, idx ) )
+ {
+ if( aIdfVersion > IDF_V2 )
+ ERROR_IDF << "no OWNER; setting to UNOWNED\n";
+
+ owner = UNOWNED;
+ }
+ else
+ {
+ if( !ParseOwner( token, owner ) )
+ {
+ ERROR_IDF << "invalid OWNER (reverting to UNOWNED): " << token << "\n";
+ owner = UNOWNED;
+ }
+ }
+
+ std::string iline;
+ bool comment = false;
+
+ // check RECORD 2
+ // [side: Top/Bot/Both ] [component group name]
+ while( aBoardFile.good() && !FetchIDFLine( aBoardFile, iline, comment, pos ) );
+
+ if( !aBoardFile.good() )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: premature end\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ idx = 0;
+ if( comment )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: comment within section\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: no board side specified\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !ParseIDFLayer( token, side ) ||
+ ( side != LYR_TOP && side != LYR_BOTTOM && side != LYR_BOTH ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: invalid board side, must be one of TOP/BOTTOM/BOTH\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: no outline identifier\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ groupName = token;
+
+ // read RECORD 3 values
+ readOutlines( aBoardFile, aIdfVersion );
+
+ // check RECORD 4
+ while( aBoardFile.good() && !FetchIDFLine( aBoardFile, iline, comment, pos ) );
+
+ if( ( !aBoardFile.good() && aBoardFile.eof() ) || iline.empty() )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: premature end\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ idx = 0;
+ if( comment )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: comment within section\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !GetIDFString( iline, token, quoted, idx )
+ || !CompareToken( ".END_PLACE_REGION", token ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "\n* invalid .PLACE_REGION section: no .END_PLACE_REGION found" ) );
+
+ return;
+}
+
+
+void GROUP_OUTLINE::writeData( std::ofstream& aBoardFile )
+{
+ // this section is optional; do not write if not required
+ if( outlines.empty() )
+ return;
+
+ writeComments( aBoardFile );
+
+ // write RECORD 1
+ aBoardFile << ".PLACE_REGION ";
+
+ writeOwner( aBoardFile );
+
+ // write RECORD 2
+ switch( side )
+ {
+ case LYR_TOP:
+ case LYR_BOTTOM:
+ case LYR_BOTH:
+ WriteLayersText( aBoardFile, side );
+ break;
+
+ default:
+ do{
+ ostringstream ostr;
+ ostr << "\n* invalid PLACE_REGION side (must be TOP/BOTTOM/BOTH): ";
+ ostr << side;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ } while( 0 );
+
+ break;
+ }
+
+ aBoardFile << " \"" << groupName << "\"\n";
+
+ // write RECORD 3
+ writeOutlines( aBoardFile );
+
+ // write RECORD 4
+ aBoardFile << ".END_PLACE_REGION\n\n";
+
+ return;
+}
+
+bool GROUP_OUTLINE::Clear( void )
+{
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !CheckOwnership( __LINE__, __FUNCTION__, parent, owner, outlineType, errormsg ) )
+ return false;
+#endif
+
+ clear();
+ thickness = 0.0;
+ side = LYR_INVALID;
+ groupName.clear();
+
+ return true;
+}
+
+/*
+ * CLASS: COMPONENT OUTLINE
+ */
+IDF3_COMP_OUTLINE::IDF3_COMP_OUTLINE( IDF3_BOARD* aParent )
+{
+ setParent( aParent );
+ single = true;
+ outlineType = OTLN_COMPONENT;
+ compType = COMP_INVALID;
+ refNum = 0;
+ return;
+}
+
+void IDF3_COMP_OUTLINE::readProperties( std::ifstream& aLibFile )
+{
+ bool quoted = false;
+ bool comment = false;
+ std::string iline;
+ std::string token;
+ std::streampos pos;
+ std::string pname; // property name
+ std::string pval; // property value
+ int idx = 0;
+
+ while( aLibFile.good() )
+ {
+ if( !FetchIDFLine( aLibFile, iline, comment, pos ) )
+ continue;
+
+ idx = 0;
+
+ if( comment )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: comment within section\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: bad property section (no PROP)\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( quoted )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: PROP or .END must not be quoted\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( token.size() >= 5 && CompareToken( ".END_", token.substr( 0, 5 ) ) )
+ {
+ if(aLibFile.eof())
+ aLibFile.clear();
+
+ aLibFile.seekg( pos );
+ return;
+ }
+
+ if( !CompareToken( "PROP", token ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: expecting PROP or .END_ELECTRICAL\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: no PROP name\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ pname = token;
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: no PROP value\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ pval = token;
+
+ if( props.insert( pair< string, string >(pname, pval) ).second == false )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: duplicate property name \"" << pname << "\"\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+ }
+
+ return;
+}
+
+
+bool IDF3_COMP_OUTLINE::writeProperties( std::ofstream& aLibFile )
+{
+ if( props.empty() )
+ return true;
+ std::map< std::string, std::string >::const_iterator itS = props.begin();
+ std::map< std::string, std::string >::const_iterator itE = props.end();
+
+ while( itS != itE )
+ {
+ aLibFile << "PROP " << "\"" << itS->first << "\" \""
+ << itS->second << "\"\n";
+ ++itS;
+ }
+
+ return !aLibFile.fail();
+}
+
+void IDF3_COMP_OUTLINE::readData( std::ifstream& aLibFile, const std::string& aHeader,
+ IDF3::IDF_VERSION aIdfVersion )
+{
+ // .ELECTRICAL/.MECHANICAL
+ // [GEOM] [PART] [UNIT] [HEIGHT]
+ // [outline]
+ // [PROP] [prop name] [prop value]
+ // check RECORD 1
+ std::string token;
+ bool quoted = false;
+ int idx = 0;
+ std::streampos pos = aLibFile.tellg();
+
+ if( !GetIDFString( aHeader, token, quoted, idx ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "\n* BUG: invalid invocation: blank header line" ) );
+
+ if( quoted )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: section name must not be in quotes\n";
+ ostr << "* line: '" << aHeader << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( CompareToken( ".ELECTRICAL", token ) )
+ {
+ compType = COMP_ELEC;
+ }
+ else if( CompareToken( ".MECHANICAL", token ) )
+ {
+ compType = COMP_MECH;
+ }
+ else
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: expecting .ELECTRICAL or .MECHANICAL header\n";
+ ostr << "* line: '" << aHeader << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ // check RECORD 2
+ // [GEOM] [PART] [UNIT] [HEIGHT]
+ std::string iline;
+ bool comment = false;
+
+ while( aLibFile.good() && !FetchIDFLine( aLibFile, iline, comment, pos ) );
+
+ if( !aLibFile.good() )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: premature end\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ idx = 0;
+ if( comment )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: comment within section\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: no GEOMETRY NAME\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ geometry = token;
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: no PART NAME\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ part = token;
+
+ if( part.empty() && geometry.empty() )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: both GEOMETRY and PART names are empty\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: no UNIT type\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( CompareToken( "MM", token ) )
+ {
+ unit = UNIT_MM;
+ }
+ else if( CompareToken( "THOU", token ) )
+ {
+ unit = UNIT_THOU;
+ }
+ else if( aIdfVersion == IDF_V2 && !CompareToken( "TNM", token ) )
+ {
+ unit = UNIT_TNM;
+ }
+ else
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: invalid UNIT '" << token << "': must be one of MM or THOU\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: no height specified\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ std::istringstream teststr;
+ teststr.str( token );
+
+ teststr >> thickness;
+ if( teststr.fail() )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: invalid height '" << token << "'\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( unit == UNIT_THOU )
+ {
+ thickness *= IDF_THOU_TO_MM;
+ }
+ else if( ( aIdfVersion == IDF_V2 ) && ( unit == UNIT_TNM ) )
+ {
+ thickness *= IDF_TNM_TO_MM;
+ }
+ else if( unit != UNIT_MM )
+ {
+ ostringstream ostr;
+ ostr << "\n* BUG: invalid UNIT type: " << unit;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ // read RECORD 3 values
+ readOutlines( aLibFile, aIdfVersion );
+
+ if( compType == COMP_ELEC && aIdfVersion > IDF_V2 )
+ readProperties( aLibFile );
+
+ // check RECORD 4
+ while( aLibFile.good() && !FetchIDFLine( aLibFile, iline, comment, pos ) );
+
+ if( ( !aLibFile.good() && aLibFile.eof() ) || iline.empty() )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: premature end\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ idx = 0;
+ if( comment )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: comment within section\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( compType == COMP_ELEC )
+ {
+ if( !CompareToken( ".END_ELECTRICAL", iline ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: no .END_ELECTRICAL found\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+ }
+ else
+ {
+ if( !CompareToken( ".END_MECHANICAL", iline ) )
+ {
+ ostringstream ostr;
+
+ ostr << "\n* invalid outline: " << GetOutlineTypeString( outlineType ) << "\n";
+ ostr << "* violation: no .END_MECHANICAL found\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+ }
+
+ return;
+}
+
+
+void IDF3_COMP_OUTLINE::writeData( std::ofstream& aLibFile )
+{
+ if( refNum == 0 )
+ return; // nothing to do
+
+ if( compType != COMP_ELEC && compType != COMP_MECH )
+ {
+ ostringstream ostr;
+ ostr << "\n* component type not set or invalid: " << compType;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ writeComments( aLibFile );
+
+ // note: the outline section is required, even if it is empty
+ if( compType == COMP_ELEC )
+ aLibFile << ".ELECTRICAL\n";
+ else
+ aLibFile << ".MECHANICAL\n";
+
+ // RECORD 2
+ // [GEOM] [PART] [UNIT] [HEIGHT]
+ aLibFile << "\"" << geometry << "\" \"" << part << "\" ";
+
+ if( unit != UNIT_THOU )
+ aLibFile << "MM " << setiosflags(ios::fixed) << setprecision(5) << thickness << "\n";
+ else
+ aLibFile << "THOU " << setiosflags(ios::fixed) << setprecision(1) << (thickness / IDF_THOU_TO_MM) << "\n";
+
+ writeOutlines( aLibFile );
+
+ if( compType == COMP_ELEC )
+ {
+ writeProperties( aLibFile );
+ aLibFile << ".END_ELECTRICAL\n\n";
+ }
+ else
+ {
+ aLibFile << ".END_MECHANICAL\n\n";
+ }
+
+ return;
+}
+
+
+bool IDF3_COMP_OUTLINE::Clear( void )
+{
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !CheckOwnership( __LINE__, __FUNCTION__, parent, owner, outlineType, errormsg ) )
+ return false;
+#endif
+
+ clear();
+ uid.clear();
+ geometry.clear();
+ part.clear();
+ compType = COMP_INVALID;
+ refNum = 0;
+ props.clear();
+
+ return true;
+}
+
+bool IDF3_COMP_OUTLINE::SetComponentClass( IDF3::COMP_TYPE aCompClass )
+{
+ switch( aCompClass )
+ {
+ case COMP_ELEC:
+ case COMP_MECH:
+ compType = aCompClass;
+ break;
+
+ default:
+ do{
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* BUG: invalid component class (must be ELECTRICAL or MECHANICAL): ";
+ ostr << aCompClass << "\n";
+ errormsg = ostr.str();
+
+ return false;
+ } while( 0 );
+
+ break;
+ }
+
+ return true;
+}
+
+
+IDF3::COMP_TYPE IDF3_COMP_OUTLINE::GetComponentClass( void )
+{
+ return compType;
+}
+
+
+void IDF3_COMP_OUTLINE::SetGeomName( const std::string& aGeomName )
+{
+ geometry = aGeomName;
+ uid.clear();
+ return;
+}
+
+const std::string& IDF3_COMP_OUTLINE::GetGeomName( void )
+{
+ return geometry;
+}
+
+void IDF3_COMP_OUTLINE::SetPartName( const std::string& aPartName )
+{
+ part = aPartName;
+ uid.clear();
+ return;
+}
+
+const std::string& IDF3_COMP_OUTLINE::GetPartName( void )
+{
+ return part;
+}
+
+const std::string& IDF3_COMP_OUTLINE::GetUID( void )
+{
+ if( !uid.empty() )
+ return uid;
+
+ if( geometry.empty() && part.empty() )
+ return uid;
+
+ uid = geometry + "_" + part;
+
+ return uid;
+}
+
+
+int IDF3_COMP_OUTLINE::incrementRef( void )
+{
+ return ++refNum;
+}
+
+int IDF3_COMP_OUTLINE::decrementRef( void )
+{
+ if( refNum == 0 )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* BUG: decrementing refNum beyond 0";
+ errormsg = ostr.str();
+
+ return -1;
+ }
+
+ --refNum;
+ return refNum;
+}
+
+bool IDF3_COMP_OUTLINE::CreateDefaultOutline( const std::string &aGeom, const std::string &aPart )
+{
+ Clear();
+
+ if( aGeom.empty() && aPart.empty() )
+ {
+ geometry = "NOGEOM";
+ part = "NOPART";
+ uid = "NOGEOM_NOPART";
+ }
+ else
+ {
+ geometry = aGeom;
+ part = aPart;
+ uid = aGeom + "_" + aPart;
+ }
+
+ compType = COMP_ELEC;
+ thickness = 5.0;
+ unit = UNIT_MM;
+
+ // Create a star shape 5mm high with points on 5 and 3 mm circles
+ double a, da;
+ da = M_PI / 5.0;
+ a = da / 2.0;
+
+ IDF_POINT p1, p2;
+ IDF_OUTLINE* ol = new IDF_OUTLINE;
+ IDF_SEGMENT* sp;
+
+ p1.x = 1.5 * cos( a );
+ p1.y = 1.5 * sin( a );
+
+ if( ol == NULL )
+ return false;
+
+ for( int i = 0; i < 10; ++i )
+ {
+ if( i & 1 )
+ {
+ p2.x = 2.5 * cos( a );
+ p2.y = 2.5 * sin( a );
+ }
+ else
+ {
+ p2.x = 1.5 * cos( a );
+ p2.y = 1.5 * sin( a );
+ }
+
+ sp = new IDF_SEGMENT( p1, p2 );
+
+ if( sp == NULL )
+ {
+ Clear();
+ return false;
+ }
+
+ ol->push( sp );
+ a += da;
+ p1 = p2;
+ }
+
+ a = da / 2.0;
+ p2.x = 1.5 * cos( a );
+ p2.y = 1.5 * sin( a );
+
+ sp = new IDF_SEGMENT( p1, p2 );
+
+ if( sp == NULL )
+ {
+ Clear();
+ return false;
+ }
+
+ ol->push( sp );
+ outlines.push_back( ol );
+
+ return true;
+}
diff --git a/utils/idftools/idf_outlines.h b/utils/idftools/idf_outlines.h
new file mode 100644
index 0000000..33957e7
--- /dev/null
+++ b/utils/idftools/idf_outlines.h
@@ -0,0 +1,771 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2014 Cirilo Bernardo
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+
+#ifndef IDF_OUTLINES_H
+#define IDF_OUTLINES_H
+
+#include <string>
+#include <list>
+#include <map>
+#include <wx/string.h>
+#include <wx/filename.h>
+
+#include <idf_common.h>
+
+/*
+ * NOTES ON OUTLINE TYPES:
+ *
+ * BOARD_OUTLINE (PANEL_OUTLINE)
+ * .BOARD_OUTLINE [OWNER]
+ * [thickness]
+ * [outlines]
+ *
+ * OTHER_OUTLINE
+ * .OTHER_OUTLINE [OWNER]
+ * [outline identifier] [thickness] [board side: Top/Bot]
+ * [outline]
+ *
+ * ROUTE_OUTLINE
+ * .ROUTE_OUTLINE [OWNER]
+ * [layers]
+ * [outline]
+ *
+ * PLACE_OUTLINE
+ * .PLACE_OUTLINE [OWNER]
+ * [board side: Top/Bot/Both] [height]
+ * [outline]
+ *
+ * ROUTE_KEEPOUT
+ * .ROUTE_KEEPOUT [OWNER]
+ * [layers]
+ * [outline]
+ *
+ * VIA_KEEPOUT
+ * .VIA_KEEPOUT [OWNER]
+ * [outline]
+ *
+ * PLACE_KEEPOUT
+ * .PLACE_KEEPOUT [OWNER]
+ * [board side: Top/Bot/Both] [height]
+ * [outline]
+ *
+ * Placement Group
+ * .PLACE_REGION [OWNER]
+ * [side: Top/Bot/Both ] [component group name]
+ * [outline]
+ *
+ * Component Outline:
+ * .ELECTRICAL/.MECHANICAL
+ * [GEOM] [PART] [UNIT] [HEIGHT]
+ * [outline]
+ * [PROP] [prop name] [prop value]
+ */
+
+class IDF3_BOARD;
+
+
+/**
+ * Class BOARD_OUTLINE
+ * supports the IDFv3 BOARD OUTLINE data and is the basis of other IDFv3 outline classes
+ */
+class BOARD_OUTLINE
+{
+friend class IDF3_BOARD;
+protected:
+ std::string errormsg;
+ std::list< IDF_OUTLINE* > outlines;
+ IDF3::KEY_OWNER owner; // indicates the owner of this outline (MCAD, ECAD, UNOWNED)
+ IDF3::OUTLINE_TYPE outlineType;// type of IDF outline
+ bool single; // true if only a single outline is accepted
+ std::list< std::string > comments; // associated comment list
+ IDF3::IDF_UNIT unit; // outline's native unit (MM or THOU)
+ IDF3_BOARD* parent; // BOARD which contains this outline
+ double thickness; // Board/Extrude Thickness or Height (IDF spec)
+
+ // Read outline data from a BOARD or LIBRARY file's outline section
+ void readOutlines( std::ifstream& aBoardFile, IDF3::IDF_VERSION aIdfVersion );
+ // Write comments to a BOARD or LIBRARY file (must not be within a SECTION as per IDFv3 spec)
+ bool writeComments( std::ofstream& aBoardFile );
+ // Write the outline owner to a BOARD file
+ bool writeOwner( std::ofstream& aBoardFile );
+ // Write the data of a single outline object
+ void writeOutline( std::ofstream& aBoardFile, IDF_OUTLINE* aOutline, size_t aIndex );
+ // Iterate through the outlines and write out all data
+ void writeOutlines( std::ofstream& aBoardFile ); // write outline data (no headers)
+ // Clear internal list of outlines
+ void clearOutlines( void );
+ /**
+ * Function SetParent
+ * sets the parent IDF_BOARD object
+ */
+ void setParent( IDF3_BOARD* aParent );
+
+ // Shadow routines used by friends to bypass ownership checks
+ bool addOutline( IDF_OUTLINE* aOutline );
+ virtual bool setThickness( double aThickness );
+ virtual void clear( void );
+
+ /**
+ * Function readData
+ * reads data from a .BOARD_OUTLINE section
+ * In case of an unrecoverable error an exception is thrown. On a successful
+ * return the file pointer will be at the line following .END_BOARD_OUTLINE
+ *
+ * @param aBoardFile is an IDFv3 file opened for reading
+ * @param aHeader is the ".BOARD_OUTLINE" header line as read by FetchIDFLine
+ */
+ virtual void readData( std::ifstream& aBoardFile, const std::string& aHeader,
+ IDF3::IDF_VERSION aIdfVersion );
+
+ /**
+ * Function writeData
+ * writes the comments and .BOARD_OUTLINE section to an IDFv3 file.
+ * Throws exceptions.
+ *
+ * @param aBoardFile is an IDFv3 file opened for writing
+ */
+ virtual void writeData( std::ofstream& aBoardFile );
+
+public:
+ BOARD_OUTLINE();
+ virtual ~BOARD_OUTLINE();
+
+ /**
+ * Function SetUnit
+ * sets the native unit of the outline; except for component outlines this must
+ * be the same as the native unit of the parent IDF_BOARD object
+ *
+ * @param aUnit is the native unit (UNIT_MM or UNIT_THOU)
+ */
+ virtual bool SetUnit( IDF3::IDF_UNIT aUnit );
+
+ /**
+ * Function GetUnit
+ * returns the native unit type of the outline
+ *
+ * @return IDF_UNIT is the native unit (UNIT_MM or UNIT_THOU)
+ */
+ virtual IDF3::IDF_UNIT GetUnit( void );
+
+ /**
+ * Function SetThickness
+ * sets the thickness or height of the outline (mm)
+ *
+ * @param aThickness is the thickness or height of the outline in mm
+ */
+ virtual bool SetThickness( double aThickness );
+
+ /**
+ * Function GetThickness
+ * returns the thickness or height of an outline (mm)
+ */
+ virtual double GetThickness( void );
+
+ /**
+ * Function Clear
+ * frees memory and reinitializes all internal data except for the parent pointer.
+ *
+ * @return bool: true if OK, false on ownership violations
+ */
+ virtual bool Clear( void );
+
+ /**
+ * Function GetOutlineType
+ * returns the type of outline according to the IDFv3 classification
+ */
+ IDF3::OUTLINE_TYPE GetOutlineType( void );
+
+ /**
+ * Function GetParent
+ * returns the parent IDF_BOARD object
+ */
+ IDF3_BOARD* GetParent( void );
+
+
+ /**
+ * Function AddOutline
+ * adds the specified outline to this object.
+ *
+ * @param aOutline is a valid IDF outline
+ *
+ * @return bool: true if the outline was added; false if the outline
+ * already existed or an ownership violation occurs.
+ */
+ bool AddOutline( IDF_OUTLINE* aOutline );
+
+ /**
+ * Function DelOutline( IDF_OUTLINE* aOutline )
+ * removes the given outline, subject to IDF ownership rules,
+ * if it is owned by this object. The outline pointer remains
+ * valid and it is the user's responsibility to delete the object.
+ * The first outline in the list will never be deleted unless it
+ * is the sole remaining outline; this is to ensure that a board
+ * outline is not removed while the cutouts remain.
+ *
+ * @param aOutline is a pointer to the outline to remove from the list
+ *
+ * @return bool: true if the outline was found and removed; false if
+ * the outline was not found or an ownership violation occurs.
+ */
+ bool DelOutline( IDF_OUTLINE* aOutline );
+
+ /**
+ * Function DelOutline( IDF_OUTLINE* aOutline )
+ * deletes the outline specified by the given index, subject to
+ * IDF ownership rules. The outline data is destroyed.
+ * The first outline in the list will never be deleted unless it
+ * is the sole remaining outline; this is to ensure that a board
+ * outline is not removed while the cutouts remain.
+ *
+ * @param aIndex is an index to the outline to delete
+ *
+ * @return bool: true if the outline was found and deleted; false if
+ * the outline was not found or an ownership violation or indexation
+ * error occurs.
+ */
+ bool DelOutline( size_t aIndex );
+
+ /**
+ * Function GetOutlines
+ * returns a pointer to the internal outlines list. It is up to the
+ * user to respect the IDFv3 specification and avoid changes to this
+ * list which are in violation of the specification.
+ */
+ const std::list< IDF_OUTLINE* >*const GetOutlines( void );
+
+ /**
+ * Function OutlinesSize
+ * returns the number of items in the internal outline list
+ */
+ size_t OutlinesSize( void );
+
+ /**
+ * Function GetOutline
+ * returns a pointer to the outline as specified by aIndex.
+ * If the index is out of bounds NULL is returned and the
+ * error message is set. It is the responsibility of the
+ * user to observe IDF ownership rules.
+ */
+ IDF_OUTLINE* GetOutline( size_t aIndex );
+
+ /**
+ * Function GetOwner
+ * returns the ownership status of the outline ( ECAD, MCAD, UNOWNED)
+ */
+ IDF3::KEY_OWNER GetOwner( void );
+
+ /**
+ * Function SetOwner
+ * sets the ownership status of the outline subject to IDF
+ * ownership rules. The return value is true if the ownership
+ * was changed and false if a specification violation occurred.
+ */
+ bool SetOwner( IDF3::KEY_OWNER aOwner );
+
+ /**
+ * Function IsSingle
+ * return true if this type of outline only supports a single
+ * outline. All outlines except for BOARD_OUTLINE are single.
+ */
+ bool IsSingle( void );
+
+ /**
+ * Function ClearOutlines
+ * clears internal data except for the parent pointer
+ */
+ void ClearOutlines( void );
+
+ /**
+ * Function AddComment
+ * adds a comment to the outline data; this function is not
+ * subject to IDF ownership rules.
+ */
+ void AddComment( const std::string& aComment );
+
+ /**
+ * Function CommentSize
+ * returns the number of comments in the internal list
+ */
+ size_t CommentsSize( void );
+
+ /**
+ * Function GetComments
+ * returns a pointer to the internal list of comments
+ */
+ std::list< std::string >* GetComments( void );
+
+ /**
+ * Function GetComment
+ * returns the string representing the indexed comment or
+ * NULL if the index is out of bounds
+ */
+ const std::string* GetComment( size_t aIndex );
+
+ /**
+ * Function DeleteComment
+ * deletes a comment based on the given index.
+ *
+ * @return bool: true if a comment was deleted, false if
+ * the index is out of bounds.
+ */
+ bool DeleteComment( size_t aIndex );
+
+ /**
+ * Function ClearComments
+ * deletes all comments
+ */
+ void ClearComments( void );
+
+ const std::string& GetError( void )
+ {
+ return errormsg;
+ }
+};
+
+
+/**
+ * Class OTHER_OUTLINE
+ * describes miscellaneous extrusions on the board
+ */
+class OTHER_OUTLINE : public BOARD_OUTLINE
+{
+friend class IDF3_BOARD;
+private:
+ std::string uniqueID; // Outline Identifier (IDF spec)
+ IDF3::IDF_LAYER side; // Board Side [TOP/BOTTOM ONLY] (IDF spec)
+
+ /**
+ * Function readData
+ * reads an OTHER_OUTLINE data from an IDFv3 file.
+ * If an unrecoverable error occurs an exception is thrown.
+ *
+ * @param aBoardFile is an IDFv3 file open for reading
+ * @param aHeader is the .OTHER_OUTLINE header as read via FetchIDFLine
+ */
+ virtual void readData( std::ifstream& aBoardFile, const std::string& aHeader,
+ IDF3::IDF_VERSION aIdfVersion );
+
+ /**
+ * Function writeData
+ * writes the OTHER_OUTLINE data to an open IDFv3 file
+ *
+ * @param aBoardFile is an IDFv3 file open for writing
+ *
+ * @return bool: true if the data was successfully written, otherwise false.
+ */
+ virtual void writeData( std::ofstream& aBoardFile );
+
+public:
+ OTHER_OUTLINE( IDF3_BOARD* aParent );
+
+ /**
+ * Function SetOutlineIdentifier
+ * sets the Outline Identifier string of this OTHER_OUTLINE object
+ * as per IDFv3 spec.
+ */
+ virtual bool SetOutlineIdentifier( const std::string aUniqueID );
+
+ /**
+ * Function GetOutlineIdentifier
+ * returns the object's Outline Identifier
+ */
+ virtual const std::string& GetOutlineIdentifier( void );
+
+ /**
+ * Function SetSide
+ * sets the side which this outline is applicable to (TOP, BOTTOM).
+ *
+ * @return bool: true if the side was set, false if the side is invalid
+ * or there is a violation of IDF ownership rules.
+ */
+ virtual bool SetSide( IDF3::IDF_LAYER aSide );
+
+ /**
+ * Function GetSide
+ * returns the side which this outline is applicable to
+ */
+ virtual IDF3::IDF_LAYER GetSide( void );
+
+ /**
+ * Function Clear
+ * deletes internal data except for the parent object
+ */
+ virtual bool Clear( void );
+};
+
+
+/**
+ * Class ROUTE_OUTLINE
+ * describes routing areas on the board
+ */
+class ROUTE_OUTLINE : public BOARD_OUTLINE
+{
+friend class IDF3_BOARD;
+private:
+ /**
+ * Function readData
+ * reads ROUTE_OUTLINE data from an IDFv3 file
+ * If an unrecoverable error occurs an exception is thrown.
+ *
+ * @param aBoardFile is an open IDFv3 board file
+ * @param aHeader is the .ROUTE_OUTLINE header as returned by FetchIDFLine
+ */
+ virtual void readData( std::ifstream& aBoardFile, const std::string& aHeader,
+ IDF3::IDF_VERSION aIdfVersion );
+
+ /**
+ * Function writeData
+ * writes the ROUTE_OUTLINE data to an open IDFv3 file
+ */
+ virtual void writeData( std::ofstream& aBoardFile );
+
+protected:
+ IDF3::IDF_LAYER layers; // Routing layers (IDF spec)
+
+public:
+ ROUTE_OUTLINE( IDF3_BOARD* aParent );
+
+ /**
+ * Function SetLayers
+ * sets the layer or group of layers this outline is applicable to.
+ * This function is subject to IDF ownership rules; true is returned
+ * on success, otherwise false is returned and the error message is set.
+ */
+ virtual bool SetLayers( IDF3::IDF_LAYER aLayer );
+
+ /**
+ * Function GetLayers
+ * returns the layer or group of layers which this outline is applicable to
+ */
+ virtual IDF3::IDF_LAYER GetLayers( void );
+
+ /**
+ * Function Clear
+ * deletes internal data except for the parent object
+ */
+ virtual bool Clear( void );
+};
+
+/**
+ * Class PLACE_OUTLINE
+ * describes areas on the board for placing components
+ */
+class PLACE_OUTLINE : public BOARD_OUTLINE
+{
+friend class IDF3_BOARD;
+private:
+ /**
+ * Function readData
+ * reads PLACE_OUTLINE data from an open IDFv3 file.
+ * If an unrecoverable error occurs an exception is thrown.
+ *
+ * @param aBoardFile is an IDFv3 file opened for reading
+ * @param aHeader is the .PLACE_OUTLINE header as returned by FetchIDFLine
+ */
+ virtual void readData( std::ifstream& aBoardFile, const std::string& aHeader,
+ IDF3::IDF_VERSION aIdfVersion );
+
+ /**
+ * Function writeData
+ * writes the PLACE_OUTLINE data to an open IDFv3 file
+ *
+ * @param aBoardFile is an IDFv3 file opened for writing
+ *
+ * @return bool: true if the data was successfully written, otherwise false
+ */
+ virtual void writeData( std::ofstream& aBoardFile );
+
+protected:
+ IDF3::IDF_LAYER side; // Board Side [TOP/BOTTOM/BOTH ONLY] (IDF spec)
+
+public:
+ PLACE_OUTLINE( IDF3_BOARD* aParent );
+
+ /**
+ * Function SetSide
+ * sets the side (TOP, BOTTOM, BOTH) which this outline applies to.
+ * This function is subject to IDF ownership rules; true is returned
+ * on success, otherwise false is returned and the error message is set.
+ */
+ virtual bool SetSide( IDF3::IDF_LAYER aSide );
+
+ /**
+ * Function GetSide
+ * returns the side which this outline is applicable to
+ */
+ virtual IDF3::IDF_LAYER GetSide( void );
+
+ /**
+ * Function SetMaxHeight
+ * sets the maximum height of a component within this outline.
+ * This function is subject to IDF ownership rules; true is returned
+ * on success, otherwise false is returned and the error message is set.
+ */
+ virtual bool SetMaxHeight( double aHeight );
+
+ /**
+ * Function GetMaxHeight
+ * returns the maximum allowable height for a component in this region
+ */
+ virtual double GetMaxHeight( void );
+
+ /**
+ * Function Clear
+ * deletes all internal data
+ */
+ virtual bool Clear( void );
+};
+
+
+/**
+ * Class ROUTE_KO_OUTLINE
+ * describes regions and layers where no electrical routing is permitted
+ */
+class ROUTE_KO_OUTLINE : public ROUTE_OUTLINE
+{
+public:
+ ROUTE_KO_OUTLINE( IDF3_BOARD* aParent );
+};
+
+/**
+ * Class VIA_KO_OUTLINE
+ * describes regions in which vias are prohibited. Note: IDFv3 only considers
+ * thru-hole vias and makes no statement regarding behavior with blind or buried
+ * vias.
+ */
+class VIA_KO_OUTLINE : public OTHER_OUTLINE
+{
+public:
+ VIA_KO_OUTLINE( IDF3_BOARD* aParent );
+};
+
+
+/**
+ * Class PLACE_KO_OUTLINE
+ * represents regions and layers in which no component may
+ * be placed or on which a maximum component height is in effect.
+ */
+class PLACE_KO_OUTLINE : public PLACE_OUTLINE
+{
+public:
+ PLACE_KO_OUTLINE( IDF3_BOARD* aParent );
+};
+
+/**
+ * Class GROUP_OUTLINE
+ * represents regions and layers in which user-specified features or components
+ * may be placed.
+ */
+class GROUP_OUTLINE : public BOARD_OUTLINE
+{
+friend class IDF3_BOARD;
+private:
+ IDF3::IDF_LAYER side; // Board Side [TOP/BOTTOM/BOTH ONLY] (IDF spec)
+ std::string groupName; // non-unique string
+
+ /**
+ * Function readData
+ * reads GROUP_OUTLINE data from an open IDFv3 file
+ * If an unrecoverable error occurs an exception is thrown.
+ *
+ * @param aBoardFile is an open IDFv3 file
+ * @param aHeader is the .PLACE_REGION header as returned by FetchIDFLine
+ */
+ virtual void readData( std::ifstream& aBoardFile, const std::string& aHeader,
+ IDF3::IDF_VERSION aIdfVersion );
+
+ /**
+ * Function writeData
+ * writes the data to a .PLACE_REGION section of an IDFv3 file
+ *
+ * @param aBoardFile is an IDFv3 file open for writing
+ *
+ * @return bool: true if the data is successfully written, otherwise false
+ */
+ virtual void writeData( std::ofstream& aBoardFile );
+
+public:
+ GROUP_OUTLINE( IDF3_BOARD* aParent );
+
+ /**
+ * Function SetSide
+ * sets the side which this outline applies to (TOP, BOTTOM, BOTH).
+ * This function is subject to IDF ownership rules; true is returned
+ * on success, otherwise false is returned and the error message is set.
+ */
+ virtual bool SetSide( IDF3::IDF_LAYER aSide );
+
+ /**
+ * Function GetSide
+ * returns the side which this outline applies to
+ */
+ virtual IDF3::IDF_LAYER GetSide( void );
+
+ /**
+ * Function SetGroupName
+ * sets the name of the group, subject to IDF ownership rules.
+ * This function is subject to IDF ownership rules; true is returned
+ * on success, otherwise false is returned and the error message is set.
+ */
+ virtual bool SetGroupName( std::string aGroupName );
+
+ /**
+ * Function GetGroupName
+ * returns a reference to the (non-unique) group name
+ */
+ virtual const std::string& GetGroupName( void );
+
+ /**
+ * Function Clear
+ * deletes internal data, subject to IDF ownership rules
+ */
+ virtual bool Clear( void );
+};
+
+
+/**
+ * class IDF3_COMP_OUTLINE
+ * represents a component's outline as stored in an IDF library file
+ */
+class IDF3_COMP_OUTLINE : public BOARD_OUTLINE
+{
+friend class IDF3_BOARD;
+friend class IDF3_COMP_OUTLINE_DATA;
+private:
+ std::string uid; // unique ID
+ std::string geometry; // geometry name (IDF)
+ std::string part; // part name (IDF)
+ IDF3::COMP_TYPE compType; // component type
+ int refNum; // number of components referring to this outline
+
+ std::map< std::string, std::string > props; // properties list
+
+ void readProperties( std::ifstream& aLibFile );
+ bool writeProperties( std::ofstream& aLibFile );
+
+ /**
+ * Function readData
+ * reads a component outline from an open IDFv3 file
+ * If an unrecoverable error occurs, an exception is thrown.
+ *
+ * @param aLibFile is an open IDFv3 Library file
+ * @param aHeader is the .ELECTRICAL or .MECHANICAL header as returned by FetchIDFLine
+ */
+ virtual void readData( std::ifstream& aLibFile, const std::string& aHeader,
+ IDF3::IDF_VERSION aIdfVersion );
+
+ /**
+ * Function writeData
+ * writes comments and component outline data to an IDFv3 Library file
+ *
+ * @param aLibFile is an IDFv3 library file open for writing
+ *
+ * @return bool: true if the data was successfully written, otherwise false
+ */
+ virtual void writeData( std::ofstream& aLibFile );
+
+ /**
+ * Function incrementRef
+ * increments the internal reference counter to keep track of the number of
+ * components referring to this outline.
+ *
+ * @return int: the number of current references to this component outline
+ */
+ int incrementRef( void );
+
+ /**
+ * Function decrementRef
+ * decrements the internal reference counter to keep track of the number of
+ * components referring to this outline.
+ *
+ * @return int: the number of remaining references or -1 if there were no
+ * references when the function was invoked, in which case the error message
+ * is also set.
+ */
+ int decrementRef( void );
+
+public:
+ IDF3_COMP_OUTLINE( IDF3_BOARD* aParent );
+
+ /**
+ * Function Clear
+ * deletes internal outline data
+ */
+ virtual bool Clear( void );
+
+ /**
+ * Function SetComponentClass
+ * sets the type of component outline (.ELECTRICAL or .MECHANICAL).
+ * Returns true on success, otherwise false and the error message is set
+ */
+ bool SetComponentClass( IDF3::COMP_TYPE aCompClass );
+
+ /**
+ * Function GetComponentClass
+ * returns the class of component represented by this outline
+ */
+ IDF3::COMP_TYPE GetComponentClass( void );
+
+ /**
+ * Function SetGeomName
+ * sets the Geometry Name (Package Name, IDFv3 spec) of the component outline
+ */
+ void SetGeomName( const std::string& aGeomName );
+
+ /**
+ * Function GetGeomName
+ * returns the Geometry Name (Package Name) of the component outline
+ */
+ const std::string& GetGeomName( void );
+
+ /**
+ * Function SetPartName
+ * sets the Part Name (Part Number, IDFv3 spec) of the component outline
+ */
+ void SetPartName( const std::string& aPartName );
+
+ /**
+ * Function GetPartName
+ * returns the Part Name (Part Number) of the component outline
+ */
+ const std::string& GetPartName( void );
+
+ /**
+ * Function GetUID
+ * returns the unique identifier for this component outline;
+ * this is equal to GEOM_NAME + "_" + PART_NAME
+ */
+ const std::string& GetUID( void );
+
+ /**
+ * Function CreateDefaultOutline
+ * creates a default outline with the given Geometry and Part names.
+ * This outline is a star with outer radius 5mm and inner radius 2.5mm.
+ */
+ bool CreateDefaultOutline( const std::string &aGeom, const std::string &aPart );
+
+ // XXX: property manipulators
+};
+
+#endif // IDF_OUTLINES_H
diff --git a/utils/idftools/idf_parser.cpp b/utils/idftools/idf_parser.cpp
new file mode 100644
index 0000000..c4d3e4c
--- /dev/null
+++ b/utils/idftools/idf_parser.cpp
@@ -0,0 +1,4288 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2014 Cirilo Bernardo
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+
+#include <iostream>
+#include <iomanip>
+#include <fstream>
+#include <sstream>
+#include <cmath>
+#include <cerrno>
+#include <algorithm>
+
+#include <idf_parser.h>
+#include <idf_helpers.h>
+
+using namespace std;
+using namespace IDF3;
+
+
+static bool MatchCompOutline( IDF3_COMP_OUTLINE* aOutlineA, IDF3_COMP_OUTLINE* aOutlineB )
+{
+ if( aOutlineA->GetComponentClass() != aOutlineB->GetComponentClass() )
+ return false;
+
+ if( aOutlineA->OutlinesSize() != aOutlineB->OutlinesSize() )
+ return false;
+
+ // are both outlines empty?
+ if( aOutlineA->OutlinesSize() == 0 )
+ return true;
+
+ IDF_OUTLINE* opA = aOutlineA->GetOutline( 0 );
+ IDF_OUTLINE* opB = aOutlineB->GetOutline( 0 );
+
+ if( opA->size() != opB->size() )
+ return false;
+
+ if( opA->size() == 0 )
+ return true;
+
+ std::list<IDF_SEGMENT*>::iterator olAs = opA->begin();
+ std::list<IDF_SEGMENT*>::iterator olAe = opA->end();
+ std::list<IDF_SEGMENT*>::iterator olBs = opB->begin();
+
+ while( olAs != olAe )
+ {
+ if( !(*olAs)->MatchesStart( (*olBs)->startPoint ) )
+ return false;
+
+ if( !(*olAs)->MatchesEnd( (*olBs)->endPoint ) )
+ return false;
+
+ ++olAs;
+ ++olBs;
+ }
+
+ return true;
+}
+
+
+/*
+ * CLASS: IDF3_COMP_OUTLINE_DATA
+ * This represents the outline placement
+ * information and other data specific to
+ * each component instance.
+ */
+IDF3_COMP_OUTLINE_DATA::IDF3_COMP_OUTLINE_DATA()
+{
+ parent = NULL;
+ outline = NULL;
+ xoff = 0.0;
+ yoff = 0.0;
+ zoff = 0.0;
+ aoff = 0.0;
+
+ return;
+}
+
+IDF3_COMP_OUTLINE_DATA::IDF3_COMP_OUTLINE_DATA( IDF3_COMPONENT* aParent,
+ IDF3_COMP_OUTLINE* aOutline )
+{
+ parent = aParent;
+ outline = aOutline;
+ xoff = 0.0;
+ yoff = 0.0;
+ zoff = 0.0;
+ aoff = 0.0;
+
+ if( aOutline )
+ aOutline->incrementRef();
+
+ return;
+}
+
+IDF3_COMP_OUTLINE_DATA::IDF3_COMP_OUTLINE_DATA( IDF3_COMPONENT* aParent,
+ IDF3_COMP_OUTLINE* aOutline,
+ double aXoff, double aYoff,
+ double aZoff, double aAngleOff )
+{
+ parent = aParent;
+ outline = aOutline;
+ xoff = aXoff;
+ yoff = aYoff;
+ zoff = aZoff;
+ aoff = aAngleOff;
+ return;
+}
+
+IDF3_COMP_OUTLINE_DATA::~IDF3_COMP_OUTLINE_DATA()
+{
+ if( outline )
+ outline->decrementRef();
+
+ return;
+}
+
+#ifndef DISABLE_IDF_OWNERSHIP
+bool IDF3_COMP_OUTLINE_DATA::checkOwnership( int aSourceLine, const char* aSourceFunc )
+{
+ if( !parent )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << aSourceLine << ":" << aSourceFunc << "():\n";
+ ostr << "* BUG: IDF3_COMP_OUTLINE_DATA::parent not set; cannot enforce ownership rules\n";
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ IDF3::IDF_PLACEMENT placement = parent->GetPlacement();
+ IDF3::CAD_TYPE parentCAD = parent->GetCadType();
+
+ if( placement == PS_PLACED || placement == PS_UNPLACED )
+ return true;
+
+ if( placement == PS_MCAD && parentCAD == CAD_MECH )
+ return true;
+
+ if( placement == PS_ECAD && parentCAD == CAD_ELEC )
+ return true;
+
+ do
+ {
+ ostringstream ostr;
+ ostr << "* " << __FILE__ << ":" << aSourceLine << ":" << aSourceFunc << "():\n";
+ ostr << "* ownership violation; CAD type is ";
+
+ if( parentCAD == CAD_MECH )
+ ostr << "MCAD ";
+ else
+ ostr << "ECAD ";
+
+ ostr << "while outline owner is " << GetPlacementString( placement ) << "\n";
+ errormsg = ostr.str();
+
+ } while( 0 );
+
+ return false;
+}
+#endif
+
+bool IDF3_COMP_OUTLINE_DATA::SetOffsets( double aXoff, double aYoff,
+ double aZoff, double aAngleOff )
+{
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !checkOwnership( __LINE__, __FUNCTION__ ) )
+ return false;
+#endif
+
+ xoff = aXoff;
+ yoff = aYoff;
+ zoff = aZoff;
+ aoff = aAngleOff;
+ return true;
+}
+
+void IDF3_COMP_OUTLINE_DATA::GetOffsets( double& aXoff, double& aYoff,
+ double& aZoff, double& aAngleOff )
+{
+ aXoff = xoff;
+ aYoff = yoff;
+ aZoff = zoff;
+ aAngleOff = aoff;
+ return;
+}
+
+
+void IDF3_COMP_OUTLINE_DATA::SetParent( IDF3_COMPONENT* aParent )
+{
+ parent = aParent;
+}
+
+bool IDF3_COMP_OUTLINE_DATA::SetOutline( IDF3_COMP_OUTLINE* aOutline )
+{
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !checkOwnership( __LINE__, __FUNCTION__ ) )
+ return false;
+#endif
+
+ if( outline )
+ outline->decrementRef();
+
+ outline = aOutline;
+
+ if( outline )
+ outline->incrementRef();
+
+ return true;
+}
+
+
+bool IDF3_COMP_OUTLINE_DATA::readPlaceData( std::ifstream &aBoardFile,
+ IDF3::FILE_STATE& aBoardState,
+ IDF3_BOARD *aBoard,
+ IDF3::IDF_VERSION aIdfVersion,
+ bool aNoSubstituteOutlines )
+{
+ if( !aBoard )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "\n* BUG: invoked with no reference to the parent IDF_BOARD" ) );
+
+ // clear out data possibly left over from previous use of the object
+ outline = NULL;
+ parent = NULL;
+
+ std::string iline; // the input line
+ bool isComment; // true if a line just read in is a comment line
+ std::streampos pos;
+ int idx = 0;
+ bool quoted = false;
+ std::string token;
+ std::string uid;
+ std::string refdes;
+ IDF3::IDF_PLACEMENT placement = IDF3::PS_UNPLACED;
+ IDF3::IDF_LAYER side = IDF3::LYR_TOP;
+
+ // RECORD 2: 'package name', 'part number', 'Refdes' (any, NOREFDES, BOARD)
+ while( !FetchIDFLine( aBoardFile, iline, isComment, pos ) && aBoardFile.good() );
+
+ if( ( !aBoardFile.good() && !aBoardFile.eof() ) || iline.empty() )
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* violation: could not read PLACEMENT section\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( isComment )
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* violation: comment within PLACEMENT section\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ idx = 0;
+ GetIDFString( iline, token, quoted, idx );
+
+ if( !quoted && CompareToken( ".END_PLACEMENT", token ) )
+ {
+ aBoardState = IDF3::FILE_PLACEMENT;
+ return false;
+ }
+
+ std::string ngeom = token;
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* violation: no PART NAME in PLACEMENT RECORD2\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ std::string npart = token;
+ uid = ngeom + "_" + npart;
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* violation: no REFDES in PLACEMENT RECORD2\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( CompareToken( "NOREFDES", token ) )
+ {
+ // according to the IDF3.0 specification, this is a
+ // mechanical component. The specification is defective
+ // since it is impossible to associate mechanical
+ // components with their holes unless the mechanical
+ // component is given a unique RefDes. This class of defect
+ // is one reason IDF does not work well in faithfully
+ // conveying information between ECAD and MCAD.
+ refdes = aBoard->GetNewRefDes();
+ }
+ else if( CompareToken( "BOARD", token ) )
+ {
+ ostringstream ostr;
+
+ ostr << "UNSUPPORTED FEATURE\n";
+ ostr << "* RefDes is 'BOARD', indicating this is a PANEL FILE (not supported)\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+ else if( CompareToken( "PANEL", token ) )
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* violation: RefDes in PLACEMENT RECORD2 is 'PANEL'\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+ else if( token.empty() )
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* violation: empty RefDes string in PLACEMENT RECORD2\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+ else
+ {
+ // note: perversely, spaces can be a valid RefDes
+ refdes = token;
+ }
+
+ // V2: RECORD 3: X, Y, ROT, SIDE (top/bot), PLACEMENT (fixed, placed, unplaced)
+ // V3: RECORD 3: X, Y, Z, ROT, SIDE (top/bot), PLACEMENT (placed, unplaced, mcad, ecad)
+ while( !FetchIDFLine( aBoardFile, iline, isComment, pos ) && aBoardFile.good() );
+
+ if( !aBoardFile.good() )
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* problems reading PLACEMENT SECTION, RECORD 3\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( isComment )
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* violation: comment within PLACEMENT section\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ idx = 0;
+ GetIDFString( iline, token, quoted, idx );
+
+ if( quoted )
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* violation: X value must not be in quotes (PLACEMENT RECORD 3)\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ istringstream istr;
+ istr.str( token );
+
+ istr >> xoff;
+ if( istr.fail() )
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* violation: X value is not numeric (PLACEMENT RECORD 3)\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* violation: no Y value (PLACEMENT RECORD 3)\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ istr.clear();
+ istr.str( token );
+
+ istr >> yoff;
+ if( istr.fail() )
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* violation: Y value is not numeric (PLACEMENT RECORD 3)\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( aIdfVersion > IDF_V2 )
+ {
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDFv3 file\n";
+ ostr << "* violation: no Z value (PLACEMENT RECORD 3)\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ istr.clear();
+ istr.str( token );
+
+ istr >> zoff;
+ if( istr.fail() )
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDFv3 file\n";
+ ostr << "* violation: Z value is not numeric (PLACEMENT RECORD 3)\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+ }
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* violation: no rotation value (PLACEMENT RECORD 3)\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ istr.clear();
+ istr.str( token );
+
+ istr >> aoff;
+ if( istr.fail() )
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* violation: rotation value is not numeric (PLACEMENT RECORD 3)\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* violation: no SIDE value (PLACEMENT RECORD 3)\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( CompareToken( "TOP", token ) )
+ {
+ side = IDF3::LYR_TOP;
+ }
+ else if( CompareToken( "BOTTOM", token ) )
+ {
+ side = IDF3::LYR_BOTTOM;
+ }
+ else
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* violation: invalid SIDE value in PLACEMENT RECORD 3 ('";
+ ostr << token << "'); must be one of TOP/BOTTOM\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* violation: no PLACEMENT value in PLACEMENT RECORD 3\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( CompareToken( "PLACED", token ) )
+ {
+ placement = IDF3::PS_PLACED;
+ }
+ else if( CompareToken( "UNPLACED", token ) )
+ {
+ placement = IDF3::PS_UNPLACED;
+ }
+ else if( aIdfVersion > IDF_V2 && CompareToken( "MCAD", token ) )
+ {
+ placement = IDF3::PS_MCAD;
+ }
+ else if( aIdfVersion > IDF_V2 && CompareToken( "ECAD", token ) )
+ {
+ placement = IDF3::PS_ECAD;
+ }
+ else if( aIdfVersion < IDF_V3 && CompareToken( "FIXED", token ) )
+ {
+ if( aBoard->GetCadType() == CAD_ELEC )
+ placement = IDF3::PS_MCAD;
+ else
+ placement = IDF3::PS_ECAD;
+ }
+ else
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* violation: invalid PLACEMENT value ('";
+ ostr << token << "') in PLACEMENT RECORD 3\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ outline = aBoard->GetComponentOutline( uid );
+
+ if( outline == NULL && !aNoSubstituteOutlines )
+ {
+ ERROR_IDF << "MISSING OUTLINE\n";
+ cerr << "* GeomName( " << ngeom << " ), PartName( " << npart << " )\n";
+ cerr << "* Substituting default outline.\n";
+ outline = aBoard->GetInvalidOutline( ngeom, npart );
+
+ if( outline == NULL )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "\n* missing outline: cannot create default" ) );
+ }
+
+ if( aBoard->GetUnit() == IDF3::UNIT_THOU )
+ {
+ xoff *= IDF_THOU_TO_MM;
+ yoff *= IDF_THOU_TO_MM;
+ zoff *= IDF_THOU_TO_MM;
+ }
+
+ parent = aBoard->FindComponent( refdes );
+
+ if( parent == NULL )
+ {
+ IDF3_COMPONENT* cp = new IDF3_COMPONENT( aBoard );
+
+ if( cp == NULL )
+ {
+ outline = NULL;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "cannot create component object" ) );
+ }
+
+ cp->SetRefDes( refdes );
+ cp->SetPosition( xoff, yoff, aoff, side );
+ cp->SetPlacement( placement );
+
+ xoff = 0;
+ yoff = 0;
+ aoff = 0;
+
+ aBoard->AddComponent( cp );
+
+ parent = cp;
+ }
+ else
+ {
+ double tX, tY, tA;
+ IDF3::IDF_LAYER tL;
+
+ if( parent->GetPosition( tX, tY, tA, tL ) )
+ {
+ if( side != tL )
+ {
+ outline = NULL;
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* violation: inconsistent PLACEMENT data; ";
+ ostr << "* SIDE value has changed from " << GetLayerString( tL );
+ ostr << " to " << GetLayerString( side ) << "\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ xoff -= tX;
+ yoff -= tY;
+ aoff -= tA;
+ }
+ else
+ {
+ parent->SetPosition( xoff, yoff, aoff, side );
+ parent->SetPlacement( placement );
+
+ xoff = 0;
+ yoff = 0;
+ aoff = 0;
+ }
+
+ if( placement != parent->GetPlacement() )
+ {
+ outline = NULL;
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* violation: inconsistent PLACEMENT data; ";
+ ostr << "* PLACEMENT value has changed from ";
+ ostr << GetPlacementString( parent->GetPlacement() );
+ ostr << " to " << GetPlacementString( placement ) << "\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* file position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ }
+
+ // copy internal data to a new object and push it into the component's outline list
+ IDF3_COMP_OUTLINE_DATA* cdp = new IDF3_COMP_OUTLINE_DATA;
+ *cdp = *this;
+ if( outline ) outline->incrementRef();
+ outline = NULL;
+
+ if( !parent->AddOutlineData( cdp ) )
+ {
+ delete cdp;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "could not add outline data object" ) );
+ }
+
+ return true;
+} // IDF3_COMP_OUTLINE_DATA::readPlaceData
+
+
+void IDF3_COMP_OUTLINE_DATA::writePlaceData( std::ofstream& aBoardFile,
+ double aXpos, double aYpos, double aAngle,
+ const std::string aRefDes,
+ IDF3::IDF_PLACEMENT aPlacement,
+ IDF3::IDF_LAYER aSide )
+{
+ if( outline == NULL )
+ return;
+
+ if( outline->GetUID().empty() )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "empty GEOM and PART names" ) );
+
+ if( aPlacement == PS_INVALID )
+ {
+ ERROR_IDF << "placement invalid (" << aRefDes << ":";
+ std::cerr << aPlacement << "); defaulting to PLACED\n";
+ aPlacement = PS_PLACED;
+ }
+
+ if( aSide != LYR_TOP && aSide != LYR_BOTTOM )
+ {
+ ostringstream ostr;
+ ostr << "\n* invalid side (" << GetLayerString( aSide ) << "); ";
+ ostr << "must be TOP or BOTTOM\n";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ // calculate the final position based on layer
+ double xpos, ypos, ang;
+
+ switch( aSide )
+ {
+ case LYR_TOP:
+ xpos = aXpos + xoff;
+ ypos = aYpos + yoff;
+ ang = aAngle + aoff;
+ break;
+
+ default:
+ xpos = aXpos - xoff;
+ ypos = aYpos + yoff;
+ ang = aAngle - aoff;
+ break;
+ }
+
+ std::string arefdes = aRefDes;
+
+ if( arefdes.empty() || !arefdes.compare( "~" )
+ || ( arefdes.size() >= 8 && CompareToken( "NOREFDES", arefdes.substr(0, 8) ) ) )
+ arefdes = "NOREFDES";
+
+ aBoardFile << "\"" << outline->GetGeomName() << "\" \"" << outline->GetPartName() << "\" "
+ << arefdes << "\n";
+
+ IDF3::IDF_UNIT unit = UNIT_MM;
+
+ if( parent )
+ unit = parent->GetUnit();
+
+ if( unit == UNIT_MM )
+ {
+ aBoardFile << setiosflags(ios::fixed) << setprecision(5) << xpos << " "
+ << ypos << " " << setprecision(3) << zoff << " "
+ << ang << " ";
+ }
+ else
+ {
+ aBoardFile << setiosflags(ios::fixed) << setprecision(1) << (xpos / IDF_THOU_TO_MM) << " "
+ << (ypos / IDF_THOU_TO_MM) << " " << (zoff / IDF_THOU_TO_MM) << " "
+ << setprecision(3) << ang << " ";
+ }
+
+ WriteLayersText( aBoardFile, aSide );
+
+ switch( aPlacement )
+ {
+ case PS_PLACED:
+ aBoardFile << " PLACED\n";
+ break;
+
+ case PS_UNPLACED:
+ aBoardFile << " UNPLACED\n";
+ break;
+
+ case PS_MCAD:
+ aBoardFile << " MCAD\n";
+ break;
+
+ default:
+ aBoardFile << " ECAD\n";
+ break;
+ }
+
+ return;
+}
+
+
+/*
+ * CLASS: IDF3_COMPONENT
+ *
+ * This represents a component and its associated
+ * IDF outlines and ancillary data (position, etc)
+ */
+IDF3_COMPONENT::IDF3_COMPONENT( IDF3_BOARD* aParent )
+{
+ xpos = 0.0;
+ ypos = 0.0;
+ angle = 0.0;
+
+ hasPosition = false;
+ placement = PS_INVALID;
+ layer = LYR_INVALID;
+
+ parent = aParent;
+ return;
+}
+
+IDF3_COMPONENT::~IDF3_COMPONENT()
+{
+ std::list< IDF3_COMP_OUTLINE_DATA* >::iterator itcS = components.begin();
+ std::list< IDF3_COMP_OUTLINE_DATA* >::iterator itcE = components.end();
+
+ while( itcS != itcE )
+ {
+ delete *itcS;
+ ++itcS;
+ }
+
+ components.clear();
+
+ std::list< IDF_DRILL_DATA* >::iterator itdS = drills.begin();
+ std::list< IDF_DRILL_DATA* >::iterator itdE = drills.end();
+
+ while( itdS != itdE )
+ {
+ delete *itdS;
+ ++itdS;
+ }
+
+ drills.clear();
+
+ return;
+}
+
+#ifndef DISABLE_IDF_OWNERSHIP
+bool IDF3_COMPONENT::checkOwnership( int aSourceLine, const char* aSourceFunc )
+{
+ if( !parent )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << aSourceLine << ":" << aSourceFunc << "():\n";
+ ostr << "\n* BUG: parent not set";
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ IDF3::CAD_TYPE pcad = parent->GetCadType();
+
+ switch( placement )
+ {
+ case PS_UNPLACED:
+ case PS_PLACED:
+ case PS_INVALID:
+ break;
+
+ case PS_MCAD:
+
+ if( pcad != CAD_MECH )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "\n* ownership violation; internal CAD type (MCAD) conflicts with PLACEMENT (";
+ ostr << GetPlacementString( placement ) << ")";
+ errormsg = ostr.str();
+
+ return false;
+ }
+ break;
+
+ case PS_ECAD:
+
+ if( pcad != CAD_ELEC )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "\n* ownership violation; internal CAD type (MCAD) conflicts with PLACEMENT (";
+ ostr << GetPlacementString( placement ) << ")";
+ errormsg = ostr.str();
+
+ return false;
+ }
+ break;
+
+ default:
+ do{
+ ostringstream ostr;
+ ostr << "\n* BUG: unhandled internal placement value (" << placement << ")";
+ errormsg = ostr.str();
+
+ return false;
+ } while( 0 );
+
+ break;
+ }
+
+ return true;
+}
+#endif
+
+
+void IDF3_COMPONENT::SetParent( IDF3_BOARD* aParent )
+{
+ parent = aParent;
+ return;
+}
+
+IDF3::CAD_TYPE IDF3_COMPONENT::GetCadType( void )
+{
+ if( parent )
+ return parent->GetCadType();
+
+ return CAD_INVALID;
+}
+
+IDF3::IDF_UNIT IDF3_COMPONENT::GetUnit( void )
+{
+ if( parent )
+ return parent->GetUnit();
+
+ return UNIT_INVALID;
+}
+
+bool IDF3_COMPONENT::SetRefDes( const std::string& aRefDes )
+{
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !checkOwnership( __LINE__, __FUNCTION__ ) )
+ return false;
+#endif
+
+ if( aRefDes.empty() )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): invalid RefDes (empty)";
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ if( CompareToken( "PANEL", aRefDes ) )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* BUG: PANEL is a reserved designator and may not be used by components";
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ refdes = aRefDes;
+ return true;
+}
+
+
+const std::string& IDF3_COMPONENT::GetRefDes( void )
+{
+ return refdes;
+}
+
+IDF_DRILL_DATA* IDF3_COMPONENT::AddDrill( double aDia, double aXpos, double aYpos,
+ IDF3::KEY_PLATING aPlating,
+ const std::string aHoleType,
+ IDF3::KEY_OWNER aOwner )
+{
+ IDF_DRILL_DATA* dp = new IDF_DRILL_DATA( aDia, aXpos, aYpos, aPlating,
+ refdes, aHoleType, aOwner );
+
+ if( dp == NULL )
+ return NULL;
+
+ drills.push_back( dp );
+
+ return dp;
+}
+
+
+IDF_DRILL_DATA* IDF3_COMPONENT::AddDrill( IDF_DRILL_DATA* aDrilledHole )
+{
+ if( !aDrilledHole )
+ return NULL;
+
+ if( CompareToken( "PANEL", refdes ) )
+ {
+ ERROR_IDF;
+ cerr << "\n* BUG: PANEL drills not supported at component level\n";
+ return NULL;
+ }
+
+ if( refdes.compare( aDrilledHole->GetDrillRefDes() ) )
+ {
+ ERROR_IDF;
+ cerr << "\n* BUG: pushing an incorrect REFDES ('" << aDrilledHole->GetDrillRefDes();
+ cerr << "') to component ('" << refdes << "')\n";
+ return NULL;
+ }
+
+ drills.push_back( aDrilledHole );
+
+ return aDrilledHole;
+}
+
+
+bool IDF3_COMPONENT::DelDrill( double aDia, double aXpos, double aYpos )
+{
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !checkOwnership( __LINE__, __FUNCTION__ ) )
+ return false;
+#endif
+
+ errormsg.clear();
+
+ if( drills.empty() )
+ return false;
+
+ bool val = false;
+
+ list< IDF_DRILL_DATA* >::iterator itS = drills.begin();
+ list< IDF_DRILL_DATA* >::iterator itE = drills.end();
+
+ while( !drills.empty() && itS != itE )
+ {
+ if( (*itS)->Matches( aDia, aXpos, aYpos ) )
+ {
+ val = true;
+ delete *itS;
+ itS = drills.erase( itS );
+ continue;
+ }
+ ++itS;
+ }
+
+ return val;
+}
+
+
+bool IDF3_COMPONENT::DelDrill( IDF_DRILL_DATA* aDrill )
+{
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !checkOwnership( __LINE__, __FUNCTION__ ) )
+ return false;
+#endif
+
+ errormsg.clear();
+
+ if( drills.empty() )
+ return false;
+
+ list< IDF_DRILL_DATA* >::iterator itS = drills.begin();
+ list< IDF_DRILL_DATA* >::iterator itE = drills.end();
+
+ while( !drills.empty() && itS != itE )
+ {
+ if( *itS == aDrill )
+ {
+ delete *itS;
+ drills.erase( itS );
+ return true;
+ }
+ ++itS;
+ }
+
+ return false;
+}
+
+const std::list< IDF_DRILL_DATA* >*const IDF3_COMPONENT::GetDrills( void )
+{
+ return &drills;
+}
+
+bool IDF3_COMPONENT::AddOutlineData( IDF3_COMP_OUTLINE_DATA* aComponentOutline )
+{
+ if( aComponentOutline == NULL )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): invalid aComponentOutline (NULL)";
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+
+ components.push_back( aComponentOutline );
+
+ return true;
+}
+
+bool IDF3_COMPONENT::DeleteOutlineData( IDF3_COMP_OUTLINE_DATA* aComponentOutline )
+{
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !checkOwnership( __LINE__, __FUNCTION__ ) )
+ return false;
+#endif
+
+ if( components.empty() )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): component list is empty";
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ if( aComponentOutline == NULL )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): invalid aComponentOutline (NULL)";
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ errormsg.clear();
+
+ std::list< IDF3_COMP_OUTLINE_DATA* >::iterator itS = components.begin();
+ std::list< IDF3_COMP_OUTLINE_DATA* >::iterator itE = components.end();
+
+ while( itS != itE )
+ {
+ if( *itS == aComponentOutline )
+ {
+ delete *itS;
+ components.erase( itS );
+ return true;
+ }
+
+ ++itS;
+ }
+
+ return false;
+}
+
+bool IDF3_COMPONENT::DeleteOutlineData( size_t aIndex )
+{
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !checkOwnership( __LINE__, __FUNCTION__ ) )
+ return false;
+#endif
+
+ if( aIndex >= components.size() )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* aIndex (" << aIndex << ") out of range; list size is " << components.size();
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ std::list< IDF3_COMP_OUTLINE_DATA* >::iterator itS = components.begin();
+ std::list< IDF3_COMP_OUTLINE_DATA* >::iterator itE = components.end();
+ size_t idx = 0;
+
+ while( itS != itE )
+ {
+ if( idx == aIndex )
+ {
+ delete *itS;
+ components.erase( itS );
+ return true;
+ }
+
+ ++idx;
+ ++itS;
+ }
+
+ return false;
+}
+
+size_t IDF3_COMPONENT::GetOutlinesSize( void )
+{
+ return components.size();
+}
+
+const std::list< IDF3_COMP_OUTLINE_DATA* >*const IDF3_COMPONENT::GetOutlinesData( void )
+{
+ return &components;
+}
+
+bool IDF3_COMPONENT::GetPosition( double& aXpos, double& aYpos, double& aAngle,
+ IDF3::IDF_LAYER& aLayer )
+{
+ errormsg.clear();
+
+ if( !hasPosition )
+ {
+ aXpos = 0.0;
+ aYpos = 0.0;
+ aAngle = 0.0;
+ aLayer = IDF3::LYR_INVALID;
+ return false;
+ }
+
+ aXpos = xpos;
+ aYpos = ypos;
+ aAngle = angle;
+ aLayer = layer;
+ return true;
+}
+
+bool IDF3_COMPONENT::SetPosition( double aXpos, double aYpos, double aAngle, IDF3::IDF_LAYER aLayer )
+{
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !checkOwnership( __LINE__, __FUNCTION__ ) )
+ return false;
+#endif
+
+ errormsg.clear();
+
+ switch( aLayer )
+ {
+ case LYR_TOP:
+ case LYR_BOTTOM:
+ break;
+
+ default:
+ do{
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "\n* invalid side (must be TOP or BOTTOM only): " << GetLayerString( aLayer );
+ errormsg = ostr.str();
+
+ return false;
+ } while( 0 );
+ break;
+ }
+
+ if( hasPosition )
+ return false;
+
+ hasPosition = true;
+ xpos = aXpos;
+ ypos = aYpos;
+ angle = aAngle;
+ layer = aLayer;
+ return true;
+}
+
+
+IDF3::IDF_PLACEMENT IDF3_COMPONENT::GetPlacement( void )
+{
+ return placement;
+}
+
+
+bool IDF3_COMPONENT::SetPlacement( IDF3::IDF_PLACEMENT aPlacementValue )
+{
+ if( aPlacementValue < PS_UNPLACED || aPlacementValue >= PS_INVALID )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "\n* invalid PLACEMENT value (" << aPlacementValue << ")";
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !checkOwnership( __LINE__, __FUNCTION__ ) )
+ return false;
+#endif
+
+ placement = aPlacementValue;
+
+ return true;
+}
+
+bool IDF3_COMPONENT::writeDrillData( std::ofstream& aBoardFile )
+{
+ if( drills.empty() )
+ return true;
+
+ std::list< IDF_DRILL_DATA* >::iterator itS = drills.begin();
+ std::list< IDF_DRILL_DATA* >::iterator itE = drills.end();
+
+ while( itS != itE )
+ {
+ (*itS)->write( aBoardFile, GetUnit() );
+ ++itS;
+ }
+
+ return true;
+}
+
+
+bool IDF3_COMPONENT::writePlaceData( std::ofstream& aBoardFile )
+{
+ if( components.empty() )
+ return true;
+
+ std::list< IDF3_COMP_OUTLINE_DATA* >::iterator itS = components.begin();
+ std::list< IDF3_COMP_OUTLINE_DATA* >::iterator itE = components.end();
+
+ while( itS != itE )
+ {
+ (*itS)->writePlaceData( aBoardFile, xpos, ypos, angle, refdes, placement, layer );
+ ++itS;
+ }
+
+ return true;
+}
+
+
+IDF3_BOARD::IDF3_BOARD( IDF3::CAD_TYPE aCadType )
+{
+ idfVer = IDF_V3;
+ state = FILE_START;
+ cadType = aCadType;
+ userPrec = 5;
+ userScale = 1.0;
+ userXoff = 0.0;
+ userYoff = 0.0;
+ brdFileVersion = 0;
+ libFileVersion = 0;
+ iRefDes = 0;
+ unit = UNIT_MM;
+
+ // unlike other outlines which are created as necessary,
+ // the board outline always exists and its parent must
+ // be set here
+ olnBoard.setParent( this );
+ olnBoard.setThickness( 1.6 );
+
+ return;
+}
+
+IDF3_BOARD::~IDF3_BOARD()
+{
+ Clear();
+
+ return;
+}
+
+
+const std::string& IDF3_BOARD::GetNewRefDes( void )
+{
+ ostringstream ostr;
+ ostr << "NOREFDESn" << iRefDes++;
+
+ sRefDes = ostr.str();
+
+ return sRefDes;
+}
+
+
+#ifndef DISABLE_IDF_OWNERSHIP
+bool IDF3_BOARD::checkComponentOwnership( int aSourceLine, const char* aSourceFunc,
+ IDF3_COMPONENT* aComponent )
+{
+ if( !aComponent )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << aSourceLine << ":" << aSourceFunc;
+ ostr << "(): Invalid component pointer (NULL)";
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ IDF3::IDF_PLACEMENT place = aComponent->GetPlacement();
+
+ if( place == PS_PLACED || place == PS_UNPLACED )
+ return true;
+
+ if( place == PS_MCAD && cadType == CAD_MECH )
+ return true;
+
+ if( place == PS_ECAD && cadType == CAD_ELEC )
+ return true;
+
+ do
+ {
+ ostringstream ostr;
+ ostr << "* " << __FILE__ << ":" << aSourceLine << ":" << aSourceFunc << "():\n";
+ ostr << "* ownership violation; CAD type is ";
+
+ if( cadType == CAD_MECH )
+ ostr << "MCAD ";
+ else
+ ostr << "ECAD ";
+
+ ostr << "while outline owner is " << GetPlacementString( place ) << "\n";
+ errormsg = ostr.str();
+
+ } while( 0 );
+
+ return false;
+}
+#endif
+
+IDF3::CAD_TYPE IDF3_BOARD::GetCadType( void )
+{
+ return cadType;
+}
+
+void IDF3_BOARD::SetBoardName( std::string aBoardName )
+{
+ boardName = aBoardName;
+ return;
+}
+
+const std::string& IDF3_BOARD::GetBoardName( void )
+{
+ return boardName;
+}
+
+bool IDF3_BOARD::setUnit( IDF3::IDF_UNIT aUnit, bool convert )
+{
+ switch( aUnit )
+ {
+ case UNIT_MM:
+ case UNIT_THOU:
+ unit = aUnit;
+ break;
+
+ case UNIT_TNM:
+ ERROR_IDF << "\n* TNM unit is not supported; defaulting to mm\n";
+ unit = UNIT_MM;
+ break;
+
+ default:
+ do
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* invalid board unit (" << aUnit << ")";
+ errormsg = ostr.str();
+
+ return false;
+ } while( 0 );
+
+ break;
+ }
+
+ // iterate through all owned OUTLINE objects (except IDF3_COMP_OUTLINE)
+ // and set to the same unit
+
+ olnBoard.SetUnit( aUnit );
+
+ do
+ {
+ std::map< std::string, OTHER_OUTLINE*>::iterator its = olnOther.begin();
+ std::map< std::string, OTHER_OUTLINE*>::iterator ite = olnOther.end();
+
+ while( its != ite )
+ {
+ its->second->SetUnit( aUnit );
+ ++its;
+ }
+
+ } while( 0 );
+
+ do
+ {
+ std::list<ROUTE_OUTLINE*>::iterator its = olnRoute.begin();
+ std::list<ROUTE_OUTLINE*>::iterator ite = olnRoute.end();
+
+ while( its != ite )
+ {
+ (*its)->SetUnit( aUnit );
+ ++its;
+ }
+
+ } while( 0 );
+
+ do
+ {
+ std::list<PLACE_OUTLINE*>::iterator its = olnPlace.begin();
+ std::list<PLACE_OUTLINE*>::iterator ite = olnPlace.end();
+
+ while( its != ite )
+ {
+ (*its)->SetUnit( aUnit );
+ ++its;
+ }
+
+ } while( 0 );
+
+ do
+ {
+ std::list<ROUTE_KO_OUTLINE*>::iterator its = olnRouteKeepout.begin();
+ std::list<ROUTE_KO_OUTLINE*>::iterator ite = olnRouteKeepout.end();
+
+ while( its != ite )
+ {
+ (*its)->SetUnit( aUnit );
+ ++its;
+ }
+
+ } while( 0 );
+
+ do
+ {
+ std::list<VIA_KO_OUTLINE*>::iterator its = olnViaKeepout.begin();
+ std::list<VIA_KO_OUTLINE*>::iterator ite = olnViaKeepout.end();
+
+ while( its != ite )
+ {
+ (*its)->SetUnit( aUnit );
+ ++its;
+ }
+
+ } while( 0 );
+
+ do
+ {
+ std::list<PLACE_KO_OUTLINE*>::iterator its = olnPlaceKeepout.begin();
+ std::list<PLACE_KO_OUTLINE*>::iterator ite = olnPlaceKeepout.end();
+
+ while( its != ite )
+ {
+ (*its)->SetUnit( aUnit );
+ ++its;
+ }
+
+ } while( 0 );
+
+ do
+ {
+ std::multimap<std::string, GROUP_OUTLINE*>::iterator its = olnGroup.begin();
+ std::multimap<std::string, GROUP_OUTLINE*>::iterator ite = olnGroup.end();
+
+ while( its != ite )
+ {
+ its->second->SetUnit( aUnit );
+ ++its;
+ }
+
+ } while( 0 );
+
+ //iterate through all owned IDF3_COMP_OUTLINE objects and
+ // set to the same unit IF convert = true
+ if( convert )
+ {
+ std::map<std::string, IDF3_COMP_OUTLINE*>::iterator its = compOutlines.begin();
+ std::map<std::string, IDF3_COMP_OUTLINE*>::iterator ite = compOutlines.end();
+
+ while( its != ite )
+ {
+ its->second->SetUnit( aUnit );
+ ++its;
+ }
+
+ }
+
+ return true;
+}
+
+
+IDF3::IDF_UNIT IDF3_BOARD::GetUnit( void )
+{
+ return unit;
+}
+
+
+bool IDF3_BOARD::SetBoardThickness( double aBoardThickness )
+{
+ if( aBoardThickness <= 0.0 )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): ";
+ ostr << "board thickness (" << aBoardThickness << ") must be > 0";
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ if(! olnBoard.SetThickness( aBoardThickness ) )
+ {
+ errormsg = olnBoard.GetError();
+ return false;
+ }
+
+ return true;
+}
+
+
+double IDF3_BOARD::GetBoardThickness( void )
+{
+ return olnBoard.GetThickness();
+}
+
+
+// read the DRILLED HOLES section
+void IDF3_BOARD::readBrdDrills( std::ifstream& aBoardFile, IDF3::FILE_STATE& aBoardState )
+{
+ IDF_DRILL_DATA drill;
+
+ while( drill.read( aBoardFile, unit, aBoardState, idfVer ) )
+ {
+ IDF_DRILL_DATA *dp = new IDF_DRILL_DATA;
+ *dp = drill;
+
+ if( AddDrill( dp ) == NULL )
+ {
+ delete dp;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "\n* BUG: could not add drill data; cannot continue reading the file" ) );
+ }
+ }
+
+ return;
+}
+
+
+// read the NOTES section
+void IDF3_BOARD::readBrdNotes( std::ifstream& aBoardFile, IDF3::FILE_STATE& aBoardState )
+{
+ IDF_NOTE note;
+
+ while( note.readNote( aBoardFile, aBoardState, unit ) )
+ {
+ IDF_NOTE *np = new IDF_NOTE;
+ *np = note;
+ notes.push_back( np );
+ }
+
+ return;
+}
+
+
+// read the component placement section
+void IDF3_BOARD::readBrdPlacement( std::ifstream& aBoardFile, IDF3::FILE_STATE& aBoardState, bool aNoSubstituteOutlines )
+{
+ IDF3_COMP_OUTLINE_DATA oldata;
+
+ while( oldata.readPlaceData( aBoardFile, aBoardState, this, idfVer, aNoSubstituteOutlines ) );
+
+ return;
+}
+
+
+// read the board HEADER
+void IDF3_BOARD::readBrdHeader( std::ifstream& aBoardFile, IDF3::FILE_STATE& aBoardState )
+{
+ std::string iline; // the input line
+ bool isComment; // true if a line just read in is a comment line
+ std::streampos pos;
+ int idx = 0;
+ bool quoted = false;
+ std::string token;
+
+ // RECORD 1: ".HEADER" must be the very first line
+ while( !FetchIDFLine( aBoardFile, iline, isComment, pos ) && aBoardFile.good() );
+
+ if( !aBoardFile.good() )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "problems reading board header" ) );
+
+ if( isComment )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: first line must be .HEADER\n" ) );
+
+ if( !CompareToken( ".HEADER", iline ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification:\n"
+ "* first line must be .HEADER and have no quotes or trailing text" ) );
+
+ // RECORD 2:
+ // File Type [str]: BOARD_FILE (PANEL_FILE not supported)
+ // IDF Version Number [float]: must be 3.0
+ // Source System [str]: ignored
+ // Date [str]: ignored
+ // Board File Version [int]: ignored
+ while( !FetchIDFLine( aBoardFile, iline, isComment, pos ) && aBoardFile.good() );
+
+ if( !aBoardFile.good() )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "problems reading board header, RECORD 2" ) );
+
+ if( isComment )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: comment within .HEADER section" ) );
+
+ idx = 0;
+ GetIDFString( iline, token, quoted, idx );
+
+ if( quoted )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification:\n"
+ "* File Type in HEADER section must not be in quotes" ) );
+
+ if( !CompareToken( "BOARD_FILE", token ) )
+ {
+ ERROR_IDF;
+
+ if( CompareToken( "PANEL_FILE", token ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "not a board file\n"
+ "* PANEL_FILE is not supported (expecting BOARD_FILE)" ) );
+ else
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Expecting string: BOARD_FILE" ) );
+ }
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: HEADER section, RECORD 2: no FIELD 2" ) );
+
+ if( quoted )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: IDF Version must not be in quotes" ) );
+
+ if( !token.compare( "3.0" ) || !token.compare( "3." ) || !token.compare( "3" ) )
+ idfVer = IDF_V3;
+ else if( !token.compare( "2.0" ) || !token.compare( "2." ) || !token.compare( "2" ) )
+ idfVer = IDF_V2;
+ else
+ {
+ ostringstream ostr;
+
+ ostr << "unsupported IDF version\n";
+ ostr << "* Expecting version to be a variant of '3.0', '2.0' (value: '" << token << "')\n";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification:\n"
+ "* HEADER section, RECORD 2, FIELD 3: no Source System string" ) );
+
+ brdSource = token;
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification:\n"
+ "* HEADER section, RECORD 2, FIELD 4: no Date string" ) );
+
+ brdDate = token;
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification:\n"
+ "* HEADER section, RECORD 2, FIELD 5: no Board File Version number" ) );
+
+ std::istringstream istr;
+ istr.str( token );
+
+ istr >> brdFileVersion;
+
+ if( istr.fail() )
+ {
+ ERROR_IDF << "invalid Board File Version in header\n";
+ cerr << "* Setting default version of 1\n";
+ brdFileVersion = 1;
+ }
+
+ if( quoted )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification:\n"
+ "* HEADER section, RECORD 2, FIELD 5: Board File Version must not be in quotes" ) );
+
+ // RECORD 3:
+ // Board Name [str]: stored
+ // Units [str]: MM or THOU
+ while( !FetchIDFLine( aBoardFile, iline, isComment, pos ) && aBoardFile.good() );
+
+ if( !aBoardFile.good() )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "\n* problems reading board header, RECORD 2" ) );
+
+ if( isComment )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: comment within .HEADER section" ) );
+
+ idx = 0;
+ GetIDFString( iline, token, quoted, idx );
+
+ boardName = token;
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification:\n"
+ "* HEADER section, RECORD 3, FIELD 1: no Board Name" ) );
+
+ if( quoted )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification:\n"
+ "* HEADER section, RECORD 3, FIELD 2: UNIT may not be in quotes" ) );
+
+ if( CompareToken( "MM", token ) )
+ {
+ unit = IDF3::UNIT_MM;
+ }
+ else if( CompareToken( "THOU", token ) )
+ {
+ unit = IDF3::UNIT_THOU;
+ }
+ else if( ( idfVer == IDF_V2 ) && CompareToken( "TNM", token ) )
+ {
+ unit = IDF3::UNIT_TNM;
+ }
+ else
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* HEADER section, RECORD 3, FIELD 2: expecting MM or THOU (got '" << token << "')\n";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ olnBoard.SetUnit( unit );
+
+ // RECORD 4:
+ // .END_HEADER
+ while( !FetchIDFLine( aBoardFile, iline, isComment, pos ) && aBoardFile.good() );
+
+ if( ( !aBoardFile.good() && !aBoardFile.eof() ) || iline.empty() )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "problems reading board header, RECORD 4" ) );
+
+ if( isComment )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: comment within .HEADER section\n" ) );
+
+ if( !CompareToken( ".END_HEADER", iline ) )
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* Violation of specification: expected .END_HEADER\n";
+ ostr << "* line: '" << iline << "'";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ aBoardState = IDF3::FILE_HEADER;
+ return;
+}
+
+
+// read individual board sections; pay attention to IDFv3 section specifications
+void IDF3_BOARD::readBrdSection( std::ifstream& aBoardFile, IDF3::FILE_STATE& aBoardState,
+ bool aNoSubstituteOutlines )
+{
+ std::list< std::string > comments; // comments associated with a section
+
+ // Reads in .SECTION_ID or #COMMENTS
+ // Expected SECTION IDs:
+ // .BOARD_OUTLINE
+ // .PANEL_OUTLINE (NOT SUPPORTED)
+ // .OTHER_OUTLINE
+ // .ROUTE_OUTLINE
+ // .PLACE_OUTLINE
+ // .ROUTE_KEEPOUT
+ // .VIA_KEEPOUT
+ // .PLACE_KEEPOUT
+ // .PLACE_REGION
+ // .DRILLED_HOLES
+ // .NOTES
+ // .PLACEMENT
+ std::string iline; // the input line
+ bool isComment; // true if a line just read in is a comment line
+ std::streampos pos;
+ int idx = 0;
+ bool quoted = false;
+ std::string token;
+
+ while( aBoardFile.good() )
+ {
+ while( !FetchIDFLine( aBoardFile, iline, isComment, pos ) && aBoardFile.good() );
+
+ if( !aBoardFile.good() )
+ {
+ if( aBoardFile.eof() && aBoardState >= IDF3::FILE_HEADER && aBoardState < IDF3::FILE_INVALID )
+ {
+ if( !comments.empty() )
+ ERROR_IDF << "[warning]: trailing comments in IDF file (comments will be lost)\n";
+
+ return;
+ }
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "problems reading board section" ) );
+ }
+
+ if( isComment )
+ {
+ comments.push_back( iline );
+ continue;
+ }
+
+ // This must be a header
+ GetIDFString( iline, token, quoted, idx );
+
+ if( quoted )
+ {
+ ostringstream ostr;
+
+ ostr << "invalid IDF file\n";
+ ostr << "* Violation of specification: quoted string where SECTION HEADER expected\n";
+ ostr << "* line: '" << iline << "'";
+ ostr << "* position: " << pos;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( CompareToken( ".BOARD_OUTLINE", token ) )
+ {
+ if( aBoardState != IDF3::FILE_HEADER )
+ {
+ aBoardState = IDF3::FILE_INVALID;
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: no HEADER section" ) );
+ }
+
+ olnBoard.readData( aBoardFile, iline, idfVer );
+
+ if( !comments.empty() )
+ {
+ std::list<std::string>::iterator its = comments.begin();
+ std::list<std::string>::iterator ite = comments.end();
+
+ while( its != ite )
+ {
+ olnBoard.AddComment( *its );
+ ++its;
+ }
+ }
+
+ aBoardState = IDF3::FILE_OUTLINE;
+ return;
+ }
+
+ if( CompareToken( ".PANEL_OUTLINE", token ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "PANEL_OUTLINE not supported" ) );
+
+ if( CompareToken( ".OTHER_OUTLINE", token ) )
+ {
+ if( aBoardState != IDF3::FILE_OUTLINE )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: expecting .BOARD_OUTLINE, have .OTHER_OUTLINE" ) );
+
+ OTHER_OUTLINE* op = new OTHER_OUTLINE( this );
+
+ if( op == NULL )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "could not create OTHER_OUTLINE object" ) );
+
+ op->SetUnit( unit );
+ op->readData( aBoardFile, iline, idfVer );
+
+ if( !comments.empty() )
+ {
+ std::list<std::string>::iterator its = comments.begin();
+ std::list<std::string>::iterator ite = comments.end();
+
+ while( its != ite )
+ {
+ op->AddComment( *its );
+ ++its;
+ }
+ }
+
+ if( olnOther.insert( pair<string, OTHER_OUTLINE*>(op->GetOutlineIdentifier(), op) ).second == false )
+ {
+ ostringstream ostr;
+ ostr << "invalid IDF file\n";
+ ostr << "* Violation of specification. Non-unique ID in OTHER_OUTLINE '";
+ ostr << op->GetOutlineIdentifier() << "'\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* pos: " << pos;
+ delete op;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ return;
+ }
+
+ if( CompareToken( ".ROUTE_OUTLINE", token ) )
+ {
+ if( aBoardState != IDF3::FILE_OUTLINE )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: expecting .BOARD_OUTLINE, have .ROUTE_OUTLINE" ) );
+
+ ROUTE_OUTLINE* op = new ROUTE_OUTLINE( this );
+
+ if( op == NULL )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "could not create ROUTE_OUTLINE object" ) );
+
+ op->SetUnit( unit );
+ op->readData( aBoardFile, iline, idfVer );
+
+ if( !comments.empty() )
+ {
+ std::list<std::string>::iterator its = comments.begin();
+ std::list<std::string>::iterator ite = comments.end();
+
+ while( its != ite )
+ {
+ op->AddComment( *its );
+ ++its;
+ }
+ }
+
+ olnRoute.push_back( op );
+
+ return;
+ }
+
+ if( CompareToken( ".PLACE_OUTLINE", token ) )
+ {
+ if( aBoardState != IDF3::FILE_OUTLINE )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: expecting .BOARD_OUTLINE, have .PLACE_OUTLINE" ) );
+
+ PLACE_OUTLINE* op = new PLACE_OUTLINE( this );
+
+ if( op == NULL )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "could not create PLACE_OUTLINE object" ) );
+
+ op->SetUnit( unit );
+ op->readData( aBoardFile, iline, idfVer );
+
+ if( !comments.empty() )
+ {
+ std::list<std::string>::iterator its = comments.begin();
+ std::list<std::string>::iterator ite = comments.end();
+
+ while( its != ite )
+ {
+ op->AddComment( *its );
+ ++its;
+ }
+ }
+
+ olnPlace.push_back( op );
+
+ return;
+ }
+
+ if( CompareToken( ".ROUTE_KEEPOUT", token ) )
+ {
+ if( aBoardState != IDF3::FILE_OUTLINE )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: expecting .BOARD_OUTLINE, have .ROUTE_KEEPOUT" ) );
+
+ ROUTE_KO_OUTLINE* op = new ROUTE_KO_OUTLINE( this );
+
+ if( op == NULL )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "could not create ROUTE_KEEPOUT object" ) );
+
+ op->SetUnit( unit );
+ op->readData( aBoardFile, iline, idfVer );
+
+ if( !comments.empty() )
+ {
+ std::list<std::string>::iterator its = comments.begin();
+ std::list<std::string>::iterator ite = comments.end();
+
+ while( its != ite )
+ {
+ op->AddComment( *its );
+ ++its;
+ }
+ }
+
+ olnRouteKeepout.push_back( op );
+
+ return;
+ }
+
+ if( CompareToken( ".VIA_KEEPOUT", token ) )
+ {
+ if( aBoardState != IDF3::FILE_OUTLINE )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: expecting .BOARD_OUTLINE, have .VIA_KEEPOUT" ) );
+
+ VIA_KO_OUTLINE* op = new VIA_KO_OUTLINE( this );
+
+ if( op == NULL )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "could not create VIA_KEEPOUT object" ) );
+
+ op->SetUnit( unit );
+ op->readData( aBoardFile, iline, idfVer );
+
+ if( !comments.empty() )
+ {
+ std::list<std::string>::iterator its = comments.begin();
+ std::list<std::string>::iterator ite = comments.end();
+
+ while( its != ite )
+ {
+ op->AddComment( *its );
+ ++its;
+ }
+ }
+
+ olnViaKeepout.push_back( op );
+
+ return;
+ }
+
+ if( CompareToken( ".PLACE_KEEPOUT", token ) )
+ {
+ if( aBoardState != IDF3::FILE_OUTLINE )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: expecting .BOARD_OUTLINE, have .PLACE_KEEPOUT" ) );
+
+ PLACE_KO_OUTLINE* op = new PLACE_KO_OUTLINE( this );
+
+ if( op == NULL )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "could not create PLACE_KEEPOUT object" ) );
+
+ op->SetUnit( unit );
+ op->readData( aBoardFile, iline, idfVer );
+
+ if( !comments.empty() )
+ {
+ std::list<std::string>::iterator its = comments.begin();
+ std::list<std::string>::iterator ite = comments.end();
+
+ while( its != ite )
+ {
+ op->AddComment( *its );
+ ++its;
+ }
+ }
+
+ olnPlaceKeepout.push_back( op );
+
+ return;
+ }
+
+ if( CompareToken( ".PLACE_REGION", token ) )
+ {
+ if( aBoardState != IDF3::FILE_OUTLINE )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: expecting .BOARD_OUTLINE, have .PLACE_REGION" ) );
+
+ GROUP_OUTLINE* op = new GROUP_OUTLINE( this );
+
+ if( op == NULL )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "could not create PLACE_REGION object" ) );
+
+ op->SetUnit( unit );
+ op->readData( aBoardFile, iline, idfVer );
+
+ if( !comments.empty() )
+ {
+ std::list<std::string>::iterator its = comments.begin();
+ std::list<std::string>::iterator ite = comments.end();
+
+ while( its != ite )
+ {
+ op->AddComment( *its );
+ ++its;
+ }
+ }
+
+ olnGroup.insert( pair<string, GROUP_OUTLINE*>(op->GetGroupName(), op) );
+
+ return;
+ }
+
+ if( CompareToken( ".DRILLED_HOLES", token ) )
+ {
+ if( aBoardState != IDF3::FILE_OUTLINE )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: expecting .BOARD_OUTLINE, have .DRILLED_HOLES" ) );
+
+ readBrdDrills( aBoardFile, aBoardState );
+
+ if( !comments.empty() )
+ {
+ std::list<std::string>::iterator its = comments.begin();
+ std::list<std::string>::iterator ite = comments.end();
+
+ while( its != ite )
+ {
+ drillComments.push_back( *its );
+ ++its;
+ }
+ }
+
+ return;
+ }
+
+ if( CompareToken( ".NOTES", token ) )
+ {
+ if( aBoardState != IDF3::FILE_OUTLINE )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: expecting .BOARD_OUTLINE, have .NOTES" ) );
+
+ if( idfVer < IDF_V3 )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDFv2 file\n"
+ "* Violation of specification: NOTES section not in specification" ) );
+
+ readBrdNotes( aBoardFile, aBoardState );
+
+ if( !comments.empty() )
+ {
+ std::list<std::string>::iterator its = comments.begin();
+ std::list<std::string>::iterator ite = comments.end();
+
+ while( its != ite )
+ {
+ noteComments.push_back( *its );
+ ++its;
+ }
+ }
+
+ return;
+ }
+
+ if( CompareToken( ".PLACEMENT", token ) )
+ {
+ if( aBoardState != IDF3::FILE_OUTLINE )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: expecting .BOARD_OUTLINE, have .PLACEMENT" ) );
+
+ readBrdPlacement( aBoardFile, aBoardState, aNoSubstituteOutlines );
+
+ if( !comments.empty() )
+ {
+ std::list<std::string>::iterator its = comments.begin();
+ std::list<std::string>::iterator ite = comments.end();
+
+ while( its != ite )
+ {
+ placeComments.push_back( *its );
+ ++its;
+ }
+ }
+
+ return;
+ }
+ } // while( aBoardFile.good()
+
+ return;
+} // readBrdSection()
+
+
+// read the board file data
+void IDF3_BOARD::readBoardFile( const std::string& aFileName, bool aNoSubstituteOutlines )
+{
+ std::ifstream brd;
+
+ brd.exceptions ( std::ifstream::badbit );
+
+ try
+ {
+ brd.open( aFileName.c_str(), std::ios_base::in | std::ios_base::binary );
+
+ if( !brd.is_open() )
+ {
+ ostringstream ostr;
+ ostr << "\n* could not open file: '" << aFileName << "'";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ std::string iline; // the input line
+ bool isComment; // true if a line just read in is a comment line
+ std::streampos pos;
+ IDF3::FILE_STATE state = IDF3::FILE_START;
+
+ // note: as per IDFv3 specification:
+ // "The Header section must be the first section in the file, the second
+ // section must be the Outline section, and the last section must be the
+ // Placement section. All other sections may be in any order."
+
+ // further notes: Except for the HEADER section, sections may be preceeded by
+ // comment lines which will be copied back out on write(). No comments may
+ // be associated with the board file itself since the only logical location
+ // for unambiguous association is at the end of the file, which is inconvenient
+ // for large files.
+
+ readBrdHeader( brd, state );
+
+ // read the various sections
+ while( state != IDF3::FILE_PLACEMENT && brd.good() )
+ readBrdSection( brd, state, aNoSubstituteOutlines );
+
+ if( !brd.good() )
+ {
+ // check if we have valid data
+ if( brd.eof() && state >= IDF3::FILE_OUTLINE && state < IDF3::FILE_INVALID )
+ {
+ brd.close();
+ return;
+ }
+
+ brd.close();
+
+ ostringstream ostr;
+ ostr << "\n* empty IDF file: '" << aFileName << "'";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( brd.good() && state == IDF3::FILE_PLACEMENT )
+ {
+ // read in any trailing lines and report on ignored comments (minor fault)
+ // and any non-comment item (non-compliance with IDFv3)
+ while( brd.good() )
+ {
+ while( !FetchIDFLine( brd, iline, isComment, pos ) && brd.good() );
+
+ // normally this is a fault but we have all the data in accordance with specs
+ if( ( !brd.good() && !brd.eof() ) || iline.empty() )
+ break;
+
+ if( isComment )
+ {
+ ERROR_IDF << "[warning]: trailing comments after PLACEMENT\n";
+ }
+ else
+ {
+ ostringstream ostr;
+ ostr << "\n* problems reading file: '" << aFileName << "'";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF file\n"
+ "* Violation of specification: non-comment lines after PLACEMENT section" ) );
+ }
+ }
+ }
+ }
+ catch( const std::exception& e )
+ {
+ brd.exceptions ( std::ios_base::goodbit );
+
+ if( brd.is_open() )
+ brd.close();
+
+ throw;
+ }
+
+ brd.close();
+ return;
+} // readBoardFile()
+
+
+// read the library sections (outlines)
+void IDF3_BOARD::readLibSection( std::ifstream& aLibFile, IDF3::FILE_STATE& aLibState, IDF3_BOARD* aBoard )
+{
+ if( aBoard == NULL )
+ {
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "\n* BUG: invoked with NULL reference aBoard" ) );
+ }
+
+ std::list< std::string > comments; // comments associated with a section
+
+ // Reads in .ELECTRICAL, .MECHANICAL or #COMMENTS
+ std::string iline; // the input line
+ bool isComment; // true if a line just read in is a comment line
+ std::streampos pos;
+ int idx = 0;
+ bool quoted = false;
+ std::string token;
+ IDF3_COMP_OUTLINE *pout = new IDF3_COMP_OUTLINE( this );
+
+ if( !pout )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "\n* memory allocation failure" ) );
+
+ while( aLibFile.good() )
+ {
+ while( !FetchIDFLine( aLibFile, iline, isComment, pos ) && aLibFile.good() );
+
+ if( !aLibFile.good() && !aLibFile.eof() )
+ {
+ delete pout;
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "problems reading library section" ) );
+ }
+
+ // no data was read; this only happens at eof()
+ if( iline.empty() )
+ {
+ delete pout;
+ return;
+ }
+
+ if( isComment )
+ {
+ comments.push_back( iline );
+ continue;
+ }
+
+ // This must be a header
+ GetIDFString( iline, token, quoted, idx );
+
+ if( quoted )
+ {
+ ostringstream ostr;
+ ostr << "invalid IDF library\n";
+ ostr << "* Violation of specification: quoted string where .ELECTRICAL or .MECHANICAL expected\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* pos: " << pos;
+ delete pout;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( CompareToken( ".ELECTRICAL", token ) || CompareToken( ".MECHANICAL", token ) )
+ {
+ pout->readData( aLibFile, token, idfVer );
+
+ if( !comments.empty() )
+ {
+ std::list<std::string>::iterator its = comments.begin();
+ std::list<std::string>::iterator ite = comments.end();
+
+ while( its != ite )
+ {
+ pout->AddComment( *its );
+ ++its;
+ }
+ }
+
+ IDF3_COMP_OUTLINE* cop = aBoard->GetComponentOutline( pout->GetUID() );
+
+ if( cop == NULL )
+ {
+ compOutlines.insert( pair<const std::string, IDF3_COMP_OUTLINE*>( pout->GetUID(), pout ) );
+ }
+ else
+ {
+ if( MatchCompOutline( pout, cop ) )
+ {
+ delete pout;
+ // everything is fine; the outlines are genuine duplicates
+ return;
+ }
+
+ ostringstream ostr;
+ ostr << "invalid IDF library\n";
+ ostr << "duplicate Component Outline: '" << pout->GetUID() << "'\n";
+ ostr << "* Violation of specification: multiple outlines have the same GEOM and PART name\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* pos: " << pos;
+ delete pout;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ return;
+ }
+ else
+ {
+ ostringstream ostr;
+ ostr << "invalid IDF library\n";
+ ostr << "* Expecting .ELECTRICAL or .MECHANICAL, got '" << token << "'\n";
+ ostr << "* line: '" << iline << "'\n";
+ ostr << "* pos: " << pos;
+ delete pout;
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+ }
+
+ delete pout;
+
+ if( !aLibFile.eof() )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "problems reading IDF library file" ) );
+
+ return;
+}
+
+
+// read the library HEADER
+void IDF3_BOARD::readLibHeader( std::ifstream& aLibFile, IDF3::FILE_STATE& aLibState )
+{
+ std::string iline; // the input line
+ bool isComment; // true if a line just read in is a comment line
+ std::streampos pos;
+ int idx = 0;
+ bool quoted = false;
+ std::string token;
+
+ // RECORD 1: ".HEADER" must be the very first line
+ while( !FetchIDFLine( aLibFile, iline, isComment, pos ) && aLibFile.good() );
+
+ if( !aLibFile.good() )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF library file\n"
+ "* premature end of file (no HEADER)" ) );
+
+ if( isComment )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF library file\n"
+ "* Violation of specification: first line must be .HEADER" ) );
+
+ if( !CompareToken( ".HEADER", iline ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF library file\n"
+ "* Violation of specification:\n"
+ "* first line must be .HEADER and have no quotes or trailing text" ) );
+
+ // RECORD 2:
+ // File Type [str]: LIBRARY_FILE
+ // IDF Version Number [float]: must be 3.0
+ // Source System [str]: ignored
+ // Date [str]: ignored
+ // Library File Version [int]: ignored
+ while( !FetchIDFLine( aLibFile, iline, isComment, pos ) && aLibFile.good() );
+
+ if( !aLibFile.good() )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF library file\n"
+ "* premature end of HEADER" ) );
+
+ if( isComment )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF library file\n"
+ "* Violation of specification: comment within .HEADER section" ) );
+
+ idx = 0;
+ GetIDFString( iline, token, quoted, idx );
+
+ if( quoted )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF library file\n"
+ "* Violation of specification:\n"
+ "* file Type in HEADER section must not be in quotes" ) );
+
+ if( !CompareToken( "LIBRARY_FILE", token ) )
+ {
+ ostringstream ostr;
+ ostr << "invalid IDF library\n";
+ ostr << "* Expecting string: LIBRARY_FILE (got '" << token << "')\n";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF library file\n"
+ "* Violation of specification: HEADER section, RECORD 2: no FIELD 2" ) );
+
+ if( quoted )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF library file\n"
+ "* Violation of specification: IDF Version must not be in quotes" ) );
+
+ if( !token.compare( "3.0" ) || !token.compare( "3." ) || !token.compare( "3" ) )
+ idfVer = IDF_V3;
+ else if( !token.compare( "2.0" ) || !token.compare( "2." ) || !token.compare( "2" ) )
+ idfVer = IDF_V2;
+ else
+ {
+ ostringstream ostr;
+
+ ostr << "unsupported IDF version\n";
+ ostr << "* Expecting version to be a variant of '3.0', '2.0' (value: '" << token << "')\n";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF library file\n"
+ "* Violation of specification:\n"
+ "* HEADER section, RECORD 2, FIELD 3: no Source System string" ) );
+
+ libSource = token;
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF library file\n"
+ "* Violation of specification:\n"
+ "* HEADER section, RECORD 2, FIELD 4: no Date string" ) );
+
+ libDate = token;
+
+ if( !GetIDFString( iline, token, quoted, idx ) )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF library file\n"
+ "* Violation of specification:\n"
+ "* HEADER section, RECORD 2, FIELD 5: no Board File Version number" ) );
+
+ std::istringstream istr;
+ istr.str( token );
+
+ istr >> libFileVersion;
+
+ if( istr.fail() )
+ {
+ ERROR_IDF << "invalid Library File Version in header\n";
+ cerr << "* Setting default version of 1\n";
+ libFileVersion = 1;
+ }
+
+ if( quoted )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF library file\n"
+ "* Violation of specification:\n"
+ "* HEADER section, RECORD 2, FIELD 5: Library File Version must not be in quotes" ) );
+
+ // RECORD 3:
+ // .END_HEADER
+ while( !FetchIDFLine( aLibFile, iline, isComment, pos ) && aLibFile.good() );
+
+ if( ( !aLibFile.good() && !aLibFile.eof() ) || iline.empty() )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "problems reading library header, RECORD 3" ) );
+
+ if( isComment )
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__,
+ "invalid IDF library file\n"
+ "* Violation of specification: comment within .HEADER section" ) );
+
+ if( !CompareToken( ".END_HEADER", iline ) )
+ {
+ ostringstream ostr;
+ ostr << "invalid IDF header\n";
+ ostr << "* Violation of specification: expected .END_HEADER (got '" << iline << "')\n";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ aLibState = IDF3::FILE_HEADER;
+ return;
+}
+
+
+// read the library file data
+void IDF3_BOARD::readLibFile( const std::string& aFileName )
+{
+ std::ifstream lib;
+
+ lib.exceptions ( std::ifstream::badbit );
+
+ try
+ {
+ lib.open( aFileName.c_str(), std::ios_base::in | std::ios_base::binary );
+
+ IDF3::FILE_STATE state = IDF3::FILE_START;
+
+ readLibHeader( lib, state );
+
+ while( lib.good() ) readLibSection( lib, state, this );
+ }
+ catch( const std::exception& e )
+ {
+ lib.exceptions ( std::ios_base::goodbit );
+
+ if( lib.is_open() )
+ lib.close();
+
+ throw;
+ }
+
+ lib.close();
+ return;
+}
+
+
+bool IDF3_BOARD::ReadFile( const wxString& aFullFileName, bool aNoSubstituteOutlines )
+{
+ // 1. Check that the file extension is 'emn'
+ // 2. Check if a file with extension 'emp' exists and read it
+ // 3. Open the specified filename and read it
+
+ std::string fname = TO_UTF8( aFullFileName );
+
+ wxFileName brdname( aFullFileName );
+ wxFileName libname( aFullFileName );
+
+ brdname.SetExt( wxT( "emn" ) );
+ libname.SetExt( wxT( "emp" ) );
+
+ std::string bfname = TO_UTF8( aFullFileName );
+
+ try
+ {
+ if( !brdname.IsOk() )
+ {
+ ostringstream ostr;
+ ostr << "\n* invalid file name: '" << bfname << "'";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !brdname.FileExists() )
+ {
+ ostringstream ostr;
+ ostr << "\n* no such file: '" << bfname << "'";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( !brdname.IsFileReadable() )
+ {
+ ostringstream ostr;
+ ostr << "\n* cannot read file: '" << bfname << "'";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ bfname = TO_UTF8( brdname.GetFullPath() );
+ std::string lfname = TO_UTF8( libname.GetFullPath() );
+
+ if( !libname.FileExists() )
+ {
+ // NOTE: Since this is a common case we simply proceed
+ // with the assumption that there is no library file;
+ // however we print a message to inform the user.
+ ERROR_IDF;
+ cerr << "no associated library file (*.emp)\n";
+ }
+ else if( !libname.IsFileReadable() )
+ {
+ ostringstream ostr;
+ ostr << "\n* cannot read library file: '" << lfname << "'";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+ else
+ {
+ // read the library file before proceeding
+ readLibFile( lfname );
+ }
+
+ // read the board file
+ readBoardFile( bfname, aNoSubstituteOutlines );
+ }
+ catch( const std::exception& e )
+ {
+ Clear();
+ errormsg = e.what();
+
+ return false;
+ }
+
+ return true;
+}
+
+
+// write the library file data
+bool IDF3_BOARD::writeLibFile( const std::string& aFileName )
+{
+ std::ofstream lib;
+ lib.exceptions( std::ofstream::failbit );
+
+ try
+ {
+ lib.open( aFileName.c_str(), std::ios_base::out );
+
+ wxDateTime tdate( time( NULL ) );
+
+ if( idfSource.empty() )
+ idfSource = "KiCad-IDF Framework";
+
+ ostringstream fileDate;
+ fileDate << setfill( '0' ) << setw(4) << tdate.GetYear();
+ fileDate << "/" << setw(2) << tdate.GetMonth() << "/" << tdate.GetDay();
+ fileDate << "." << tdate.GetHour() << ":" << tdate.GetMinute() << ":" << tdate.GetSecond();
+ libDate = fileDate.str();
+
+ lib << ".HEADER\n";
+ lib << "LIBRARY_FILE 3.0 \"Created by " << idfSource;
+ lib << "\" " << libDate << " " << (++libFileVersion) << "\n";
+ lib << ".END_HEADER\n\n";
+
+ std::map< std::string, IDF3_COMP_OUTLINE*>::iterator its = compOutlines.begin();
+ std::map< std::string, IDF3_COMP_OUTLINE*>::iterator ite = compOutlines.end();
+
+ while( its != ite )
+ {
+ its->second->writeData( lib );
+ ++its;
+ }
+
+ }
+ catch( const std::exception& e )
+ {
+ lib.exceptions( std::ios_base::goodbit );
+
+ if( lib.is_open() )
+ lib.close();
+
+ throw;
+ }
+
+ lib.close();
+
+ return true;
+}
+
+// write the board file data
+void IDF3_BOARD::writeBoardFile( const std::string& aFileName )
+{
+ std::ofstream brd;
+ brd.exceptions( std::ofstream::failbit );
+
+ try
+ {
+ brd.open( aFileName.c_str(), std::ios_base::out );
+
+ wxDateTime tdate( time( NULL ) );
+
+ if( idfSource.empty() )
+ idfSource = "KiCad-IDF Framework";
+
+ ostringstream fileDate;
+ fileDate << setfill( '0' ) << setw(4) << tdate.GetYear();
+ fileDate << "/" << setw(2) << tdate.GetMonth() << "/" << tdate.GetDay();
+ fileDate << "." << tdate.GetHour() << ":" << tdate.GetMinute() << ":" << tdate.GetSecond();
+ brdDate = fileDate.str();
+
+ brd << ".HEADER\n";
+ brd << "BOARD_FILE 3.0 \"Created by " << idfSource;
+ brd << "\" " << brdDate << " " << (++brdFileVersion) << "\n";
+
+ if( boardName.empty() )
+ brd << "\"BOARD WITH NO NAME\" ";
+ else
+ brd << "\"" << boardName << "\" ";
+
+ brd << setw(1) << setfill( ' ' );
+
+ if( unit == IDF3::UNIT_MM )
+ brd << "MM\n";
+ else
+ brd << "THOU\n";
+
+ brd << ".END_HEADER\n\n";
+
+ // write the BOARD_OUTLINE
+ olnBoard.writeData( brd );
+
+ // OTHER outlines
+ do
+ {
+ std::map<std::string, OTHER_OUTLINE*>::iterator its = olnOther.begin();
+ std::map<std::string, OTHER_OUTLINE*>::iterator ite = olnOther.end();
+
+ while(its != ite )
+ {
+ its->second->writeData( brd );
+ ++its;
+ }
+
+ } while( 0 );
+
+ // ROUTE outlines
+ do
+ {
+ std::list<ROUTE_OUTLINE*>::iterator its = olnRoute.begin();
+ std::list<ROUTE_OUTLINE*>::iterator ite = olnRoute.end();
+
+ while( its != ite )
+ {
+ (*its)->writeData( brd );
+ ++its;
+ }
+
+ } while( 0 );
+
+ // PLACEMENT outlines
+ do
+ {
+ std::list<PLACE_OUTLINE*>::iterator its = olnPlace.begin();
+ std::list<PLACE_OUTLINE*>::iterator ite = olnPlace.end();
+
+ while( its != ite )
+ {
+ (*its)->writeData( brd );
+ ++its;
+ }
+
+ } while( 0 );
+
+ // ROUTE KEEPOUT outlines
+ do
+ {
+ std::list<ROUTE_KO_OUTLINE*>::iterator its = olnRouteKeepout.begin();
+ std::list<ROUTE_KO_OUTLINE*>::iterator ite = olnRouteKeepout.end();
+
+ while( its != ite )
+ {
+ (*its)->writeData( brd );
+ ++its;
+ }
+
+ } while( 0 );
+
+ // VIA KEEPOUT outlines
+ do
+ {
+ std::list<VIA_KO_OUTLINE*>::iterator its = olnViaKeepout.begin();
+ std::list<VIA_KO_OUTLINE*>::iterator ite = olnViaKeepout.end();
+
+ while( its != ite )
+ {
+ (*its)->writeData( brd );
+ ++its;
+ }
+
+ } while( 0 );
+
+ // PLACE KEEPOUT outlines
+ do
+ {
+ std::list<PLACE_KO_OUTLINE*>::iterator its = olnPlaceKeepout.begin();
+ std::list<PLACE_KO_OUTLINE*>::iterator ite = olnPlaceKeepout.end();
+
+ while( its != ite )
+ {
+ (*its)->writeData( brd );
+ ++its;
+ }
+
+ } while( 0 );
+
+ // PLACEMENT GROUP outlines
+ do
+ {
+ std::multimap<std::string, GROUP_OUTLINE*>::iterator its = olnGroup.begin();
+ std::multimap<std::string, GROUP_OUTLINE*>::iterator ite = olnGroup.end();
+
+ while( its != ite )
+ {
+ its->second->writeData( brd );
+ ++its;
+ }
+
+ } while( 0 );
+
+ // Drilled holes
+ do
+ {
+ std::list<std::string>::iterator itds = drillComments.begin();
+ std::list<std::string>::iterator itde = drillComments.end();
+
+ while( itds != itde )
+ {
+ brd << "# " << *itds << "\n";
+ ++itds;
+ }
+
+ brd << ".DRILLED_HOLES\n";
+
+ std::list<IDF_DRILL_DATA*>::iterator itbs = board_drills.begin();
+ std::list<IDF_DRILL_DATA*>::iterator itbe = board_drills.end();
+
+ while( itbs != itbe )
+ {
+ (*itbs)->write( brd, unit );
+ ++itbs;
+ }
+
+ std::map< std::string, IDF3_COMPONENT*>::iterator itcs = components.begin();
+ std::map< std::string, IDF3_COMPONENT*>::iterator itce = components.end();
+
+ while( itcs != itce )
+ {
+ itcs->second->writeDrillData( brd );
+ ++itcs;
+ }
+
+ brd << ".END_DRILLED_HOLES\n\n";
+ } while( 0 );
+
+ // Notes
+ if( !notes.empty() )
+ {
+ std::list<std::string>::iterator itncs = noteComments.begin();
+ std::list<std::string>::iterator itnce = noteComments.end();
+
+ while( itncs != itnce )
+ {
+ brd << "# " << *itncs << "\n";
+ ++itncs;
+ }
+
+ brd << ".NOTES\n";
+
+ std::list<IDF_NOTE*>::iterator itns = notes.begin();
+ std::list<IDF_NOTE*>::iterator itne = notes.end();
+
+ while( itns != itne )
+ {
+ (*itns)->writeNote( brd, unit );
+ ++itns;
+ }
+
+ brd << ".END_NOTES\n\n";
+
+ }
+
+ // Placement
+ if( !components.empty() )
+ {
+ std::list<std::string>::iterator itpcs = placeComments.begin();
+ std::list<std::string>::iterator itpce = placeComments.end();
+
+ while( itpcs != itpce )
+ {
+ brd << "# " << *itpcs << "\n";
+ ++itpcs;
+ }
+
+ std::map< std::string, IDF3_COMPONENT*>::iterator itcs = components.begin();
+ std::map< std::string, IDF3_COMPONENT*>::iterator itce = components.end();
+
+ // determine if there are any component outlines at all and avoid
+ // writing an empty PLACEMENT section if there are no outlines.
+ // this will cost a little time but prevents software such as
+ // CircuitWorks from segfaulting on an empty section.
+
+ bool hasOutlines = false;
+
+ while( itcs != itce )
+ {
+ if( itcs->second->GetOutlinesSize() > 0 )
+ {
+ itcs = components.begin();
+ hasOutlines = true;
+ break;
+ }
+
+ ++itcs;
+ }
+
+ if( hasOutlines )
+ {
+ brd << ".PLACEMENT\n";
+
+ while( itcs != itce )
+ {
+ itcs->second->writePlaceData( brd );
+ ++itcs;
+ }
+
+ brd << ".END_PLACEMENT\n";
+ }
+
+ }
+
+ }
+ catch( const std::exception& e )
+ {
+ brd.exceptions( std::ios_base::goodbit );
+
+ if( brd.is_open() )
+ brd.close();
+
+ throw;
+ }
+
+ brd.close();
+
+ return;
+}
+
+
+bool IDF3_BOARD::WriteFile( const wxString& aFullFileName, bool aUnitMM, bool aForceUnitFlag )
+{
+ if( aUnitMM != IDF3::UNIT_THOU )
+ setUnit( IDF3::UNIT_MM, aForceUnitFlag );
+ else
+ setUnit( IDF3::UNIT_THOU, aForceUnitFlag );
+
+ // 1. Check that the file extension is 'emn'
+ // 2. Write the *.emn file according to the IDFv3 spec
+ // 3. Write the *.emp file according to the IDFv3 spec
+
+ std::string fname = TO_UTF8( aFullFileName );
+
+ wxFileName brdname( aFullFileName );
+ wxFileName libname( aFullFileName );
+
+ brdname.SetExt( wxT( "emn" ) );
+ libname.SetExt( wxT( "emp" ) );
+
+ std::string bfname = TO_UTF8( aFullFileName );
+
+ try
+ {
+ if( !brdname.IsOk() )
+ {
+ ostringstream ostr;
+ ostr << "\n* invalid file name: '" << bfname << "'";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ if( brdname.FileExists() && !brdname.IsFileWritable() )
+ {
+ ostringstream ostr;
+ ostr << "cannot overwrite existing board file\n";
+ ostr << "* filename: '" << bfname << "'";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ bfname = TO_UTF8( brdname.GetFullPath() );
+ std::string lfname = TO_UTF8( libname.GetFullPath() );
+
+ if( libname.FileExists() && !libname.IsFileWritable() )
+ {
+ ostringstream ostr;
+ ostr << "cannot overwrite existing library file\n";
+ ostr << "* filename: '" << lfname << "'";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ writeLibFile( lfname );
+ writeBoardFile( bfname );
+
+ }
+ catch( const std::exception& e )
+ {
+ errormsg = e.what();
+
+ return false;
+ }
+
+ return true;
+}
+
+
+const std::string& IDF3_BOARD::GetIDFSource( void )
+{
+ return idfSource;
+}
+
+
+void IDF3_BOARD::SetIDFSource( const std::string& aIDFSource )
+{
+ idfSource = aIDFSource;
+ return;
+}
+
+const std::string& IDF3_BOARD::GetBoardSource( void )
+{
+ return brdSource;
+}
+
+const std::string& IDF3_BOARD::GetLibrarySource( void )
+{
+ return libSource;
+}
+
+const std::string& IDF3_BOARD::GetBoardDate( void )
+{
+ return brdDate;
+}
+
+const std::string& IDF3_BOARD::GetLibraryDate( void )
+{
+ return libDate;
+}
+
+int IDF3_BOARD::GetBoardVersion( void )
+{
+ return brdFileVersion;
+}
+
+bool IDF3_BOARD::SetBoardVersion( int aVersion )
+{
+ if( aVersion < 0 )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* board version (" << aVersion << ") must be >= 0";
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ brdFileVersion = aVersion;
+
+ return true;
+}
+
+
+int IDF3_BOARD::GetLibraryVersion( void )
+{
+ return libFileVersion;
+}
+
+
+bool IDF3_BOARD::SetLibraryVersion( int aVersion )
+{
+ if( aVersion < 0 )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* library version (" << aVersion << ") must be >= 0";
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ libFileVersion = aVersion;
+
+ return true;
+}
+
+
+double IDF3_BOARD::GetUserScale( void )
+{
+ return userScale;
+}
+
+
+bool IDF3_BOARD::SetUserScale( double aScaleFactor )
+{
+ if( aScaleFactor == 0.0 )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* BUG: user scale factor must not be 0";
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ userScale = aScaleFactor;
+ return true;
+}
+
+int IDF3_BOARD::GetUserPrecision( void )
+{
+ return userPrec;
+}
+
+bool IDF3_BOARD::SetUserPrecision( int aPrecision )
+{
+ if( aPrecision < 1 || aPrecision > 8 )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* precision value (" << aPrecision << ") must be 1..8";
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ userPrec = aPrecision;
+ return true;
+}
+
+
+void IDF3_BOARD::GetUserOffset( double& aXoff, double& aYoff )
+{
+ aXoff = userXoff;
+ aYoff = userYoff;
+ return;
+}
+
+
+void IDF3_BOARD::SetUserOffset( double aXoff, double aYoff )
+{
+ userXoff = aXoff;
+ userYoff = aYoff;
+ return;
+}
+
+
+bool IDF3_BOARD::AddBoardOutline( IDF_OUTLINE* aOutline )
+{
+ if( !olnBoard.AddOutline( aOutline ) )
+ {
+ errormsg = olnBoard.GetError();
+
+ return false;
+ }
+
+ return true;
+}
+
+
+bool IDF3_BOARD::DelBoardOutline( IDF_OUTLINE* aOutline )
+{
+ if( !olnBoard.DelOutline( aOutline ) )
+ {
+ errormsg = olnBoard.GetError();
+ return false;
+ }
+
+ return true;
+}
+
+
+bool IDF3_BOARD::DelBoardOutline( size_t aIndex )
+{
+ if( !olnBoard.DelOutline( aIndex ) )
+ {
+ errormsg = olnBoard.GetError();
+ return false;
+ }
+
+ return true;
+}
+
+
+size_t IDF3_BOARD::GetBoardOutlinesSize( void )
+{
+ return olnBoard.OutlinesSize();
+}
+
+
+BOARD_OUTLINE* IDF3_BOARD::GetBoardOutline( void )
+{
+ return &olnBoard;
+}
+
+
+const std::list< IDF_OUTLINE* >*const IDF3_BOARD::GetBoardOutlines( void )
+{
+ return olnBoard.GetOutlines();
+}
+
+
+IDF_DRILL_DATA* IDF3_BOARD::AddBoardDrill( double aDia, double aXpos, double aYpos,
+ IDF3::KEY_PLATING aPlating,
+ const std::string aHoleType,
+ IDF3::KEY_OWNER aOwner )
+{
+ IDF_DRILL_DATA* drill = new IDF_DRILL_DATA( aDia, aXpos, aYpos, aPlating,
+ "BOARD", aHoleType, aOwner );
+
+ if( drill != NULL )
+ board_drills.push_back( drill );
+
+ return drill;
+}
+
+IDF_DRILL_DATA* IDF3_BOARD::AddDrill( IDF_DRILL_DATA* aDrilledHole )
+{
+ if( !aDrilledHole )
+ return NULL;
+
+ // note: PANEL drills are essentially BOARD drills which
+ // the panel requires to be present
+ if( CompareToken( "BOARD", aDrilledHole->GetDrillRefDes() )
+ || CompareToken( "PANEL", aDrilledHole->GetDrillRefDes() ) )
+ {
+ board_drills.push_back( aDrilledHole );
+ return aDrilledHole;
+ }
+
+ return addCompDrill( aDrilledHole );
+}
+
+
+bool IDF3_BOARD::DelBoardDrill( double aDia, double aXpos, double aYpos )
+{
+ errormsg.clear();
+
+ std::list<IDF_DRILL_DATA*>::iterator sp = board_drills.begin();
+ std::list<IDF_DRILL_DATA*>::iterator ep = board_drills.end();
+ bool rval = false;
+
+ while( sp != ep )
+ {
+ if( (*sp)->Matches( aDia, aXpos, aYpos ) )
+ {
+#ifndef DISABLE_IDF_OWNERSHIP
+ IDF3::KEY_OWNER keyo = (*sp)->GetDrillOwner();
+
+ if( keyo == UNOWNED || ( keyo == MCAD && cadType == CAD_MECH )
+ || ( keyo == ECAD && cadType == CAD_ELEC ) )
+ {
+ rval = true;
+ delete *sp;
+ sp = board_drills.erase( sp );
+ continue;
+ }
+ else
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* ownership violation; drill owner (";
+
+ switch( keyo )
+ {
+ case ECAD:
+ ostr << "ECAD";
+ break;
+
+ case MCAD:
+ ostr << "MCAD";
+ break;
+
+ default:
+ ostr << "invalid: " << keyo;
+ break;
+ }
+
+ ostr << ") may not be modified by ";
+
+ if( cadType == CAD_MECH )
+ ostr << "MCAD";
+ else
+ ostr << "ECAD";
+
+ errormsg = ostr.str();
+
+ ++sp;
+ continue;
+ }
+#else
+ rval = true;
+ delete *sp;
+ sp = board_drills.erase( sp );
+ continue;
+#endif
+ }
+
+ ++sp;
+ }
+
+ return rval;
+}
+
+
+// a slot is a deficient representation of a kicad slotted hole;
+// it is usually associated with a component but IDFv3 does not
+// provide for such an association. Note: this mechanism must bypass
+// the BOARD_OUTLINE ownership rules
+bool IDF3_BOARD::AddSlot( double aWidth, double aLength, double aOrientation, double aX, double aY )
+{
+ if( aWidth < IDF_MIN_DIA_MM )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* slot width (" << aWidth << ") must be >= " << IDF_MIN_DIA_MM;
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ if( aLength < IDF_MIN_DIA_MM )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* slot length (" << aLength << ") must be >= " << IDF_MIN_DIA_MM;
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ IDF_POINT c[2]; // centers
+ IDF_POINT pt[4];
+
+ double a1 = aOrientation / 180.0 * M_PI;
+ double a2 = a1 + M_PI_2;
+ double d1 = aLength / 2.0;
+ double d2 = aWidth / 2.0;
+ double sa1 = sin( a1 );
+ double ca1 = cos( a1 );
+ double dsa2 = d2 * sin( a2 );
+ double dca2 = d2 * cos( a2 );
+
+ c[0].x = aX + d1 * ca1;
+ c[0].y = aY + d1 * sa1;
+
+ c[1].x = aX - d1 * ca1;
+ c[1].y = aY - d1 * sa1;
+
+ pt[0].x = c[0].x - dca2;
+ pt[0].y = c[0].y - dsa2;
+
+ pt[1].x = c[1].x - dca2;
+ pt[1].y = c[1].y - dsa2;
+
+ pt[2].x = c[1].x + dca2;
+ pt[2].y = c[1].y + dsa2;
+
+ pt[3].x = c[0].x + dca2;
+ pt[3].y = c[0].y + dsa2;
+
+ IDF_OUTLINE* outline = new IDF_OUTLINE;
+
+ if( outline == NULL )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* could not create an outline object";
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ // first straight run
+ IDF_SEGMENT* seg = new IDF_SEGMENT( pt[0], pt[1] );
+ outline->push( seg );
+ // first 180 degree cap
+ seg = new IDF_SEGMENT( c[1], pt[1], -180.0, true );
+ outline->push( seg );
+ // final straight run
+ seg = new IDF_SEGMENT( pt[2], pt[3] );
+ outline->push( seg );
+ // final 180 degree cap
+ seg = new IDF_SEGMENT( c[0], pt[3], -180.0, true );
+ outline->push( seg );
+
+ if( !olnBoard.addOutline( outline ) )
+ {
+ errormsg = olnBoard.GetError();
+ return false;
+ }
+
+ return true;
+}
+
+
+IDF_DRILL_DATA* IDF3_BOARD::addCompDrill( double aDia, double aXpos, double aYpos,
+ IDF3::KEY_PLATING aPlating,
+ const std::string aHoleType,
+ IDF3::KEY_OWNER aOwner,
+ const std::string& aRefDes )
+{
+ // first find the matching component; if it doesn't exist we must create it somehow -
+ // question is, do we need a component outline at this stage or can those be added later?
+ //
+ // Presumably we can create a component with no outline and add the outlines later.
+ // If a component is created and an outline specified but the outline is not loaded,
+ // we're screwed if (a) we have already read the library file (*.emp) or (b) we don't
+ // know the filename
+
+ std::string refdes = aRefDes;
+
+ // note: for components 'NOREFDES' would be assigned a Unique ID, but for holes
+ // there is no way of associating the hole with the correct entity (if any)
+ // so a hole added with "NOREFDES" goes to a generic component "NOREFDES"
+ if( refdes.empty() )
+ refdes = "NOREFDES";
+
+ // check if the target is BOARD or PANEL
+ if( CompareToken( "BOARD", refdes ) )
+ return AddBoardDrill( aDia, aXpos, aYpos, aPlating, aHoleType, aOwner );
+
+ if( CompareToken( "PANEL", refdes ) )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* PANEL data not supported";
+ errormsg = ostr.str();
+
+ return NULL;
+ }
+
+ std::map<std::string, IDF3_COMPONENT*>::iterator ref = components.find( refdes );
+
+ if( ref == components.end() )
+ {
+ // create the item
+ IDF3_COMPONENT* comp = new IDF3_COMPONENT( this );
+
+ if( comp == NULL )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* could not create new component object";
+ errormsg = ostr.str();
+
+ return NULL;
+ }
+
+ comp->SetParent( this );
+ comp->SetRefDes( refdes );
+ ref = components.insert( std::pair< std::string, IDF3_COMPONENT*> ( comp->GetRefDes(), comp ) ).first;
+ }
+
+ // add the drill
+ IDF_DRILL_DATA* dp = ref->second->AddDrill( aDia, aXpos, aYpos, aPlating, aHoleType, aOwner );
+
+ if( !dp )
+ {
+ errormsg = ref->second->GetError();
+ return NULL;
+ }
+
+ return dp;
+}
+
+
+IDF_DRILL_DATA* IDF3_BOARD::addCompDrill( IDF_DRILL_DATA* aDrilledHole )
+{
+ if( !aDrilledHole )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): NULL pointer";
+ errormsg = ostr.str();
+
+ return NULL;
+ }
+
+ if( CompareToken( "PANEL", aDrilledHole->GetDrillRefDes() ) )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* PANEL data not supported";
+ errormsg = ostr.str();
+
+ return NULL;
+ }
+
+ std::map<std::string, IDF3_COMPONENT*>::iterator ref = components.find( aDrilledHole->GetDrillRefDes() );
+
+ if( ref == components.end() )
+ {
+ // create the item
+ IDF3_COMPONENT* comp = new IDF3_COMPONENT( this );
+
+ if( comp == NULL )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* could not create new component object";
+ errormsg = ostr.str();
+
+ return NULL;
+ }
+
+ comp->SetParent( this );
+ comp->SetRefDes( aDrilledHole->GetDrillRefDes() );
+ ref = components.insert( std::pair< std::string, IDF3_COMPONENT*> ( comp->GetRefDes(), comp ) ).first;
+ }
+
+ IDF_DRILL_DATA* dp = ref->second->AddDrill( aDrilledHole );
+
+ if( !dp )
+ {
+ errormsg = ref->second->GetError();
+ return NULL;
+ }
+
+ return dp;
+}
+
+
+bool IDF3_BOARD::delCompDrill( double aDia, double aXpos, double aYpos, std::string aRefDes )
+{
+ errormsg.clear();
+
+ std::map<std::string, IDF3_COMPONENT*>::iterator ref = components.find( aRefDes );
+
+ if( ref == components.end() )
+ return false;
+
+ if( !ref->second->DelDrill( aDia, aXpos, aYpos ) )
+ {
+ errormsg = ref->second->GetError();
+ return false;
+ }
+
+ return true;
+}
+
+
+bool IDF3_BOARD::AddComponent( IDF3_COMPONENT* aComponent )
+{
+ if( !aComponent )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__;
+ ostr << "(): Invalid component pointer (NULL)";
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ if( components.insert( std::pair<std::string, IDF3_COMPONENT*>
+ ( aComponent->GetRefDes(), aComponent ) ).second == false )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): \n";
+ ostr << "* duplicate RefDes ('" << aComponent->GetRefDes() << "')";
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ return true;
+}
+
+
+bool IDF3_BOARD::DelComponent( IDF3_COMPONENT* aComponent )
+{
+ errormsg.clear();
+
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !checkComponentOwnership( __LINE__, __FUNCTION__, aComponent ) )
+ return false;
+#endif
+
+ std::map<std::string, IDF3_COMPONENT*>::iterator it =
+ components.find( aComponent->GetRefDes() );
+
+ if( it == components.end() )
+ return false;
+
+ delete it->second;
+ components.erase( it );
+
+ return true;
+}
+
+
+bool IDF3_BOARD::DelComponent( size_t aIndex )
+{
+ if( aIndex >= components.size() )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): \n";
+ ostr << "* aIndex (" << aIndex << ") out of range (" << components.size() << ")";
+ errormsg = ostr.str();
+
+ return false;
+ }
+
+ std::map<std::string, IDF3_COMPONENT*>::iterator it = components.begin();
+
+ while( aIndex-- > 0 ) ++it;
+
+#ifndef DISABLE_IDF_OWNERSHIP
+ if( !checkComponentOwnership( __LINE__, __FUNCTION__, it->second ) )
+ return false;
+#endif
+
+ delete it->second;
+ components.erase( it );
+
+ return true;
+}
+
+
+size_t IDF3_BOARD::GetComponentsSize( void )
+{
+ return components.size();
+}
+
+
+std::map< std::string, IDF3_COMPONENT* >*const IDF3_BOARD::GetComponents( void )
+{
+ return &components;
+}
+
+
+IDF3_COMPONENT* IDF3_BOARD::FindComponent( std::string aRefDes )
+{
+ std::map<std::string, IDF3_COMPONENT*>::iterator it = components.find( aRefDes );
+
+ if( it == components.end() )
+ return NULL;
+
+ return it->second;
+}
+
+
+// returns a pointer to a component outline object or NULL
+// if the object doesn't exist
+IDF3_COMP_OUTLINE* IDF3_BOARD::GetComponentOutline( wxString aFullFileName )
+{
+ std::string fname = TO_UTF8( aFullFileName );
+ wxFileName idflib( aFullFileName );
+
+ if( !idflib.IsOk() )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): \n";
+ cerr << "* invalid file name: '" << fname << "'";
+ errormsg = ostr.str();
+
+ return NULL;
+ }
+
+ if( !idflib.FileExists() )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): \n";
+ cerr << "* no such file: '" << fname << "'";
+ errormsg = ostr.str();
+
+ return NULL;
+ }
+
+ if( !idflib.IsFileReadable() )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): \n";
+ cerr << "* cannot read file: '" << fname << "'";
+ errormsg = ostr.str();
+
+ return NULL;
+ }
+
+ std::map< std::string, std::string >::iterator itm = uidFileList.find( fname );
+
+ if( itm != uidFileList.end() )
+ return GetComponentOutline( itm->second );
+
+ IDF3_COMP_OUTLINE* cp = new IDF3_COMP_OUTLINE( this );
+
+ if( cp == NULL )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): \n";
+ cerr << "* failed to create outline\n";
+ cerr << "* filename: '" << fname << "'";
+ errormsg = ostr.str();
+
+ return NULL;
+ }
+
+ std::ifstream model;
+ model.exceptions ( std::ifstream::badbit );
+
+ try
+ {
+ model.open( fname.c_str(), std::ios_base::in | std::ios_base::binary );
+
+
+ std::string iline; // the input line
+ bool isComment; // true if a line just read in is a comment line
+ std::streampos pos;
+
+
+ while( true )
+ {
+ while( !FetchIDFLine( model, iline, isComment, pos ) && model.good() );
+
+ if( !model.good() )
+ {
+ ostringstream ostr;
+ ostr << "\n* problems reading file: '" << fname << "'";
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+
+ // accept comment lines, .ELECTRICAL, or .MECHANICAL only
+ if( isComment )
+ {
+ cp->AddComment( iline );
+ continue;
+ }
+
+ if( CompareToken( ".ELECTRICAL", iline ) || CompareToken( ".MECHANICAL", iline ) )
+ {
+ cp->readData( model, iline, idfVer );
+ break;
+ }
+ else
+ {
+ ostringstream ostr;
+ ostr << "faulty IDF component definition\n";
+ ostr << "* Expecting .ELECTRICAL or .MECHANICAL, got '" << iline << "'\n";
+ cerr << "* File: '" << fname << "'\n";
+
+ throw( IDF_ERROR( __FILE__, __FUNCTION__, __LINE__, ostr.str() ) );
+ }
+ } // while( true )
+ }
+ catch( const std::exception& e )
+ {
+ delete cp;
+
+ model.exceptions ( std::ios_base::goodbit );
+
+ if( model.is_open() )
+ model.close();
+
+ errormsg = e.what();
+
+ return NULL;
+ }
+
+ model.close();
+
+ // check the unique ID against the list from library components
+ std::list< std::string >::iterator lsts = uidLibList.begin();
+ std::list< std::string >::iterator lste = uidLibList.end();
+ std::string uid = cp->GetUID();
+ IDF3_COMP_OUTLINE* oldp = NULL;
+
+ while( lsts != lste )
+ {
+ if( ! lsts->compare( uid ) )
+ {
+ oldp = GetComponentOutline( uid );
+
+ if( MatchCompOutline( cp, oldp ) )
+ {
+ // everything is fine; the outlines are genuine duplicates; delete the copy
+ delete cp;
+ // make sure we can find the item via its filename
+ uidFileList.insert( std::pair< std::string, std::string>( fname, uid ) );
+ // return the pointer to the original
+ return oldp;
+ }
+ else
+ {
+ delete cp;
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* duplicate UID for different Component Outlines: '" << uid << "'\n";
+ ostr << "* original loaded from library, duplicate in current file\n";
+ ostr << "* file: '" << fname << "'";
+
+ errormsg = ostr.str();
+ return NULL;
+ }
+ }
+
+ ++lsts;
+ }
+
+ // if we got this far then any duplicates are from files previously read
+ oldp = GetComponentOutline( uid );
+
+ if( oldp == NULL )
+ {
+ // everything is fine, there are no existing entries
+ uidFileList.insert( std::pair< std::string, std::string>( fname, uid ) );
+ compOutlines.insert( pair<const std::string, IDF3_COMP_OUTLINE*>( uid, cp ) );
+
+ return cp;
+ }
+
+ if( MatchCompOutline( cp, oldp ) )
+ {
+ // everything is fine; the outlines are genuine duplicates; delete the copy
+ delete cp;
+ // make sure we can find the item via its other filename
+ uidFileList.insert( std::pair< std::string, std::string>( fname, uid ) );
+ // return the pointer to the original
+ return oldp;
+ }
+
+ delete cp;
+
+ // determine the file name of the first instance
+ std::map< std::string, std::string >::iterator ufls = uidFileList.begin();
+ std::map< std::string, std::string >::iterator ufle = uidFileList.end();
+ std::string oldfname;
+
+ while( ufls != ufle )
+ {
+ if( ! ufls->second.compare( uid ) )
+ {
+ oldfname = ufls->first;
+ break;
+ }
+
+ ++ufls;
+ }
+
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
+ ostr << "* duplicate UID for different Component Outlines: '" << uid << "'\n";
+ ostr << "* original file: '" << oldfname << "'\n";
+ ostr << "* this file: '" << fname << "'";
+
+ errormsg = ostr.str();
+ return NULL;
+}
+
+
+// returns a pointer to the component outline object with the
+// unique ID aComponentID
+IDF3_COMP_OUTLINE* IDF3_BOARD::GetComponentOutline( std::string aComponentID )
+{
+ std::map< std::string, IDF3_COMP_OUTLINE*>::iterator its = compOutlines.find( aComponentID );
+
+ if( its != compOutlines.end() )
+ return its->second;
+
+ return NULL;
+}
+
+
+// returns a pointer to the outline which is substituted
+// whenever a true outline cannot be found or is defective
+IDF3_COMP_OUTLINE* IDF3_BOARD::GetInvalidOutline( const std::string& aGeomName, const std::string& aPartName )
+{
+ std::string uid;
+ bool empty = false;
+
+ if( aGeomName.empty() && aPartName.empty() )
+ {
+ uid = "NOGEOM_NOPART";
+ empty = true;
+ }
+ else
+ {
+ uid = aGeomName + "_" + aPartName;
+ }
+
+ IDF3_COMP_OUTLINE* cp = GetComponentOutline( uid );
+
+ if( cp != NULL )
+ return cp;
+
+ cp = new IDF3_COMP_OUTLINE( this );
+
+ if( cp == NULL )
+ {
+ ostringstream ostr;
+ ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): ";
+ cerr << "could not create new outline";
+ errormsg = ostr.str();
+
+ return NULL;
+ }
+
+ if( empty )
+ cp->CreateDefaultOutline( "", "" );
+ else
+ cp->CreateDefaultOutline( aGeomName, aPartName );
+
+ compOutlines.insert( pair<const std::string, IDF3_COMP_OUTLINE*>(cp->GetUID(), cp) );
+
+ return cp;
+}
+
+
+// clears all data
+void IDF3_BOARD::Clear( void )
+{
+ // preserve the board thickness
+ double thickness = olnBoard.GetThickness();
+
+ idfSource.clear();
+ brdSource.clear();
+ libSource.clear();
+ brdDate.clear();
+ libDate.clear();
+ uidFileList.clear();
+ uidLibList.clear();
+ brdFileVersion = 0;
+ libFileVersion = 0;
+ iRefDes = 0;
+ sRefDes.clear();
+
+ // delete comment lists
+ noteComments.clear();
+ drillComments.clear();
+ placeComments.clear();
+
+ // delete notes
+ while( !notes.empty() )
+ {
+ delete notes.front();
+ notes.pop_front();
+ }
+
+ // delete drill list
+ do
+ {
+ std::list<IDF_DRILL_DATA*>::iterator ds = board_drills.begin();
+ std::list<IDF_DRILL_DATA*>::iterator de = board_drills.end();
+
+ while( ds != de )
+ {
+ delete *ds;
+ ++ds;
+ }
+
+ board_drills.clear();
+ } while(0);
+
+
+ // delete components
+ do
+ {
+ std::map<std::string, IDF3_COMPONENT*>::iterator cs = components.begin();
+ std::map<std::string, IDF3_COMPONENT*>::iterator ce = components.end();
+
+ while( cs != ce )
+ {
+ delete cs->second;
+ ++cs;
+ }
+
+ components.clear();
+ } while(0);
+
+
+ // delete component outlines
+ do
+ {
+ std::map<std::string, IDF3_COMP_OUTLINE*>::iterator cs = compOutlines.begin();
+ std::map<std::string, IDF3_COMP_OUTLINE*>::iterator ce = compOutlines.end();
+
+ while( cs != ce )
+ {
+ delete cs->second;
+ ++cs;
+ }
+
+ compOutlines.clear();
+ } while(0);
+
+
+ // delete OTHER outlines
+ do
+ {
+ std::map<std::string, OTHER_OUTLINE*>::iterator os = olnOther.begin();
+ std::map<std::string, OTHER_OUTLINE*>::iterator oe = olnOther.end();
+
+ while( os != oe )
+ {
+ delete os->second;
+ ++os;
+ }
+
+ olnOther.clear();
+ } while(0);
+
+
+ // delete ROUTE outlines
+ do
+ {
+ std::list<ROUTE_OUTLINE*>::iterator os = olnRoute.begin();
+ std::list<ROUTE_OUTLINE*>::iterator oe = olnRoute.end();
+
+ while( os != oe )
+ {
+ delete *os;
+ ++os;
+ }
+
+ olnRoute.clear();
+ } while(0);
+
+
+ // delete PLACE outlines
+ do
+ {
+ std::list<PLACE_OUTLINE*>::iterator os = olnPlace.begin();
+ std::list<PLACE_OUTLINE*>::iterator oe = olnPlace.end();
+
+ while( os != oe )
+ {
+ delete *os;
+ ++os;
+ }
+
+ olnPlace.clear();
+ } while(0);
+
+
+ // delete ROUTE KEEPOUT outlines
+ do
+ {
+ std::list<ROUTE_KO_OUTLINE*>::iterator os = olnRouteKeepout.begin();
+ std::list<ROUTE_KO_OUTLINE*>::iterator oe = olnRouteKeepout.end();
+
+ while( os != oe )
+ {
+ delete *os;
+ ++os;
+ }
+
+ olnRouteKeepout.clear();
+ } while(0);
+
+
+ // delete VIA KEEPOUT outlines
+ do
+ {
+ std::list<VIA_KO_OUTLINE*>::iterator os = olnViaKeepout.begin();
+ std::list<VIA_KO_OUTLINE*>::iterator oe = olnViaKeepout.end();
+
+ while( os != oe )
+ {
+ delete *os;
+ ++os;
+ }
+
+ olnViaKeepout.clear();
+ } while(0);
+
+
+ // delete PLACEMENT KEEPOUT outlines
+ do
+ {
+ std::list<PLACE_KO_OUTLINE*>::iterator os = olnPlaceKeepout.begin();
+ std::list<PLACE_KO_OUTLINE*>::iterator oe = olnPlaceKeepout.end();
+
+ while( os != oe )
+ {
+ delete *os;
+ ++os;
+ }
+
+ olnPlaceKeepout.clear();
+ } while(0);
+
+
+ // delete PLACEMENT GROUP outlines
+ do
+ {
+ std::multimap<std::string, GROUP_OUTLINE*>::iterator os = olnGroup.begin();
+ std::multimap<std::string, GROUP_OUTLINE*>::iterator oe = olnGroup.end();
+
+ while( os != oe )
+ {
+ delete os->second;
+ ++os;
+ }
+
+ olnGroup.clear();
+ } while(0);
+
+ boardName.clear();
+ olnBoard.setThickness( thickness );
+
+ state = FILE_START;
+ unit = UNIT_MM;
+ userScale = 1.0;
+ userXoff = 0.0;
+ userYoff = 0.0;
+
+ return;
+}
+
+
+const std::map<std::string, OTHER_OUTLINE*>*const
+IDF3_BOARD::GetOtherOutlines( void )
+{
+ return &olnOther;
+}
diff --git a/utils/idftools/idf_parser.h b/utils/idftools/idf_parser.h
new file mode 100644
index 0000000..e666e17
--- /dev/null
+++ b/utils/idftools/idf_parser.h
@@ -0,0 +1,721 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2014 Cirilo Bernardo
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/*
+ * NOTE:
+ *
+ * Rules to ensure friendly use within a DLL:
+ *
+ * 1. all functions which throw exceptions must not be publicly available;
+ * they must become FRIEND functions instead.
+ *
+ * 2. All objects with PRIVATE functions which throw exceptions when
+ * invoked by a PUBLIC function must indicate success or failure
+ * and make the exception information available via a GetError()
+ * routine.
+ *
+ * General notes:
+ *
+ * 1. Due to the complexity of objects and the risk of accumulated
+ * position errors, CAD packages should only delete or add complete
+ * components. If a component being added already exists, it is
+ * replaced by the new component IF and only if the CAD type is
+ * permitted to make such changes.
+ *
+ * 2. Internally all units shall be in mm and by default we shall
+ * write files with mm units. The internal flags mm/thou shall only
+ * be used to translate data being read from or written to files.
+ * This avoids the painful management of a mixture of mm and thou.
+ * The API shall require all dimensions in mm; for people using any
+ * other unit, it is their responsibility to perform the conversion
+ * to mm. Conversion back to thou may incur small rounding errors.
+ */
+
+
+#ifndef IDF_PARSER_H
+#define IDF_PARSER_H
+
+#include <idf_outlines.h>
+
+class IDF3_COMPONENT;
+
+class IDF3_COMP_OUTLINE_DATA
+{
+friend class IDF3_BOARD;
+friend class IDF3_COMPONENT;
+private:
+ double xoff; // X offset from KiCad or X placement from IDF file
+ double yoff; // Y offset from KiCad or Y placement from IDF file
+ double zoff; // height offset (specified in IDFv3 spec, corresponds to KiCad Z offset)
+ double aoff; // angular offset from KiCad or Rotation Angle from IDF file
+ std::string errormsg;
+
+ IDF3_COMP_OUTLINE* outline; // component outline to use
+ IDF3_COMPONENT* parent; // associated component
+
+#ifndef DISABLE_IDF_OWNERSHIP
+ bool checkOwnership( int aSourceLine, const char* aSourceFunc );
+#endif
+
+ /**
+ * Function readPlaceData
+ * reads placement data from an open IDFv3 file
+ *
+ * @param aBoardFile is the open IDFv3 file
+ * @param aBoardState is the internal status flag of the IDF parser
+ * @param aIdfVersion is the version of the file currently being parsed
+ * @param aBoard is the IDF3_BOARD object which will store the data
+ *
+ * @return bool: true if placement data was successfully read. false if
+ * no placement data was read; this may happen if the end of the placement
+ * data was encountered or an error occurred. if an error occurred then
+ * an exception is thrown.
+ */
+ bool readPlaceData( std::ifstream &aBoardFile, IDF3::FILE_STATE& aBoardState,
+ IDF3_BOARD *aBoard, IDF3::IDF_VERSION aIdfVersion,
+ bool aNoSubstituteOutlines );
+
+ /**
+ * Function writePlaceData
+ * writes RECORD 2 and RECORD 3 of a PLACEMENT section as per IDFv3 specification
+ *
+ * @param aBoardFile is the open IDFv3 file
+ * @param aXpos is the X location of the parent component
+ * @param aYpos is the Y location of the parent component
+ * @param aAngle is the rotation of the parent component
+ * @param aRefDes is the reference designator of the parent component
+ * @param aPlacement is the IDF Placement Status of the parent component
+ * @param aSide is the IDF Layer Designator (TOP or BOTTOM)
+ *
+ * @return bool: true if data was successfully written, otherwise false
+ */
+ void writePlaceData( std::ofstream& aBoardFile, double aXpos, double aYpos, double aAngle,
+ const std::string aRefDes, IDF3::IDF_PLACEMENT aPlacement,
+ IDF3::IDF_LAYER aSide );
+
+public:
+ /**
+ * Constructor
+ * creates an object with default settings and no parent or associated outline
+ */
+ IDF3_COMP_OUTLINE_DATA();
+
+ /**
+ * Constructor
+ * creates an object with default settings and the specified parent and associated outline
+ *
+ * @param aParent is the owning IDF3_COMPONENT object
+ * @param aOutline is the outline for this placed component
+ */
+ IDF3_COMP_OUTLINE_DATA( IDF3_COMPONENT* aParent, IDF3_COMP_OUTLINE* aOutline );
+
+ /**
+ * Constructor
+ * creates an object the specified parent and associated outline and the specified
+ * data.
+ *
+ * @param aParent is the owning IDF3_COMPONENT object
+ * @param aOutline is the outline for this placed component
+ * @param aXoff is the X offset of this outline in relation to its parent
+ * @param aYoff is the Y offset of this outline in relation to its parent
+ * @param aZoff is the board offset of this outline as per IDFv3 specification
+ * @param aAoff is the rotational offset of this outline in relation to its parent
+ */
+ IDF3_COMP_OUTLINE_DATA( IDF3_COMPONENT* aParent, IDF3_COMP_OUTLINE* aOutline,
+ double aXoff, double aYoff, double aZoff, double aAngleOff );
+
+ ~IDF3_COMP_OUTLINE_DATA();
+
+ /**
+ * Function SetOffsets
+ * sets the position and orientation of this outline item in relation to its parent
+ *
+ * @param aXoff is the X offset of this outline in relation to its parent
+ * @param aYoff is the Y offset of this outline in relation to its parent
+ * @param aZoff is the board offset of this outline as per IDFv3 specification
+ * @param aAoff is the rotational offset of this outline in relation to its parent
+ *
+ * @return bool: true if the operation succeeded, false if an ownership
+ * violation occurred
+ */
+ bool SetOffsets( double aXoff, double aYoff, double aZoff, double aAngleOff );
+
+ /**
+ * Function GetOffsets
+ * retrieves the position and orientation of this outline item in relation to its parent
+ *
+ * @param aXoff is the X offset of this outline in relation to its parent
+ * @param aYoff is the Y offset of this outline in relation to its parent
+ * @param aZoff is the board offset of this outline as per IDFv3 specification
+ * @param aAoff is the rotational offset of this outline in relation to its parent
+ */
+ void GetOffsets( double& aXoff, double& aYoff, double& aZoff, double& aAngleOff );
+
+ /**
+ * Function SetParent
+ * sets the parent object
+ *
+ * @param aParent is the owning IDF3_COMPONENT object
+ */
+ void SetParent( IDF3_COMPONENT* aParent );
+
+ /**
+ * Function SetOutline
+ * sets the outline whose position is managed by this object
+ *
+ * @param aOutline is the outline for this component
+ *
+ * @return bool: true if the operation succeeded, false if an ownership
+ * violation occurred
+ */
+ bool SetOutline( IDF3_COMP_OUTLINE* aOutline );
+
+ /**
+ * Function GetOutline
+ * retrieves the outline whose position is managed by this object
+ *
+ * @return IDF3_COMP_OUTLINE*: the outline for this component
+ */
+ IDF3_COMP_OUTLINE* GetOutline( void )
+ {
+ return outline;
+ }
+
+ const std::string& GetError( void )
+ {
+ return errormsg;
+ }
+};
+
+
+class IDF3_COMPONENT
+{
+friend class IDF3_BOARD;
+private:
+ std::list< IDF3_COMP_OUTLINE_DATA* > components;
+ std::list< IDF_DRILL_DATA* > drills;
+
+ double xpos;
+ double ypos;
+ double angle;
+ IDF3::IDF_PLACEMENT placement;
+ IDF3::IDF_LAYER layer; // [TOP/BOTTOM ONLY as per IDF spec]
+ bool hasPosition; ///< True after SetPosition is called once
+ std::string refdes; ///< Reference Description (MUST BE UNIQUE)
+ IDF3_BOARD* parent;
+ std::string errormsg;
+
+ /**
+ * Function WriteDrillData
+ * writes the internal drill data to an IDFv3 .DRILLED_HOLES section
+ *
+ * @param aBoardFile is an IDFv3 file opened for writing
+ *
+ * @return bool: true if the operation succeeded, otherwise false
+ */
+ bool writeDrillData( std::ofstream& aBoardFile );
+
+ /**
+ * Function WritePlaceData
+ * writes the component placement data to an IDFv3 .PLACEMENT section
+ *
+ * @param aBoardFile is an IDFv3 file opened for writing
+ *
+ * @return bool: true if the operation succeeded, otherwise false
+ */
+ bool writePlaceData( std::ofstream& aBoardFile );
+
+#ifndef DISABLE_IDF_OWNERSHIP
+ bool checkOwnership( int aSourceLine, const char* aSourceFunc );
+#endif
+
+public:
+ /**
+ * Constructor
+ * sets the parent object and initializes other internal parameters to default values
+ *
+ * @param aParent is the owning IDF3_BOARD object
+ */
+ IDF3_COMPONENT( IDF3_BOARD* aParent );
+
+ ~IDF3_COMPONENT();
+
+ /**
+ * Function SetParent
+ * sets the parent object
+ *
+ * @param aParent is the owning IDF3_BOARD object
+ */
+ void SetParent( IDF3_BOARD* aParent );
+
+ /**
+ * Function GetCadType
+ * returns the type of CAD (IDF3::CAD_ELEC, IDF3::CAD_MECH) which instantiated this object
+ *
+ * @return IDF3::CAD_TYPE
+ */
+ IDF3::CAD_TYPE GetCadType( void );
+
+ /**
+ * Function GetCadType
+ * returns the IDF UNIT type of the parent object or IDF3::UNIT_INVALID if
+ * the parent was not set
+ *
+ * @return IDF3::IDF_UNIT
+ */
+ IDF3::IDF_UNIT GetUnit( void );
+
+ /**
+ * Function SetRefDes
+ * sets the Reference Designator (RefDes) of this component; the RefDes is shared
+ * by all outlines associated with this component.
+ *
+ * @return bool: true if the RefDes was accepted, otherwise false. Prohibited
+ * values include empty strings and the word PANEL.
+ */
+ bool SetRefDes( const std::string& aRefDes );
+
+ /**
+ * Function GetRefDes
+ * Retrieves the Reference Designator (RefDes) of this component
+ *
+ * @return string: the Reference Designator
+ */
+ const std::string& GetRefDes( void );
+
+ /**
+ * Function AddDrill
+ * adds a drill entry to the component and returns its pointer
+ *
+ * @param aDia diameter of the drill (mm)
+ * @param aXpos X position of the drill (mm)
+ * @param aYpos Y position of the drill (mm)
+ * @param aPlating plating type (PTH, NPTH)
+ * @param aHoleType hole class (PIN, VIA, MTG, TOOL, etc)
+ * @param aOwner owning CAD system (ECAD, MCAD, UNOWNED)
+ *
+ * @return pointer: a pointer to the newly created drill entry or NULL
+ */
+ IDF_DRILL_DATA* AddDrill( double aDia, double aXpos, double aYpos,
+ IDF3::KEY_PLATING aPlating,
+ const std::string aHoleType,
+ IDF3::KEY_OWNER aOwner );
+
+ /**
+ * Function AddDrill
+ * adds the given drill entry to the component and returns the pointer
+ * to indicate success. A return value of NULL indicates that the item
+ * was not added and it is the user's responsibility to delete the
+ * object if necessary.
+ *
+ * @param aDrilledHole pointer to a drill entry
+ *
+ * @return pointer: aDrilledHole if the function succeeds, otherwise NULL
+ */
+ IDF_DRILL_DATA* AddDrill( IDF_DRILL_DATA* aDrilledHole );
+
+ /**
+ * Function DelDrill( double aDia, double aXpos, double aYpos )
+ * deletes a drill entry based on its size and location. This operation is
+ * subject to IDF ownership rules.
+ *
+ * @param aDia diameter (mm) of the drilled hole to be deleted
+ * @param aXpos X position (mm) of the hole to be deleted
+ * @param aYpos X position (mm) of the hole to be deleted
+ *
+ * @return bool: true if a drill was found and deleted, otherwise false.
+ * If an ownership violation occurs an exception is thrown.
+ */
+ bool DelDrill( double aDia, double aXpos, double aYpos );
+
+ /**
+ * Function DelDrill( IDF_DRILL_DATA* aDrill )
+ * deletes a drill entry based on pointer. This operation is
+ * subject to IDF ownership rules.
+ *
+ * @param aDrill the pointer associated with the drill entry to be deleted
+ *
+ * @return bool: true if a drill was found and deleted, otherwise false.
+ * If an ownership violation occurs an exception is thrown.
+ */
+ bool DelDrill( IDF_DRILL_DATA* aDrill );
+
+ /**
+ * Function GetDrills
+ * returns a pointer to the internal list of drills. To avoid IDF
+ * violations, the user should not alter these entries.
+ */
+ const std::list< IDF_DRILL_DATA* >*const GetDrills( void );
+
+ /**
+ * Function AddOutlineData
+ * adds the given component outline data to this component
+ *
+ * @param aComponentOutline is a pointer to the outline data to be added
+ *
+ * @return true if the operation succeedes, otherwise false
+ */
+ bool AddOutlineData( IDF3_COMP_OUTLINE_DATA* aComponentOutline );
+
+ /**
+ * Function DeleteOutlineData( IDF3_COMP_OUTLINE_DATA* aComponentOutline )
+ * removes outline data based on the pointer provided.
+ *
+ * @param aComponentOutline is a pointer to be deleted from the internal list
+ *
+ * @return bool: true if the data was found and deleted, otherwise false
+ */
+ bool DeleteOutlineData( IDF3_COMP_OUTLINE_DATA* aComponentOutline );
+
+ /**
+ * Function DeleteOutlineData( size_t aIndex )
+ * removes outline data based on the provided index.
+ *
+ * @param aIndex is an index to the internal outline list
+ *
+ * @return bool: true if the data was deleted, false if the
+ * index was out of bounds.
+ */
+ bool DeleteOutlineData( size_t aIndex );
+
+ /**
+ * Function GetOutlineSize
+ * returns the number of outlines in the internal list
+ */
+ size_t GetOutlinesSize( void );
+
+
+ /**
+ * Function GetOutlinesData
+ * returns a pointer to the internal list of outline data
+ */
+ const std::list< IDF3_COMP_OUTLINE_DATA* >*const GetOutlinesData( void );
+
+ /**
+ * Function GetPosition
+ * retrieves the internal position parameters and returns true if the
+ * position was previously set, otherwise false.
+ */
+ bool GetPosition( double& aXpos, double& aYpos, double& aAngle, IDF3::IDF_LAYER& aLayer );
+
+ // NOTE: it may be possible to extend this so that internal drills and outlines
+ // are moved when the component is moved. However there is always a danger of
+ // position creep due to the relative position updates.
+ /**
+ * Function SetPosition
+ * sets the internal position parameters and returns true if the
+ * position was set, false if the position was previously set. This object
+ * does not allow modification of the position once it is set since this may
+ * adversely affect the relationship with its internal objects.
+ *
+ * @param aXpos is the X position (mm) of the component
+ * @param aYpos is the Y position (mm) of the component
+ * @param aAngle is the rotation of the component (degrees)
+ * @param aLayer is the layer on which the component is places (TOP, BOTTOM)
+ *
+ * @return bool: true if the position was set, otherwise false
+ */
+ bool SetPosition( double aXpos, double aYpos, double aAngle, IDF3::IDF_LAYER aLayer );
+
+ /**
+ * Function GetPlacement
+ * returns the IDF placement value of this component (UNPLACED, PLACED, ECAD, MCAD)
+ */
+ IDF3::IDF_PLACEMENT GetPlacement( void );
+
+ /**
+ * Function SetPlacement
+ * sets the placement value of the component subject to ownership rules.
+ * An exception is thrown if aPlacementValue is invalid or an ownership
+ * violation occurs.
+ *
+ * @return bool: true if the operation succeeded, otherwise false and the
+ * error message is set.
+ */
+ bool SetPlacement( IDF3::IDF_PLACEMENT aPlacementValue );
+
+ const std::string& GetError( void )
+ {
+ return errormsg;
+ }
+};
+
+class IDF3_BOARD
+{
+private:
+ std::map< std::string, std::string > uidFileList; // map of files opened and UIDs
+ std::list< std::string > uidLibList; // list of UIDs read from a library file
+ std::string errormsg; // string for passing error messages to user
+ std::list< IDF_NOTE* > notes; // IDF notes
+ std::list< std::string > noteComments; // comment list for NOTES section
+ std::list< std::string > drillComments; // comment list for DRILL section
+ std::list< std::string > placeComments; // comment list for PLACEMENT section
+ std::list<IDF_DRILL_DATA*> board_drills;
+ std::map< std::string, IDF3_COMPONENT*> components; // drill and placement data for components
+ std::map< std::string, IDF3_COMP_OUTLINE*> compOutlines; // component outlines (data for library file)
+ std::string boardName;
+ IDF3::FILE_STATE state;
+ IDF3::CAD_TYPE cadType;
+ IDF3::IDF_UNIT unit;
+ IDF3::IDF_VERSION idfVer; // IDF version of Board or Library
+ int iRefDes; // counter for automatically numbered NOREFDES items
+ std::string sRefDes;
+
+ std::string idfSource; // SOURCE string to use when writing BOARD and LIBRARY headers
+ std::string brdSource; // SOURCE string as retrieved from a BOARD file
+ std::string libSource; // SOURCE string as retrieved from a LIBRARY file
+ std::string brdDate; // DATE string from BOARD file
+ std::string libDate; // DATE string from LIBRARY file
+ int brdFileVersion; // File Version from BOARD file
+ int libFileVersion; // File Version from LIBRARY file
+
+ int userPrec; // user may store any integer here
+ double userScale; // user may store a scale for translating to arbitrary units
+ double userXoff; // user may specify an arbitrary X/Y offset
+ double userYoff;
+
+ // main board outline and cutouts
+ BOARD_OUTLINE olnBoard;
+ // OTHER outlines
+ std::map<std::string, OTHER_OUTLINE*> olnOther;
+ // ROUTE outlines
+ std::list<ROUTE_OUTLINE*> olnRoute;
+ // PLACEMENT outlines
+ std::list<PLACE_OUTLINE*> olnPlace;
+ // ROUTE KEEPOUT outlines
+ std::list<ROUTE_KO_OUTLINE*> olnRouteKeepout;
+ // VIA KEEPOUT outlines
+ std::list<VIA_KO_OUTLINE*> olnViaKeepout;
+ // PLACE KEEPOUT outlines
+ std::list<PLACE_KO_OUTLINE*> olnPlaceKeepout;
+ // PLACEMENT GROUP outlines
+ std::multimap<std::string, GROUP_OUTLINE*> olnGroup;
+
+ // Set the unit; this can only be done internally upon
+ // reading a file or saving
+ bool setUnit( IDF3::IDF_UNIT aUnit, bool convert = false );
+
+ IDF_DRILL_DATA* addCompDrill( double aDia, double aXpos, double aYpos,
+ IDF3::KEY_PLATING aPlating,
+ const std::string aHoleType,
+ IDF3::KEY_OWNER aOwner,
+ const std::string& aRefDes );
+
+ IDF_DRILL_DATA* addCompDrill( IDF_DRILL_DATA* aDrilledHole );
+
+ bool delCompDrill( double aDia, double aXpos, double aYpos, std::string aRefDes );
+
+ // read the DRILLED HOLES section
+ void readBrdDrills( std::ifstream& aBoardFile, IDF3::FILE_STATE& aBoardState );
+ // read the NOTES section
+ void readBrdNotes( std::ifstream& aBoardFile, IDF3::FILE_STATE& aBoardState );
+ // read the component placement section
+ void readBrdPlacement( std::ifstream& aBoardFile, IDF3::FILE_STATE& aBoardState,
+ bool aNoSubstituteOutlines );
+ // read the board HEADER
+ void readBrdHeader( std::ifstream& aBoardFile, IDF3::FILE_STATE& aBoardState );
+ // read individual board sections; pay attention to IDFv3 section specifications
+ // exception thrown on unrecoverable errors. state flag set to FILE_PLACEMENT
+ // upon reading the PLACEMENT file; according to IDFv3 this is the final section
+ void readBrdSection( std::ifstream& aBoardFile, IDF3::FILE_STATE& aBoardState,
+ bool aNoSubstituteOutlines );
+ // read the board file data
+ void readBoardFile( const std::string& aFileName, bool aNoSubstituteOutlines );
+
+ // write the board file data
+ void writeBoardFile( const std::string& aFileName );
+
+ // read the library sections (outlines)
+ void readLibSection( std::ifstream& aLibFile, IDF3::FILE_STATE& aLibState, IDF3_BOARD* aBoard );
+ // read the library HEADER
+ void readLibHeader( std::ifstream& aLibFile, IDF3::FILE_STATE& aLibState );
+ // read the library file data
+ void readLibFile( const std::string& aFileName );
+
+ // write the library file data
+ bool writeLibFile( const std::string& aFileName );
+
+#ifndef DISABLE_IDF_OWNERSHIP
+ bool checkComponentOwnership( int aSourceLine, const char* aSourceFunc,
+ IDF3_COMPONENT* aComponent );
+#endif
+
+public:
+ IDF3_BOARD( IDF3::CAD_TYPE aCadType );
+ virtual ~IDF3_BOARD();
+
+ IDF3::CAD_TYPE GetCadType( void );
+
+ // retrieve the nominal unit used in reading/writing
+ // data. This is primarily for use by owned objects
+ // and is only of informational use for the end user.
+ // Internally all data is represented in mm and the
+ // end user must use only mm in the API.
+ IDF3::IDF_UNIT GetUnit( void );
+
+ const std::string& GetNewRefDes( void );
+
+ void SetBoardName( std::string aBoardName );
+ const std::string& GetBoardName( void );
+
+ bool SetBoardThickness( double aBoardThickness );
+ double GetBoardThickness( void );
+
+ bool ReadFile( const wxString& aFullFileName, bool aNoSubstituteOutlines = false );
+ bool WriteFile( const wxString& aFullFileName, bool aUnitMM = true, bool aForceUnitFlag = false );
+
+ const std::string& GetIDFSource( void );
+ void SetIDFSource( const std::string& aIDFSource);
+ const std::string& GetBoardSource( void );
+ const std::string& GetLibrarySource( void );
+ const std::string& GetBoardDate( void );
+ const std::string& GetLibraryDate( void );
+ int GetBoardVersion( void );
+ bool SetBoardVersion( int aVersion );
+ int GetLibraryVersion( void );
+ bool SetLibraryVersion( int aVersion );
+
+ double GetUserScale( void );
+ bool SetUserScale( double aScaleFactor );
+
+ int GetUserPrecision( void );
+ bool SetUserPrecision( int aPrecision );
+
+ void GetUserOffset( double& aXoff, double& aYoff );
+ void SetUserOffset( double aXoff, double aYoff );
+
+ bool AddBoardOutline( IDF_OUTLINE* aOutline );
+ bool DelBoardOutline( IDF_OUTLINE* aOutline );
+ bool DelBoardOutline( size_t aIndex );
+ size_t GetBoardOutlinesSize( void );
+ BOARD_OUTLINE* GetBoardOutline( void );
+ const std::list< IDF_OUTLINE* >*const GetBoardOutlines( void );
+
+ // Operations for OTHER OUTLINES
+ const std::map<std::string, OTHER_OUTLINE*>*const GetOtherOutlines( void );
+
+ /// XXX - TO BE IMPLEMENTED
+ //
+ // SetBoardOutlineOwner()
+ //
+ // AddDrillComment
+ // AddPlacementComment
+ // GetDrillComments()
+ // GetPlacementComments()
+ // ClearDrillComments()
+ // ClearPlacementComments()
+ // AddNoteComment
+ // GetNoteComments
+ // AddNote
+ //
+ // [IMPLEMENTED] const std::map<std::string, OTHER_OUTLINE*>*const GetOtherOutlines( void )
+ // size_t GetOtherOutlinesSize()
+ // OTHER_OUTLINE* AddOtherOutline( OTHER_OUTLINE* aOtherOutline )
+ // bool DelOtherOutline( int aIndex )
+ // bool DelOtherOutline( OTHER_OUTLINE* aOtherOutline )
+ //
+ // const std::list<ROUTE_OUTLINE*>*const GetRouteOutlines()
+ // size_t GetRouteOutlinesSize()
+ // ROUTE_OUTLINE* AddRouteOutline( ROUTE_OUTLINE* aRouteOutline )
+ // bool DelRouteOutline( int aIndex )
+ // bool DelRouteOutline( ROUTE_OUTLINE* aRouteOutline )
+ //
+ // const std::list<PLACE_OUTLINE*>*const GetPlacementOutlines()
+ // size_t GetPlacementOutlinesSize()
+ // PLACE_OUTLINE* AddPlacementOutline( PLACE_OUTLINE* aPlaceOutline )
+ // bool DelPlacementOutline( int aIndex )
+ // bool DelPlacementOutline( PLACE_OUTLINE* aPlaceOutline )
+ //
+ // const std::list<ROUTE_KO_OUTLINE*>*const GetRouteKeepOutOutlines()
+ // size_t GetRouteKeepOutOutlinesSize()
+ // ROUTE_KO_OUTLINE* AddRouteKeepoutOutline( ROUTE_KO_OUTLINE* aRouteKeepOut )
+ // bool DelRouteKeepOutOutline( int aIndex )
+ // bool DelRouteKeepOutOutline( ROUTE_KO_OUTLINE* aRouteKeepOut )
+ //
+ // const std::list<VIA_KO_OUTLINE*>*const GetViaKeepOutOutlines()
+ // size_t GetViaKeepOutOutlinesSize()
+ // VIA_KO_OUTLINE* AddViaKeepoutOutline( VIA_KO_OUTLINE* aViaKeepOut )
+ // bool DelViaKeepOutOutline( int aIndex )
+ // bool DelViaKeepOutOutline( VIA_KO_OUTLINE* aViaKeepOut )
+ //
+ // const std::list<PLACE_KO_OUTLINE*>*const GetPlacementKeepOutOutlines()
+ // size_t GetPlacementKeepOutOutlinesSize()
+ // PLACE_KO_OUTLINE* AddPlacementKeepoutOutline( PLACE_KO_OUTLINE* aPlaceKeepOut )
+ // bool DelPlacementKeepOutOutline( int aIndex )
+ // bool DelPlacementKeepOutOutline( PLACE_KO_OUTLINE* aPlaceKeepOut )
+ //
+ // const std::multimap<std::string, GROUP_OUTLINE*>*const GetGroupOutlines()
+ // size_t GetGroupOutlinesSize()
+ // GROUP_OUTLINE* AddGroupOutline( GROUP_OUTLINE* aGroupOutline )
+ // bool DelGroupOutline( int aIndex )
+ // bool DelGroupOutline( GROUP_OUTLINE* aGroupOutline )
+
+ std::list<IDF_DRILL_DATA*>& GetBoardDrills( void )
+ {
+ return board_drills;
+ }
+
+ IDF_DRILL_DATA* AddBoardDrill( double aDia, double aXpos, double aYpos,
+ IDF3::KEY_PLATING aPlating,
+ const std::string aHoleType,
+ IDF3::KEY_OWNER aOwner );
+
+ IDF_DRILL_DATA* AddDrill( IDF_DRILL_DATA* aDrilledHole );
+
+ bool DelBoardDrill( double aDia, double aXpos, double aYpos );
+
+ // a slot is a deficient representation of a kicad slotted hole;
+ // it is usually associated with a component but IDFv3 does not
+ // provide for such an association.
+ bool AddSlot( double aWidth, double aLength, double aOrientation, double aX, double aY );
+
+ bool AddComponent( IDF3_COMPONENT* aComponent );
+ bool DelComponent( IDF3_COMPONENT* aComponent );
+ bool DelComponent( size_t aIndex );
+ size_t GetComponentsSize( void );
+ std::map< std::string, IDF3_COMPONENT* >*const GetComponents( void );
+ IDF3_COMPONENT* FindComponent( std::string aRefDes );
+
+ // returns a pointer to a component outline object or NULL
+ // if the object doesn't exist
+ IDF3_COMP_OUTLINE* GetComponentOutline( wxString aFullFileName );
+
+ // returns a pointer to the component outline object with the
+ // unique ID aComponentID
+ IDF3_COMP_OUTLINE* GetComponentOutline( std::string aComponentID );
+
+ // returns a pointer to the outline "NOGEOM NOPART" which is substituted
+ // whenever a true outline cannot be found or is defective
+ IDF3_COMP_OUTLINE* GetInvalidOutline( const std::string& aGeomName, const std::string& aPartName );
+
+ // clears all data
+ void Clear( void );
+
+ // return error string
+ const std::string& GetError( void )
+ {
+ return errormsg;
+ }
+};
+
+#endif // IDF_PARSER_H
diff --git a/utils/idftools/idf_rect.cpp b/utils/idftools/idf_rect.cpp
new file mode 100644
index 0000000..17b145b
--- /dev/null
+++ b/utils/idftools/idf_rect.cpp
@@ -0,0 +1,433 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2014 Cirilo Bernardo
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <iostream>
+#include <fstream>
+#include <string>
+#include <sstream>
+#include <cmath>
+#include <cstdio>
+#include <list>
+#include <utility>
+#include <clocale>
+
+using namespace std;
+
+void writeLeaded( FILE* fp, double width, double length, double height,
+ double wireDia, double pitch, bool inch );
+
+void writeLeadless( FILE* fp, double width, double length,
+ double height, double chamfer, bool inch );
+
+int main( int argc, char **argv )
+{
+ // IDF implicitly requires the C locale
+ setlocale( LC_ALL, "C" );
+
+ if( argc == 1 )
+ {
+ cout << "idfrect: This program generates an outline for a rectangular component.\n";
+ cout << " The component may have a single lead (axial) or a chamfer on the\n";
+ cout << " upper left corner.\n";
+ cout << "Input:\n";
+ cout << " Unit: mm, in (millimeters or inches)\n";
+ cout << " Width:\n";
+ cout << " Length:\n";
+ cout << " Height:\n";
+ cout << " Chamfer: length of the 45 deg. chamfer\n";
+ cout << " * Leaded: Y,N (lead is always to the right)\n";
+ cout << " ** Wire diameter\n";
+ cout << " ** Pitch\n";
+ cout << " File name (must end in *.idf)\n\n";
+ cout << " NOTES:\n";
+ cout << " * only required if chamfer = 0\n\n";
+ cout << " ** only required for leaded components\n\n";
+ }
+
+ bool inch = false; // default mm
+ double width = 0.0;
+ double length = 0.0;
+ double height = 0.0;
+ double wireDia = 0.0;
+ double pitch = 0.0;
+ double chamfer = 0.0;
+ bool leaded = false;
+ bool ok = false;
+
+ stringstream tstr;
+ string line;
+
+ line.clear();
+ while( line.compare( "mm" ) && line.compare( "in" ) )
+ {
+ cout << "* Units (mm,in): ";
+ line.clear();
+ std::getline( cin, line );
+ }
+
+ if( line.compare( "mm" ) )
+ inch = true;
+
+ ok = false;
+ while( !ok )
+ {
+ cout << "* Width: ";
+
+ line.clear();
+ std::getline( cin, line );
+
+ tstr.clear();
+ tstr.str( line );
+
+ tstr >> width;
+ if( !tstr.fail() && width >= 0.001 )
+ ok = true;
+ }
+
+ ok = false;
+ while( !ok )
+ {
+ cout << "* Length: ";
+
+ line.clear();
+ std::getline( cin, line );
+
+ tstr.clear();
+ tstr.str( line );
+
+ tstr >> length;
+ if( !tstr.fail() && length > 0.0 )
+ ok = true;
+ }
+
+ ok = false;
+ while( !ok )
+ {
+ cout << "* Height: ";
+
+ line.clear();
+ std::getline( cin, line );
+
+ tstr.clear();
+ tstr.str( line );
+
+ tstr >> height;
+ if( !tstr.fail() && height >= 0.001 )
+ ok = true;
+ }
+
+ ok = false;
+ while( !ok )
+ {
+ cout << "* Chamfer (0 for none): ";
+
+ line.clear();
+ std::getline( cin, line );
+
+ tstr.clear();
+ tstr.str( line );
+
+ tstr >> chamfer;
+ if( !tstr.fail() && chamfer >= 0.0 )
+ {
+ if( chamfer > width / 3.0 || chamfer > length / 3.0 )
+ cout << "* WARNING: chamfer must be <= MIN( width, length )/3\n";
+ else
+ ok = true;
+ }
+ }
+
+ if( chamfer < 1e-6 )
+ {
+ ok = false;
+ while( !ok )
+ {
+ cout << "* Leaded: Y, N: ";
+
+ line.clear();
+ std::getline( cin, line );
+
+ if( !line.compare( "Y" ) || !line.compare( "y" ) )
+ {
+ leaded = true;
+ ok = true;
+ }
+ else if( !line.compare( "N" ) || !line.compare( "n" ) )
+ {
+ leaded = false;
+ ok = true;
+ }
+ }
+ }
+
+ ok = false;
+ while( leaded && !ok )
+ {
+ cout << "* Wire dia.: ";
+
+ line.clear();
+ std::getline( cin, line );
+
+ tstr.clear();
+ tstr.str( line );
+
+ tstr >> wireDia;
+ if( !tstr.fail() && wireDia >= 0.001 )
+ {
+ if( wireDia >= length )
+ cout << "* WARNING: wire diameter must be < length\n";
+ else
+ ok = true;
+ }
+ }
+
+ ok = false;
+ while( leaded && !ok )
+ {
+ cout << "* Pitch: ";
+
+ line.clear();
+ std::getline( cin, line );
+
+ tstr.clear();
+ tstr.str( line );
+
+ tstr >> pitch;
+ if( !tstr.fail() && pitch >= 0.001 )
+ {
+ if( pitch <= ( length + wireDia ) / 2.0 )
+ cout << "* WARNING: pitch must be > (length + wireDia)/2\n";
+ else
+ ok = true;
+ }
+ }
+
+ line.clear();
+ while( line.empty() || line.find( ".idf" ) == string::npos )
+ {
+ cout << "* File name (*.idf): ";
+
+ line.clear();
+ std::getline( cin, line );
+ }
+
+ FILE* fp = fopen( line.c_str(), "w" );
+
+ if( !fp )
+ {
+ cerr << "Could not open output file: " << line << "\n";
+ }
+ else
+ {
+ fprintf( fp, "# rectangular outline%s\n", leaded ? ", leaded" : "" );
+ fprintf( fp, "# file: \"%s\"\n", line.c_str() );
+
+ if( inch )
+ {
+ width *= 1000.0;
+ length *= 1000.0;
+ height *= 1000.0;
+ wireDia *= 1000.0;
+ pitch *= 1000.0;
+ chamfer *= 1000.0;
+
+ fprintf( fp, "# width: %d THOU\n", (int) width );
+ fprintf( fp, "# length: %d THOU\n", (int) length );
+ fprintf( fp, "# height: %d THOU\n", (int) height );
+
+ if( leaded )
+ {
+ fprintf( fp, "# wire dia: %d THOU\n", (int) wireDia );
+ fprintf( fp, "# pitch: %d THOU\n", (int) pitch );
+ }
+ else
+ {
+ fprintf( fp, "# chamfer: %d THOU\n", (int) chamfer );
+ }
+
+ fprintf( fp, ".ELECTRICAL\n" );
+ fprintf( fp, "\"RECT%sIN\" \"W%d_L%d_H%d", leaded ? "L" : "",
+ (int) width, (int) length, (int) height );
+
+ if( leaded )
+ fprintf( fp, "_D%d_P%d\" ", (int) wireDia, (int) pitch );
+ else
+ fprintf( fp, "_C%d\" ", (int) chamfer );
+
+ fprintf( fp, "THOU %d\n", (int) height );
+ }
+ else
+ {
+ fprintf( fp, "# width: %.3f mm\n", width );
+ fprintf( fp, "# length: %.3f mm\n", length );
+ fprintf( fp, "# height: %.3f mm\n", height );
+
+ if( leaded )
+ {
+ fprintf( fp, "# wire dia: %.3f mm\n", wireDia );
+ fprintf( fp, "# pitch: %.3f mm\n", pitch );
+ }
+ else
+ {
+ fprintf( fp, "# chamfer: %.3f mm\n", chamfer );
+ }
+
+ fprintf( fp, ".ELECTRICAL\n" );
+ fprintf( fp, "\"RECT%sMM\" \"W%.3f_L%.3f_H%.3f_", leaded ? "L" : "",
+ width, length, height );
+
+ if( leaded )
+ fprintf( fp, "D%.3f_P%.3f\" ", wireDia, pitch );
+ else
+ fprintf( fp, "C%.3f\" ", chamfer );
+
+ fprintf( fp, "MM %.3f\n", height );
+ }
+
+ if( leaded )
+ writeLeaded( fp, width, length, height, wireDia, pitch, inch );
+ else
+ writeLeadless( fp, width, length, height, chamfer, inch );
+
+ fprintf( fp, ".END_ELECTRICAL\n" );
+ fclose( fp );
+ }
+
+ setlocale( LC_ALL, "" );
+ return 0;
+}
+
+
+void writeLeaded( FILE* fp, double width, double length,
+ double height, double wireDia, double pitch, bool inch )
+{
+ if( inch )
+ {
+ int x1, x2, x3;
+ int y1, y2;
+
+ x1 = pitch / 2.0;
+ x2 = width / 2.0 - x1;
+ x3 = x2 - width;
+
+ y1 = wireDia / 2.0;
+ y2 = length / 2.0;
+
+ fprintf( fp, "0 %d %d 0\n", x1, y1 );
+ fprintf( fp, "0 %d %d 0\n", x2, y1 );
+ fprintf( fp, "0 %d %d 0\n", x2, y2 );
+ fprintf( fp, "0 %d %d 0\n", x3, y2 );
+ fprintf( fp, "0 %d %d 0\n", x3, -y2 );
+ fprintf( fp, "0 %d %d 0\n", x2, -y2 );
+ fprintf( fp, "0 %d %d 0\n", x2, -y1 );
+ fprintf( fp, "0 %d %d 0\n", x1, -y1 );
+ fprintf( fp, "0 %d %d 180\n", x1, y1 );
+ }
+ else
+ {
+ double x1, x2, x3;
+ double y1, y2;
+
+ x1 = pitch / 2.0;
+ x2 = width / 2.0 - x1;
+ x3 = x2 - width;
+
+ y1 = wireDia / 2.0;
+ y2 = length / 2.0;
+
+ fprintf( fp, "0 %.3f %.3f 0\n", x1, y1 );
+ fprintf( fp, "0 %.3f %.3f 0\n", x2, y1 );
+ fprintf( fp, "0 %.3f %.3f 0\n", x2, y2 );
+ fprintf( fp, "0 %.3f %.3f 0\n", x3, y2 );
+ fprintf( fp, "0 %.3f %.3f 0\n", x3, -y2 );
+ fprintf( fp, "0 %.3f %.3f 0\n", x2, -y2 );
+ fprintf( fp, "0 %.3f %.3f 0\n", x2, -y1 );
+ fprintf( fp, "0 %.3f %.3f 0\n", x1, -y1 );
+ fprintf( fp, "0 %.3f %.3f 180\n", x1, y1 );
+ }
+
+ return;
+}
+
+void writeLeadless( FILE* fp, double width, double length,
+ double height, double chamfer, bool inch )
+{
+ if( chamfer < 0.001 )
+ {
+ if( inch )
+ {
+ int x = width / 2.0;
+ int y = length / 2.0;
+
+ fprintf( fp, "0 %d %d 0\n", x, y );
+ fprintf( fp, "0 %d %d 0\n", -x, y );
+ fprintf( fp, "0 %d %d 0\n", -x, -y );
+ fprintf( fp, "0 %d %d 0\n", x, -y );
+ fprintf( fp, "0 %d %d 0\n", x, y );
+ }
+ else
+ {
+ double x = width / 2.0;
+ double y = length / 2.0;
+
+ fprintf( fp, "0 %.3f %.3f 0\n", x, y );
+ fprintf( fp, "0 %.3f %.3f 0\n", -x, y );
+ fprintf( fp, "0 %.3f %.3f 0\n", -x, -y );
+ fprintf( fp, "0 %.3f %.3f 0\n", x, -y );
+ fprintf( fp, "0 %.3f %.3f 0\n", x, y );
+ }
+
+ return;
+ }
+
+ if( inch )
+ {
+ int x = width / 2.0;
+ int y = length / 2.0;
+ int x1 = x - chamfer;
+ int y1 = y - chamfer;
+
+ fprintf( fp, "0 %d %d 0\n", x, y );
+ fprintf( fp, "0 %d %d 0\n", -x1, y );
+ fprintf( fp, "0 %d %d 0\n", -x, y1 );
+ fprintf( fp, "0 %d %d 0\n", -x, -y );
+ fprintf( fp, "0 %d %d 0\n", x, -y );
+ fprintf( fp, "0 %d %d 0\n", x, y );
+ }
+ else
+ {
+ double x = width / 2.0;
+ double y = length / 2.0;
+ double x1 = x - chamfer;
+ double y1 = y - chamfer;
+
+ fprintf( fp, "0 %.3f %.3f 0\n", x, y );
+ fprintf( fp, "0 %.3f %.3f 0\n", -x1, y );
+ fprintf( fp, "0 %.3f %.3f 0\n", -x, y1 );
+ fprintf( fp, "0 %.3f %.3f 0\n", -x, -y );
+ fprintf( fp, "0 %.3f %.3f 0\n", x, -y );
+ fprintf( fp, "0 %.3f %.3f 0\n", x, y );
+ }
+
+ return;
+}
diff --git a/utils/idftools/vrml_layer.cpp b/utils/idftools/vrml_layer.cpp
new file mode 100644
index 0000000..5974371
--- /dev/null
+++ b/utils/idftools/vrml_layer.cpp
@@ -0,0 +1,1788 @@
+/*
+ * file: vrml_layer.cpp
+ *
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2013 Cirilo Bernardo
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+// Wishlist:
+// 1. crop anything outside the board outline on PTH, silk, and copper layers
+// 2. on the PTH layer, handle cropped holes differently from others;
+// these are assumed to be castellated edges and the profile is not
+// a closed loop as assumed for all other outlines.
+// 3. a scheme is needed to tell a castellated edge from a plain board edge
+
+
+#include <sstream>
+#include <string>
+#include <iomanip>
+#include <cmath>
+#include <vrml_layer.h>
+
+#ifndef CALLBACK
+#define CALLBACK
+#endif
+
+#define GLCALLBACK(x) (( void (CALLBACK*)() )&(x))
+
+// minimum sides to a circle
+#define MIN_NSIDES 6
+
+static void FormatDoublet( double x, double y, int precision, std::string& strx, std::string& stry )
+{
+ std::ostringstream ostr;
+
+ ostr << std::fixed << std::setprecision( precision );
+
+ ostr << x;
+ strx = ostr.str();
+
+ ostr.str( "" );
+ ostr << y;
+ stry = ostr.str();
+
+ while( *strx.rbegin() == '0' )
+ strx.erase( strx.size() - 1 );
+
+ while( *stry.rbegin() == '0' )
+ stry.erase( stry.size() - 1 );
+}
+
+
+static void FormatSinglet( double x, int precision, std::string& strx )
+{
+ std::ostringstream ostr;
+
+ ostr << std::fixed << std::setprecision( precision );
+
+ ostr << x;
+ strx = ostr.str();
+
+ while( *strx.rbegin() == '0' )
+ strx.erase( strx.size() - 1 );
+}
+
+
+int VRML_LAYER::calcNSides( double aRadius, double aAngle )
+{
+ // check #segments on ends of arc
+ int maxSeg = maxArcSeg * aAngle / M_PI;
+
+ if( maxSeg < 3 )
+ maxSeg = 3;
+
+ int csides = aRadius * M_PI / minSegLength;
+
+ if( csides < 0 )
+ csides = -csides;
+
+ if( csides > maxSeg )
+ {
+ if( csides < 2 * maxSeg )
+ csides /= 2;
+ else
+ csides = (((double) csides) * minSegLength / maxSegLength );
+ }
+
+ if( csides < 3 )
+ csides = 3;
+
+ if( (csides & 1) == 0 )
+ csides += 1;
+
+ return csides;
+}
+
+
+static void CALLBACK vrml_tess_begin( GLenum cmd, void* user_data )
+{
+ VRML_LAYER* lp = (VRML_LAYER*) user_data;
+
+ lp->glStart( cmd );
+}
+
+
+static void CALLBACK vrml_tess_end( void* user_data )
+{
+ VRML_LAYER* lp = (VRML_LAYER*) user_data;
+
+ lp->glEnd();
+}
+
+
+static void CALLBACK vrml_tess_vertex( void* vertex_data, void* user_data )
+{
+ VRML_LAYER* lp = (VRML_LAYER*) user_data;
+
+ lp->glPushVertex( (VERTEX_3D*) vertex_data );
+}
+
+
+static void CALLBACK vrml_tess_err( GLenum errorID, void* user_data )
+{
+ VRML_LAYER* lp = (VRML_LAYER*) user_data;
+
+ lp->Fault = true;
+ lp->SetGLError( errorID );
+}
+
+
+static void CALLBACK vrml_tess_combine( GLdouble coords[3], VERTEX_3D* vertex_data[4],
+ GLfloat weight[4], void** outData, void* user_data )
+{
+ VRML_LAYER* lp = (VRML_LAYER*) user_data;
+
+ // the plating is set to true only if all are plated
+ bool plated = vertex_data[0]->pth;
+
+ if( !vertex_data[1]->pth )
+ plated = false;
+
+ if( vertex_data[2] && !vertex_data[2]->pth )
+ plated = false;
+
+ if( vertex_data[3] && !vertex_data[3]->pth )
+ plated = false;
+
+ *outData = lp->AddExtraVertex( coords[0], coords[1], plated );
+}
+
+
+VRML_LAYER::VRML_LAYER()
+{
+ // arc parameters suitable to mm measurements
+ maxArcSeg = 48;
+ minSegLength = 0.1;
+ maxSegLength = 0.5;
+ offsetX = 0.0;
+ offsetY = 0.0;
+
+ fix = false;
+ Fault = false;
+ idx = 0;
+ hidx = 0;
+ eidx = 0;
+ ord = 0;
+ glcmd = 0;
+ pholes = NULL;
+
+ tess = gluNewTess();
+
+ if( !tess )
+ return;
+
+ // set up the tesselator callbacks
+ gluTessCallback( tess, GLU_TESS_BEGIN_DATA, GLCALLBACK( vrml_tess_begin ) );
+
+ gluTessCallback( tess, GLU_TESS_VERTEX_DATA, GLCALLBACK( vrml_tess_vertex ) );
+
+ gluTessCallback( tess, GLU_TESS_END_DATA, GLCALLBACK( vrml_tess_end ) );
+
+ gluTessCallback( tess, GLU_TESS_ERROR_DATA, GLCALLBACK( vrml_tess_err ) );
+
+ gluTessCallback( tess, GLU_TESS_COMBINE_DATA, GLCALLBACK( vrml_tess_combine ) );
+
+ gluTessProperty( tess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_POSITIVE );
+
+ gluTessNormal( tess, 0, 0, 1 );
+}
+
+
+VRML_LAYER::~VRML_LAYER()
+{
+ Clear();
+
+ if( tess )
+ {
+ gluDeleteTess( tess );
+ tess = NULL;
+ }
+}
+
+
+void VRML_LAYER::GetArcParams( int& aMaxSeg, double& aMinLength, double& aMaxLength )
+{
+ aMaxSeg = maxArcSeg;
+ aMinLength = minSegLength;
+ aMaxLength = maxSegLength;
+}
+
+bool VRML_LAYER::SetArcParams( int aMaxSeg, double aMinLength, double aMaxLength )
+{
+ if( aMaxSeg < 8 )
+ aMaxSeg = 8;
+
+ if( aMinLength <= 0 || aMaxLength <= aMinLength )
+ return false;
+
+ maxArcSeg = aMaxSeg;
+ minSegLength = aMinLength;
+ maxSegLength = aMaxLength;
+ return true;
+}
+
+
+// clear all data
+void VRML_LAYER::Clear( void )
+{
+ int i;
+
+ fix = false;
+ idx = 0;
+
+ for( i = contours.size(); i > 0; --i )
+ {
+ delete contours.back();
+ contours.pop_back();
+ }
+
+ pth.clear();
+
+ areas.clear();
+
+ for( i = vertices.size(); i > 0; --i )
+ {
+ delete vertices.back();
+ vertices.pop_back();
+ }
+
+ clearTmp();
+}
+
+
+// clear ephemeral data in between invocations of the tesselation routine
+void VRML_LAYER::clearTmp( void )
+{
+ unsigned int i;
+
+ Fault = false;
+ hidx = 0;
+ eidx = 0;
+ ord = 0;
+ glcmd = 0;
+
+ triplets.clear();
+ solid.clear();
+
+ for( i = outline.size(); i > 0; --i )
+ {
+ delete outline.back();
+ outline.pop_back();
+ }
+
+ ordmap.clear();
+
+ for( i = extra_verts.size(); i > 0; --i )
+ {
+ delete extra_verts.back();
+ extra_verts.pop_back();
+ }
+
+ // note: unlike outline and extra_verts,
+ // vlist is not responsible for memory management
+ vlist.clear();
+
+ // go through the vertex list and reset ephemeral parameters
+ for( i = 0; i < vertices.size(); ++i )
+ {
+ vertices[i]->o = -1;
+ }
+}
+
+
+// create a new contour to be populated; returns an index
+// into the contour list or -1 if there are problems
+int VRML_LAYER::NewContour( bool aPlatedHole )
+{
+ if( fix )
+ return -1;
+
+ std::list<int>* contour = new std::list<int>;
+
+ if( !contour )
+ return -1;
+
+ contours.push_back( contour );
+ areas.push_back( 0.0 );
+
+ pth.push_back( aPlatedHole );
+
+ return contours.size() - 1;
+}
+
+
+// adds a vertex to the existing list and places its index in
+// an existing contour; returns true if OK,
+// false otherwise (indexed contour does not exist)
+bool VRML_LAYER::AddVertex( int aContourID, double aXpos, double aYpos )
+{
+ if( fix )
+ {
+ error = "AddVertex(): no more vertices may be added (Tesselate was previously executed)";
+ return false;
+ }
+
+ if( aContourID < 0 || (unsigned int) aContourID >= contours.size() )
+ {
+ error = "AddVertex(): aContour is not within a valid range";
+ return false;
+ }
+
+ VERTEX_3D* vertex = new VERTEX_3D;
+
+ if( !vertex )
+ {
+ error = "AddVertex(): a new vertex could not be allocated";
+ return false;
+ }
+
+ vertex->x = aXpos;
+ vertex->y = aYpos;
+ vertex->i = idx++;
+ vertex->o = -1;
+ vertex->pth = pth[ aContourID ];
+
+ VERTEX_3D* v2 = NULL;
+
+ if( contours[aContourID]->size() > 0 )
+ v2 = vertices[ contours[aContourID]->back() ];
+
+ vertices.push_back( vertex );
+ contours[aContourID]->push_back( vertex->i );
+
+ if( v2 )
+ areas[aContourID] += ( aXpos - v2->x ) * ( aYpos + v2->y );
+
+ return true;
+}
+
+
+// ensure the winding of a contour with respect to the normal (0, 0, 1);
+// set 'hole' to true to ensure a hole (clockwise winding)
+bool VRML_LAYER::EnsureWinding( int aContourID, bool aHoleFlag )
+{
+ if( aContourID < 0 || (unsigned int) aContourID >= contours.size() )
+ {
+ error = "EnsureWinding(): aContour is outside the valid range";
+ return false;
+ }
+
+ std::list<int>* cp = contours[aContourID];
+
+ if( cp->size() < 3 )
+ {
+ error = "EnsureWinding(): there are fewer than 3 vertices";
+ return false;
+ }
+
+ double dir = areas[aContourID];
+
+ VERTEX_3D* vp0 = vertices[ cp->back() ];
+ VERTEX_3D* vp1 = vertices[ cp->front() ];
+
+ dir += ( vp1->x - vp0->x ) * ( vp1->y + vp0->y );
+
+ // if dir is positive, winding is CW
+ if( ( aHoleFlag && dir < 0 ) || ( !aHoleFlag && dir > 0 ) )
+ {
+ cp->reverse();
+ areas[aContourID] = -areas[aContourID];
+ }
+
+ return true;
+}
+
+
+bool VRML_LAYER::AppendCircle( double aXpos, double aYpos,
+ double aRadius, int aContourID,
+ bool aHoleFlag )
+{
+ if( aContourID < 0 || (unsigned int) aContourID >= contours.size() )
+ {
+ error = "AppendCircle(): invalid contour (out of range)";
+ return false;
+ }
+
+ int nsides = M_PI * 2.0 * aRadius / minSegLength;
+
+ if( nsides > maxArcSeg )
+ {
+ if( nsides > 2 * maxArcSeg )
+ {
+ // use segments approx. maxAr
+ nsides = M_PI * 2.0 * aRadius / maxSegLength;
+ }
+ else
+ {
+ nsides /= 2;
+ }
+ }
+
+ if( nsides < MIN_NSIDES )
+ nsides = MIN_NSIDES;
+
+ // even numbers give prettier results for circles
+ if( nsides & 1 )
+ nsides += 1;
+
+ double da = M_PI * 2.0 / nsides;
+
+ bool fail = false;
+
+ if( aHoleFlag )
+ {
+ fail |= !AddVertex( aContourID, aXpos + aRadius, aYpos );
+
+ for( double angle = da; angle < M_PI * 2; angle += da )
+ fail |= !AddVertex( aContourID, aXpos + aRadius * cos( angle ),
+ aYpos - aRadius * sin( angle ) );
+ }
+ else
+ {
+ fail |= !AddVertex( aContourID, aXpos + aRadius, aYpos );
+
+ for( double angle = da; angle < M_PI * 2; angle += da )
+ fail |= !AddVertex( aContourID, aXpos + aRadius * cos( angle ),
+ aYpos + aRadius * sin( angle ) );
+ }
+
+ return !fail;
+}
+
+
+// adds a circle the existing list; if 'hole' is true the contour is
+// a hole. Returns true if OK.
+bool VRML_LAYER::AddCircle( double aXpos, double aYpos, double aRadius,
+ bool aHoleFlag, bool aPlatedHole )
+{
+ int pad;
+
+ if( aHoleFlag && aPlatedHole )
+ pad = NewContour( true );
+ else
+ pad = NewContour( false );
+
+ if( pad < 0 )
+ {
+ error = "AddCircle(): failed to add a contour";
+ return false;
+ }
+
+ return AppendCircle( aXpos, aYpos, aRadius, pad, aHoleFlag );
+}
+
+
+// adds a slotted pad with orientation given by angle; if 'hole' is true the
+// contour is a hole. Returns true if OK.
+bool VRML_LAYER::AddSlot( double aCenterX, double aCenterY,
+ double aSlotLength, double aSlotWidth,
+ double aAngle, bool aHoleFlag, bool aPlatedHole )
+{
+ aAngle *= M_PI / 180.0;
+
+ if( aSlotWidth > aSlotLength )
+ {
+ aAngle += M_PI2;
+ std::swap( aSlotLength, aSlotWidth );
+ }
+
+ aSlotWidth /= 2.0;
+ aSlotLength = aSlotLength / 2.0 - aSlotWidth;
+
+ int csides = calcNSides( aSlotWidth, M_PI );
+
+ double capx, capy;
+
+ capx = aCenterX + cos( aAngle ) * aSlotLength;
+ capy = aCenterY + sin( aAngle ) * aSlotLength;
+
+ double ang, da;
+ int i;
+ int pad;
+
+ if( aHoleFlag && aPlatedHole )
+ pad = NewContour( true );
+ else
+ pad = NewContour( false );
+
+ if( pad < 0 )
+ {
+ error = "AddCircle(): failed to add a contour";
+ return false;
+ }
+
+ da = M_PI / csides;
+ bool fail = false;
+
+ if( aHoleFlag )
+ {
+ for( ang = aAngle + M_PI2, i = 0; i < csides; ang -= da, ++i )
+ fail |= !AddVertex( pad, capx + aSlotWidth * cos( ang ),
+ capy + aSlotWidth * sin( ang ) );
+
+ ang = aAngle - M_PI2;
+ fail |= !AddVertex( pad, capx + aSlotWidth * cos( ang ),
+ capy + aSlotWidth * sin( ang ) );
+
+ capx = aCenterX - cos( aAngle ) * aSlotLength;
+ capy = aCenterY - sin( aAngle ) * aSlotLength;
+
+ for( ang = aAngle - M_PI2, i = 0; i < csides; ang -= da, ++i )
+ fail |= !AddVertex( pad, capx + aSlotWidth * cos( ang ),
+ capy + aSlotWidth * sin( ang ) );
+
+ ang = aAngle + M_PI2;
+ fail |= !AddVertex( pad, capx + aSlotWidth * cos( ang ),
+ capy + aSlotWidth * sin( ang ) );
+ }
+ else
+ {
+ for( ang = aAngle - M_PI2, i = 0; i < csides; ang += da, ++i )
+ fail |= !AddVertex( pad, capx + aSlotWidth * cos( ang ),
+ capy + aSlotWidth * sin( ang ) );
+
+ ang = aAngle + M_PI2;
+ fail |= !AddVertex( pad, capx + aSlotWidth * cos( ang ),
+ capy + aSlotWidth * sin( ang ) );
+
+ capx = aCenterX - cos( aAngle ) * aSlotLength;
+ capy = aCenterY - sin( aAngle ) * aSlotLength;
+
+ for( ang = aAngle + M_PI2, i = 0; i < csides; ang += da, ++i )
+ fail |= !AddVertex( pad, capx + aSlotWidth * cos( ang ),
+ capy + aSlotWidth * sin( ang ) );
+
+ ang = aAngle - M_PI2;
+ fail |= !AddVertex( pad, capx + aSlotWidth * cos( ang ),
+ capy + aSlotWidth * sin( ang ) );
+ }
+
+ return !fail;
+}
+
+
+// adds an arc to the given center, start point, pen width, and angle (degrees).
+bool VRML_LAYER::AppendArc( double aCenterX, double aCenterY, double aRadius,
+ double aStartAngle, double aAngle, int aContourID )
+{
+ if( aContourID < 0 || (unsigned int) aContourID >= contours.size() )
+ {
+ error = "AppendArc(): invalid contour (out of range)";
+ return false;
+ }
+
+ aAngle = aAngle / 180.0 * M_PI;
+ aStartAngle = aStartAngle / 180.0 * M_PI;
+
+ int nsides = calcNSides( aRadius, aAngle );
+
+ double da = aAngle / nsides;
+
+ bool fail = false;
+
+ if( aAngle > 0 )
+ {
+ aAngle += aStartAngle;
+ for( double ang = aStartAngle; ang < aAngle; ang += da )
+ fail |= !AddVertex( aContourID, aCenterX + aRadius * cos( ang ),
+ aCenterY + aRadius * sin( ang ) );
+ }
+ else
+ {
+ aAngle += aStartAngle;
+ for( double ang = aStartAngle; ang > aAngle; ang += da )
+ fail |= !AddVertex( aContourID, aCenterX + aRadius * cos( ang ),
+ aCenterY + aRadius * sin( ang ) );
+ }
+
+ return !fail;
+}
+
+
+// adds an arc with the given center, start point, pen width, and angle (degrees).
+bool VRML_LAYER::AddArc( double aCenterX, double aCenterY, double aStartX, double aStartY,
+ double aArcWidth, double aAngle, bool aHoleFlag, bool aPlatedHole )
+{
+ aAngle *= M_PI / 180.0;
+
+ // we don't accept small angles; in fact, 1 degree ( 0.01745 ) is already
+ // way too small but we must set a limit somewhere
+ if( aAngle < 0.01745 && aAngle > -0.01745 )
+ {
+ error = "AddArc(): angle is too small: abs( angle ) < 1 degree";
+ return false;
+ }
+
+ double rad = sqrt( (aStartX - aCenterX) * (aStartX - aCenterX)
+ + (aStartY - aCenterY) * (aStartY - aCenterY) );
+
+ aArcWidth /= 2.0; // this is the radius of the caps
+
+ // we will not accept an arc with an inner radius close to zero so we
+ // set a limit here. the end result will vary somewhat depending on
+ // the output units
+ if( aArcWidth >= ( rad * 1.01 ) )
+ {
+ error = "AddArc(): width/2 exceeds radius*1.01";
+ return false;
+ }
+
+ // calculate the radii of the outer and inner arcs
+ double orad = rad + aArcWidth;
+ double irad = rad - aArcWidth;
+
+ int osides = calcNSides( orad, aAngle );
+ int isides = calcNSides( irad, aAngle );
+ int csides = calcNSides( aArcWidth, M_PI );
+
+ double stAngle = atan2( aStartY - aCenterY, aStartX - aCenterX );
+ double endAngle = stAngle + aAngle;
+
+ // calculate ends of inner and outer arc
+ double oendx = aCenterX + orad* cos( endAngle );
+ double oendy = aCenterY + orad* sin( endAngle );
+ double ostx = aCenterX + orad* cos( stAngle );
+ double osty = aCenterY + orad* sin( stAngle );
+
+ double iendx = aCenterX + irad* cos( endAngle );
+ double iendy = aCenterY + irad* sin( endAngle );
+ double istx = aCenterX + irad* cos( stAngle );
+ double isty = aCenterY + irad* sin( stAngle );
+
+ if( ( aAngle < 0 && !aHoleFlag ) || ( aAngle > 0 && aHoleFlag ) )
+ {
+ aAngle = -aAngle;
+ std::swap( stAngle, endAngle );
+ std::swap( oendx, ostx );
+ std::swap( oendy, osty );
+ std::swap( iendx, istx );
+ std::swap( iendy, isty );
+ }
+
+ int arc;
+
+ if( aHoleFlag && aPlatedHole )
+ arc = NewContour( true );
+ else
+ arc = NewContour( false );
+
+ if( arc < 0 )
+ {
+ error = "AddArc(): could not create a contour";
+ return false;
+ }
+
+ // trace the outer arc:
+ int i;
+ double ang;
+ double da = aAngle / osides;
+
+ for( ang = stAngle, i = 0; i < osides; ang += da, ++i )
+ AddVertex( arc, aCenterX + orad * cos( ang ), aCenterY + orad * sin( ang ) );
+
+ // trace the first cap
+ double capx = ( iendx + oendx ) / 2.0;
+ double capy = ( iendy + oendy ) / 2.0;
+
+ if( aHoleFlag )
+ da = -M_PI / csides;
+ else
+ da = M_PI / csides;
+
+ for( ang = endAngle, i = 0; i < csides; ang += da, ++i )
+ AddVertex( arc, capx + aArcWidth * cos( ang ), capy + aArcWidth * sin( ang ) );
+
+ // trace the inner arc:
+ da = -aAngle / isides;
+
+ for( ang = endAngle, i = 0; i < isides; ang += da, ++i )
+ AddVertex( arc, aCenterX + irad * cos( ang ), aCenterY + irad * sin( ang ) );
+
+ // trace the final cap
+ capx = ( istx + ostx ) / 2.0;
+ capy = ( isty + osty ) / 2.0;
+
+ if( aHoleFlag )
+ da = -M_PI / csides;
+ else
+ da = M_PI / csides;
+
+ for( ang = stAngle + M_PI, i = 0; i < csides; ang += da, ++i )
+ AddVertex( arc, capx + aArcWidth * cos( ang ), capy + aArcWidth * sin( ang ) );
+
+ return true;
+}
+
+
+// tesselates the contours in preparation for a 3D output;
+// returns true if all was fine, false otherwise
+bool VRML_LAYER::Tesselate( VRML_LAYER* holes, bool aHolesOnly )
+{
+ if( !tess )
+ {
+ error = "Tesselate(): GLU tesselator was not initialized";
+ return false;
+ }
+
+ pholes = holes;
+ Fault = false;
+
+ if( aHolesOnly )
+ gluTessProperty( tess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_NEGATIVE );
+ else
+ gluTessProperty( tess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_POSITIVE );
+
+
+ if( contours.size() < 1 || vertices.size() < 3 )
+ {
+ error = "Tesselate(): not enough vertices";
+ return false;
+ }
+
+ // finish the winding calculation on all vertices prior to setting 'fix'
+ if( !fix )
+ {
+ for( unsigned int i = 0; i < contours.size(); ++i )
+ {
+ if( contours[i]->size() < 3 )
+ continue;
+
+ VERTEX_3D* vp0 = vertices[ contours[i]->back() ];
+ VERTEX_3D* vp1 = vertices[ contours[i]->front() ];
+ areas[i] += ( vp1->x - vp0->x ) * ( vp1->y + vp0->y );
+ }
+ }
+
+ // prevent the addition of any further contours and contour vertices
+ fix = true;
+
+ // clear temporary internals which may have been used in a previous run
+ clearTmp();
+
+ // request an outline
+ gluTessProperty( tess, GLU_TESS_BOUNDARY_ONLY, GL_TRUE );
+
+ // adjust internal indices for extra points and holes
+ if( holes )
+ hidx = holes->GetSize();
+ else
+ hidx = 0;
+
+ eidx = idx + hidx;
+
+ if( aHolesOnly && ( checkNContours( true ) == 0 ) )
+ {
+ error = "tesselate(): no hole contours";
+ return false;
+ }
+ else if( !aHolesOnly && ( checkNContours( false ) == 0 ) )
+ {
+ error = "tesselate(): no solid contours";
+ return false;
+ }
+
+ // open the polygon
+ gluTessBeginPolygon( tess, this );
+
+ if( aHolesOnly )
+ {
+ pholes = NULL; // do not accept foreign holes
+ hidx = 0;
+ eidx = idx;
+
+ // add holes
+ pushVertices( true );
+
+ gluTessEndPolygon( tess );
+
+ if( Fault )
+ return false;
+
+ return true;
+ }
+
+ // add solid outlines
+ pushVertices( false );
+
+ // close the polygon
+ gluTessEndPolygon( tess );
+
+ if( Fault )
+ return false;
+
+ // if there are no outlines we cannot proceed
+ if( outline.empty() )
+ {
+ error = "tesselate(): no points in result";
+ return false;
+ }
+
+ // at this point we have a solid outline; add it to the tesselator
+ gluTessBeginPolygon( tess, this );
+
+ if( !pushOutline( NULL ) )
+ return false;
+
+ // add the holes contained by this object
+ pushVertices( true );
+
+ // import external holes (if any)
+ if( hidx && ( holes->Import( idx, tess ) < 0 ) )
+ {
+ std::ostringstream ostr;
+ ostr << "Tesselate():FAILED: " << holes->GetError();
+ error = ostr.str();
+ return false;
+ }
+
+ if( Fault )
+ return false;
+
+ // erase the previous outline data and vertex order
+ // but preserve the extra vertices
+ while( !outline.empty() )
+ {
+ delete outline.back();
+ outline.pop_back();
+ }
+
+ ordmap.clear();
+ ord = 0;
+
+ // go through the vertex lists and reset ephemeral parameters
+ for( unsigned int i = 0; i < vertices.size(); ++i )
+ {
+ vertices[i]->o = -1;
+ }
+
+ for( unsigned int i = 0; i < extra_verts.size(); ++i )
+ {
+ extra_verts[i]->o = -1;
+ }
+
+ // close the polygon; this creates the outline points
+ // and the point ordering list 'ordmap'
+ solid.clear();
+ gluTessEndPolygon( tess );
+
+ // repeat the last operation but request a tesselated surface
+ // rather than an outline; this creates the triangles list.
+ gluTessProperty( tess, GLU_TESS_BOUNDARY_ONLY, GL_FALSE );
+
+ gluTessBeginPolygon( tess, this );
+
+ if( !pushOutline( holes ) )
+ return false;
+
+ gluTessEndPolygon( tess );
+
+ if( Fault )
+ return false;
+
+ return true;
+}
+
+
+bool VRML_LAYER::pushOutline( VRML_LAYER* holes )
+{
+ // traverse the outline list to push all used vertices
+ if( outline.size() < 1 )
+ {
+ error = "pushOutline() failed: no vertices to push";
+ return false;
+ }
+
+ std::list<std::list<int>*>::const_iterator obeg = outline.begin();
+ std::list<std::list<int>*>::const_iterator oend = outline.end();
+
+ int nc = 0; // number of contours pushed
+
+ int pi;
+ std::list<int>::const_iterator begin;
+ std::list<int>::const_iterator end;
+ GLdouble pt[3];
+ VERTEX_3D* vp;
+
+ while( obeg != oend )
+ {
+ if( (*obeg)->size() < 3 )
+ {
+ ++obeg;
+ continue;
+ }
+
+ gluTessBeginContour( tess );
+
+ begin = (*obeg)->begin();
+ end = (*obeg)->end();
+
+ while( begin != end )
+ {
+ pi = *begin;
+
+ if( pi < 0 || (unsigned int) pi > ordmap.size() )
+ {
+ gluTessEndContour( tess );
+ error = "pushOutline():BUG: *outline.begin() is not a valid index to ordmap";
+ return false;
+ }
+
+ // retrieve the actual index
+ pi = ordmap[pi];
+
+ vp = getVertexByIndex( pi, holes );
+
+ if( !vp )
+ {
+ gluTessEndContour( tess );
+ error = "pushOutline():: BUG: ordmap[n] is not a valid index to vertices[]";
+ return false;
+ }
+
+ pt[0] = vp->x;
+ pt[1] = vp->y;
+ pt[2] = 0.0;
+ gluTessVertex( tess, pt, vp );
+ ++begin;
+ }
+
+ gluTessEndContour( tess );
+ ++obeg;
+ ++nc;
+ }
+
+ if( !nc )
+ {
+ error = "pushOutline():: no valid contours available";
+ return false;
+ }
+
+ return true;
+}
+
+
+// writes out the vertex list for a planar feature
+bool VRML_LAYER::WriteVertices( double aZcoord, std::ofstream& aOutFile, int aPrecision )
+{
+ if( ordmap.size() < 3 )
+ {
+ error = "WriteVertices(): not enough vertices";
+ return false;
+ }
+
+ if( aPrecision < 4 )
+ aPrecision = 4;
+
+ int i, j;
+
+ VERTEX_3D* vp = getVertexByIndex( ordmap[0], pholes );
+
+ if( !vp )
+ return false;
+
+ std::string strx, stry, strz;
+ FormatDoublet( vp->x + offsetX, vp->y + offsetY, aPrecision, strx, stry );
+ FormatSinglet( aZcoord, aPrecision, strz );
+
+ aOutFile << strx << " " << stry << " " << strz;
+
+ for( i = 1, j = ordmap.size(); i < j; ++i )
+ {
+ vp = getVertexByIndex( ordmap[i], pholes );
+
+ if( !vp )
+ return false;
+
+ FormatDoublet( vp->x + offsetX, vp->y + offsetY, aPrecision, strx, stry );
+
+ if( i & 1 )
+ aOutFile << ", " << strx << " " << stry << " " << strz;
+ else
+ aOutFile << ",\n" << strx << " " << stry << " " << strz;
+ }
+
+ return !aOutFile.fail();
+}
+
+
+// writes out the vertex list for a 3D feature; top and bottom are the
+// Z values for the top and bottom; top must be > bottom
+bool VRML_LAYER::Write3DVertices( double aTopZ, double aBottomZ,
+ std::ofstream& aOutFile, int aPrecision )
+{
+ if( ordmap.size() < 3 )
+ {
+ error = "Write3DVertices(): insufficient vertices";
+ return false;
+ }
+
+ if( aPrecision < 4 )
+ aPrecision = 4;
+
+ if( aTopZ <= aBottomZ )
+ {
+ error = "Write3DVertices(): top <= bottom";
+ return false;
+ }
+
+ int i, j;
+
+ VERTEX_3D* vp = getVertexByIndex( ordmap[0], pholes );
+
+ if( !vp )
+ return false;
+
+ std::string strx, stry, strz;
+ FormatDoublet( vp->x + offsetX, vp->y + offsetY, aPrecision, strx, stry );
+ FormatSinglet( aTopZ, aPrecision, strz );
+
+ aOutFile << strx << " " << stry << " " << strz;
+
+ for( i = 1, j = ordmap.size(); i < j; ++i )
+ {
+ vp = getVertexByIndex( ordmap[i], pholes );
+
+ if( !vp )
+ return false;
+
+ FormatDoublet( vp->x + offsetX, vp->y + offsetY, aPrecision, strx, stry );
+
+ if( i & 1 )
+ aOutFile << ", " << strx << " " << stry << " " << strz;
+ else
+ aOutFile << ",\n" << strx << " " << stry << " " << strz;
+ }
+
+ // repeat for the bottom layer
+ vp = getVertexByIndex( ordmap[0], pholes );
+ FormatDoublet( vp->x + offsetX, vp->y + offsetY, aPrecision, strx, stry );
+ FormatSinglet( aBottomZ, aPrecision, strz );
+
+ bool endl;
+
+ if( i & 1 )
+ {
+ aOutFile << ", " << strx << " " << stry << " " << strz;
+ endl = false;
+ }
+ else
+ {
+ aOutFile << ",\n" << strx << " " << stry << " " << strz;
+ endl = true;
+ }
+
+ for( i = 1, j = ordmap.size(); i < j; ++i )
+ {
+ vp = getVertexByIndex( ordmap[i], pholes );
+ FormatDoublet( vp->x + offsetX, vp->y + offsetY, aPrecision, strx, stry );
+
+ if( endl )
+ {
+ aOutFile << ", " << strx << " " << stry << " " << strz;
+ endl = false;
+ }
+ else
+ {
+ aOutFile << ",\n" << strx << " " << stry << " " << strz;
+ endl = true;
+ }
+ }
+
+ return !aOutFile.fail();
+}
+
+
+// writes out the index list;
+// 'top' indicates the vertex ordering and should be
+// true for a polygon visible from above the PCB
+bool VRML_LAYER::WriteIndices( bool aTopFlag, std::ofstream& aOutFile )
+{
+ if( triplets.empty() )
+ {
+ error = "WriteIndices(): no triplets (triangular facets) to write";
+ return false;
+ }
+
+ // go through the triplet list and write out the indices based on order
+ std::list<TRIPLET_3D>::const_iterator tbeg = triplets.begin();
+ std::list<TRIPLET_3D>::const_iterator tend = triplets.end();
+
+ int i = 1;
+
+ if( aTopFlag )
+ aOutFile << tbeg->i1 << ", " << tbeg->i2 << ", " << tbeg->i3 << ", -1";
+ else
+ aOutFile << tbeg->i2 << ", " << tbeg->i1 << ", " << tbeg->i3 << ", -1";
+
+ ++tbeg;
+
+ while( tbeg != tend )
+ {
+ if( (i++ & 7) == 4 )
+ {
+ i = 1;
+
+ if( aTopFlag )
+ aOutFile << ",\n" << tbeg->i1 << ", " << tbeg->i2 << ", " << tbeg->i3 << ", -1";
+ else
+ aOutFile << ",\n" << tbeg->i2 << ", " << tbeg->i1 << ", " << tbeg->i3 << ", -1";
+ }
+ else
+ {
+ if( aTopFlag )
+ aOutFile << ", " << tbeg->i1 << ", " << tbeg->i2 << ", " << tbeg->i3 << ", -1";
+ else
+ aOutFile << ", " << tbeg->i2 << ", " << tbeg->i1 << ", " << tbeg->i3 << ", -1";
+ }
+
+ ++tbeg;
+ }
+
+ return !aOutFile.fail();
+}
+
+
+// writes out the index list for a 3D feature
+bool VRML_LAYER::Write3DIndices( std::ofstream& aOutFile, bool aIncludePlatedHoles )
+{
+ if( outline.empty() )
+ {
+ error = "WriteIndices(): no outline available";
+ return false;
+ }
+
+ char mark;
+ bool holes_only = triplets.empty();
+
+ int i = 1;
+ int idx2 = ordmap.size(); // index to the bottom vertices
+
+ if( !holes_only )
+ {
+ mark = ',';
+
+ // go through the triplet list and write out the indices based on order
+ std::list<TRIPLET_3D>::const_iterator tbeg = triplets.begin();
+ std::list<TRIPLET_3D>::const_iterator tend = triplets.end();
+
+ // print out the top vertices
+ aOutFile << tbeg->i1 << ", " << tbeg->i2 << ", " << tbeg->i3 << ", -1";
+ ++tbeg;
+
+ while( tbeg != tend )
+ {
+ if( (i++ & 7) == 4 )
+ {
+ i = 1;
+ aOutFile << ",\n" << tbeg->i1 << ", " << tbeg->i2 << ", " << tbeg->i3 << ", -1";
+ }
+ else
+ {
+ aOutFile << ", " << tbeg->i1 << ", " << tbeg->i2 << ", " << tbeg->i3 << ", -1";
+ }
+
+ ++tbeg;
+ }
+
+ // print out the bottom vertices
+ tbeg = triplets.begin();
+
+ while( tbeg != tend )
+ {
+ if( (i++ & 7) == 4 )
+ {
+ i = 1;
+ aOutFile << ",\n" << (tbeg->i2 + idx2) << ", " << (tbeg->i1 + idx2) << ", " << (tbeg->i3 + idx2) << ", -1";
+ }
+ else
+ {
+ aOutFile << ", " << (tbeg->i2 + idx2) << ", " << (tbeg->i1 + idx2) << ", " << (tbeg->i3 + idx2) << ", -1";
+ }
+
+ ++tbeg;
+ }
+ }
+ else
+ mark = ' ';
+
+
+ // print out indices for the walls joining top to bottom
+ int lastPoint;
+ int curPoint;
+ int curContour = 0;
+
+ std::list<std::list<int>*>::const_iterator obeg = outline.begin();
+ std::list<std::list<int>*>::const_iterator oend = outline.end();
+ std::list<int>* cp;
+ std::list<int>::const_iterator cbeg;
+ std::list<int>::const_iterator cend;
+
+ i = 2;
+ while( obeg != oend )
+ {
+ cp = *obeg;
+
+ if( cp->size() < 3 )
+ {
+ ++obeg;
+ ++curContour;
+ continue;
+ }
+
+ cbeg = cp->begin();
+ cend = cp->end();
+ lastPoint = *(cbeg++);
+
+ // skip all PTH vertices which are not in a solid outline
+ if( !aIncludePlatedHoles && !solid[curContour]
+ && getVertexByIndex( ordmap[lastPoint], pholes )->pth )
+ {
+ ++obeg;
+ ++curContour;
+ continue;
+ }
+
+ while( cbeg != cend )
+ {
+ curPoint = *(cbeg++);
+
+ if( !holes_only )
+ {
+ if( (i++ & 3) == 2 )
+ {
+ i = 1;
+ aOutFile << mark << "\n" << curPoint << ", " << lastPoint << ", " << curPoint + idx2;
+ aOutFile << ", -1, " << curPoint + idx2 << ", " << lastPoint << ", " << lastPoint + idx2 << ", -1";
+ }
+ else
+ {
+ aOutFile << mark << " " << curPoint << ", " << lastPoint << ", " << curPoint + idx2;
+ aOutFile << ", -1, " << curPoint + idx2 << ", " << lastPoint << ", " << lastPoint + idx2 << ", -1";
+ }
+ }
+ else
+ {
+ if( (i++ & 3) == 2 )
+ {
+ i = 1;
+ aOutFile << mark << "\n" << curPoint << ", " << curPoint + idx2 << ", " << lastPoint;
+ aOutFile << ", -1, " << curPoint + idx2 << ", " << lastPoint + idx2 << ", " << lastPoint << ", -1";
+ }
+ else
+ {
+ aOutFile << mark << " " << curPoint << ", " << curPoint + idx2 << ", " << lastPoint;
+ aOutFile << ", -1, " << curPoint + idx2 << ", " << lastPoint + idx2 << ", " << lastPoint << ", -1";
+ }
+ }
+
+ mark = ',';
+ lastPoint = curPoint;
+ }
+
+ // check if the loop needs to be closed
+ cbeg = cp->begin();
+ cend = --cp->end();
+
+ curPoint = *(cbeg);
+ lastPoint = *(cend);
+
+ if( !holes_only )
+ {
+ if( (i++ & 3) == 2 )
+ {
+ aOutFile << ",\n" << curPoint << ", " << lastPoint << ", " << curPoint + idx2;
+ aOutFile << ", -1, " << curPoint + idx2 << ", " << lastPoint << ", " << lastPoint + idx2 << ", -1";
+ }
+ else
+ {
+ aOutFile << ", " << curPoint << ", " << lastPoint << ", " << curPoint + idx2;
+ aOutFile << ", -1, " << curPoint + idx2 << ", " << lastPoint << ", " << lastPoint + idx2 << ", -1";
+ }
+ }
+ else
+ {
+ if( (i++ & 3) == 2 )
+ {
+ aOutFile << ",\n" << curPoint << ", " << curPoint + idx2 << ", " << lastPoint;
+ aOutFile << ", -1, " << curPoint + idx2 << ", " << lastPoint + idx2 << ", " << lastPoint << ", -1";
+ }
+ else
+ {
+ aOutFile << ", " << curPoint << ", " << curPoint + idx2 << ", " << lastPoint;
+ aOutFile << ", -1, " << curPoint + idx2 << ", " << lastPoint + idx2 << ", " << lastPoint << ", -1";
+ }
+ }
+
+ ++obeg;
+ ++curContour;
+ }
+
+ return !aOutFile.fail();
+}
+
+
+// add a triangular facet (triplet) to the ouptut index list
+bool VRML_LAYER::addTriplet( VERTEX_3D* p0, VERTEX_3D* p1, VERTEX_3D* p2 )
+{
+ double dx0 = p1->x - p0->x;
+ double dx1 = p2->x - p0->x;
+
+ double dy0 = p1->y - p0->y;
+ double dy1 = p2->y - p0->y;
+
+ // this number is chosen because we shall only write 9 decimal places
+ // at most on the VRML output
+ double err = 0.000000001;
+
+ // test if the triangles are degenerate (parallel sides)
+
+ if( dx0 < err && dx0 > -err && dx1 < err && dx1 > -err )
+ return false;
+
+ if( dy0 < err && dy0 > -err && dy1 < err && dy1 > -err )
+ return false;
+
+ double sl0 = dy0 / dx0;
+ double sl1 = dy1 / dx1;
+
+ double dsl = sl1 - sl0;
+
+ if( dsl < err && dsl > -err )
+ return false;
+
+ triplets.push_back( TRIPLET_3D( p0->o, p1->o, p2->o ) );
+
+ return true;
+}
+
+
+// add an extra vertex (to be called only by the COMBINE callback)
+VERTEX_3D* VRML_LAYER::AddExtraVertex( double aXpos, double aYpos, bool aPlatedHole )
+{
+ VERTEX_3D* vertex = new VERTEX_3D;
+
+ if( !vertex )
+ {
+ error = "AddExtraVertex(): could not allocate a new vertex";
+ return NULL;
+ }
+
+ if( eidx == 0 )
+ eidx = idx + hidx;
+
+ vertex->x = aXpos;
+ vertex->y = aYpos;
+ vertex->i = eidx++;
+ vertex->o = -1;
+ vertex->pth = aPlatedHole;
+
+ extra_verts.push_back( vertex );
+
+ return vertex;
+}
+
+
+// start a GL command list
+void VRML_LAYER::glStart( GLenum cmd )
+{
+ glcmd = cmd;
+
+ while( !vlist.empty() )
+ vlist.pop_back();
+}
+
+
+// process a vertex
+void VRML_LAYER::glPushVertex( VERTEX_3D* vertex )
+{
+ if( vertex->o < 0 )
+ {
+ vertex->o = ord++;
+ ordmap.push_back( vertex->i );
+ }
+
+ vlist.push_back( vertex );
+}
+
+
+// end a GL command list
+void VRML_LAYER::glEnd( void )
+{
+ switch( glcmd )
+ {
+ case GL_LINE_LOOP:
+ {
+ // add the loop to the list of outlines
+ std::list<int>* loop = new std::list<int>;
+
+ if( !loop )
+ break;
+
+ double firstX = 0.0;
+ double firstY = 0.0;
+ double lastX = 0.0;
+ double lastY = 0.0;
+ double curX, curY;
+ double area = 0.0;
+
+ if( vlist.size() > 0 )
+ {
+ loop->push_back( vlist[0]->o );
+ firstX = vlist[0]->x;
+ firstY = vlist[0]->y;
+ lastX = firstX;
+ lastY = firstY;
+ }
+
+ for( size_t i = 1; i < vlist.size(); ++i )
+ {
+ loop->push_back( vlist[i]->o );
+ curX = vlist[i]->x;
+ curY = vlist[i]->y;
+ area += ( curX - lastX ) * ( curY + lastY );
+ lastX = curX;
+ lastY = curY;
+ }
+
+ area += ( firstX - lastX ) * ( firstY + lastY );
+
+ outline.push_back( loop );
+
+ if( area <= 0.0 )
+ solid.push_back( true );
+ else
+ solid.push_back( false );
+ }
+ break;
+
+ case GL_TRIANGLE_FAN:
+ processFan();
+ break;
+
+ case GL_TRIANGLE_STRIP:
+ processStrip();
+ break;
+
+ case GL_TRIANGLES:
+ processTri();
+ break;
+
+ default:
+ break;
+ }
+
+ while( !vlist.empty() )
+ vlist.pop_back();
+
+ glcmd = 0;
+}
+
+
+// set the error message
+void VRML_LAYER::SetGLError( GLenum errorID )
+{
+ const char * msg = (const char*)gluErrorString( errorID );
+
+ // If errorID is an illegal id, gluErrorString returns NULL
+ if( msg )
+ error = msg;
+ else
+ error.clear();
+
+ if( error.empty() )
+ {
+ std::ostringstream ostr;
+ ostr << "Unknown OpenGL error: " << errorID;
+ error = ostr.str();
+ }
+}
+
+
+// process a GL_TRIANGLE_FAN list
+void VRML_LAYER::processFan( void )
+{
+ if( vlist.size() < 3 )
+ return;
+
+ VERTEX_3D* p0 = vlist[0];
+
+ int i;
+ int end = vlist.size();
+
+ for( i = 2; i < end; ++i )
+ {
+ addTriplet( p0, vlist[i - 1], vlist[i] );
+ }
+}
+
+
+// process a GL_TRIANGLE_STRIP list
+void VRML_LAYER::processStrip( void )
+{
+ // note: (source: http://www.opengl.org/wiki/Primitive)
+ // GL_TRIANGLE_STRIP​: Every group of 3 adjacent vertices forms a triangle.
+ // The face direction of the strip is determined by the winding of the
+ // first triangle. Each successive triangle will have its effective face
+ // order reverse, so the system compensates for that by testing it in the
+ // opposite way. A vertex stream of n length will generate n-2 triangles.
+
+ if( vlist.size() < 3 )
+ return;
+
+ int i;
+ int end = vlist.size();
+ bool flip = false;
+
+ for( i = 2; i < end; ++i )
+ {
+ if( flip )
+ {
+ addTriplet( vlist[i - 1], vlist[i - 2], vlist[i] );
+ flip = false;
+ }
+ else
+ {
+ addTriplet( vlist[i - 2], vlist[i - 1], vlist[i] );
+ flip = true;
+ }
+ }
+}
+
+
+// process a GL_TRIANGLES list
+void VRML_LAYER::processTri( void )
+{
+ // notes:
+ // 1. each successive group of 3 vertices is a triangle
+ // 2. as per OpenGL specification, any incomplete triangles are to be ignored
+
+ if( vlist.size() < 3 )
+ return;
+
+ int i;
+ int end = vlist.size();
+
+ for( i = 2; i < end; i += 3 )
+ addTriplet( vlist[i - 2], vlist[i - 1], vlist[i] );
+}
+
+
+int VRML_LAYER::checkNContours( bool holes )
+{
+ int nc = 0; // number of contours
+
+ if( contours.empty() )
+ return 0;
+
+ std::list<int>::const_iterator begin;
+ std::list<int>::const_iterator end;
+
+ for( size_t i = 0; i < contours.size(); ++i )
+ {
+ if( contours[i]->size() < 3 )
+ continue;
+
+ if( ( holes && areas[i] <= 0.0 ) || ( !holes && areas[i] > 0.0 ) )
+ continue;
+
+ ++nc;
+ }
+
+ return nc;
+}
+
+
+// push the internally held vertices
+void VRML_LAYER::pushVertices( bool holes )
+{
+ // push the internally held vertices
+ unsigned int i;
+
+ std::list<int>::const_iterator begin;
+ std::list<int>::const_iterator end;
+ GLdouble pt[3];
+ VERTEX_3D* vp;
+
+ for( i = 0; i < contours.size(); ++i )
+ {
+ if( contours[i]->size() < 3 )
+ continue;
+
+ if( ( holes && areas[i] <= 0.0 ) || ( !holes && areas[i] > 0.0 ) )
+ continue;
+
+ gluTessBeginContour( tess );
+
+ begin = contours[i]->begin();
+ end = contours[i]->end();
+
+ while( begin != end )
+ {
+ vp = vertices[ *begin ];
+ pt[0] = vp->x;
+ pt[1] = vp->y;
+ pt[2] = 0.0;
+ gluTessVertex( tess, pt, vp );
+ ++begin;
+ }
+
+ gluTessEndContour( tess );
+ }
+
+ return;
+}
+
+
+VERTEX_3D* VRML_LAYER::getVertexByIndex( int aPointIndex, VRML_LAYER* holes )
+{
+ if( aPointIndex < 0 || (unsigned int) aPointIndex >= ( idx + hidx + extra_verts.size() ) )
+ {
+ error = "getVertexByIndex():BUG: invalid index";
+ return NULL;
+ }
+
+ if( aPointIndex < idx )
+ {
+ // vertex is in the vertices[] list
+ return vertices[ aPointIndex ];
+ }
+ else if( aPointIndex >= idx + hidx )
+ {
+ // vertex is in the extra_verts[] list
+ return extra_verts[aPointIndex - idx - hidx];
+ }
+
+ // vertex is in the holes object
+ if( !holes )
+ {
+ error = "getVertexByIndex():BUG: invalid index";
+ return NULL;
+ }
+
+ VERTEX_3D* vp = holes->GetVertexByIndex( aPointIndex );
+
+ if( !vp )
+ {
+ std::ostringstream ostr;
+ ostr << "getVertexByIndex():FAILED: " << holes->GetError();
+ error = ostr.str();
+ return NULL;
+ }
+
+ return vp;
+}
+
+
+// retrieve the total number of vertices
+int VRML_LAYER::GetSize( void )
+{
+ return vertices.size();
+}
+
+
+// Inserts all contours into the given tesselator; this results in the
+// renumbering of all vertices from 'start'. Returns the end number.
+// Take care when using this call since tesselators cannot work on
+// the internal data concurrently
+int VRML_LAYER::Import( int start, GLUtesselator* tess )
+{
+ if( start < 0 )
+ {
+ error = "Import(): invalid index ( start < 0 )";
+ return -1;
+ }
+
+ if( !tess )
+ {
+ error = "Import(): NULL tesselator pointer";
+ return -1;
+ }
+
+ unsigned int i, j;
+
+ // renumber from 'start'
+ for( i = 0, j = vertices.size(); i < j; ++i )
+ {
+ vertices[i]->i = start++;
+ vertices[i]->o = -1;
+ }
+
+ // push each contour to the tesselator
+ VERTEX_3D* vp;
+ GLdouble pt[3];
+
+ std::list<int>::const_iterator cbeg;
+ std::list<int>::const_iterator cend;
+
+ for( i = 0; i < contours.size(); ++i )
+ {
+ if( contours[i]->size() < 3 )
+ continue;
+
+ cbeg = contours[i]->begin();
+ cend = contours[i]->end();
+
+ gluTessBeginContour( tess );
+
+ while( cbeg != cend )
+ {
+ vp = vertices[ *cbeg++ ];
+ pt[0] = vp->x;
+ pt[1] = vp->y;
+ pt[2] = 0.0;
+ gluTessVertex( tess, pt, vp );
+ }
+
+ gluTessEndContour( tess );
+ }
+
+ return start;
+}
+
+
+// return the vertex identified by index
+VERTEX_3D* VRML_LAYER::GetVertexByIndex( int aPointIndex )
+{
+ int i0 = vertices[0]->i;
+
+ if( aPointIndex < i0 || aPointIndex >= ( i0 + (int) vertices.size() ) )
+ {
+ error = "GetVertexByIndex(): invalid index";
+ return NULL;
+ }
+
+ return vertices[aPointIndex - i0];
+}
+
+
+// return the error string
+const std::string& VRML_LAYER::GetError( void )
+{
+ return error;
+}
+
+
+void VRML_LAYER::SetVertexOffsets( double aXoffset, double aYoffset )
+{
+ offsetX = aXoffset;
+ offsetY = aYoffset;
+ return;
+}
diff --git a/utils/idftools/vrml_layer.h b/utils/idftools/vrml_layer.h
new file mode 100644
index 0000000..92b5891
--- /dev/null
+++ b/utils/idftools/vrml_layer.h
@@ -0,0 +1,461 @@
+/*
+ * file: vrml_layer.h
+ *
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2013 Cirilo Bernardo
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * @file vrml_layer.h
+ */
+
+/*
+ * Classes and structures to support the tesselation of a
+ * PCB for VRML output.
+ */
+
+#ifndef VRML_LAYER_H
+#define VRML_LAYER_H
+
+
+#include <wx/glcanvas.h> // CALLBACK definition, needed on Windows
+ // alse needed on OSX to define __DARWIN__
+
+#ifdef __WXMAC__
+# ifdef __DARWIN__
+# include <OpenGL/glu.h>
+# else
+# include <glu.h>
+# endif
+#else
+# include <GL/glu.h>
+#endif
+
+#include <fstream>
+#include <vector>
+#include <list>
+#include <utility>
+
+#ifndef M_PI2
+#define M_PI2 ( M_PI / 2.0 )
+#endif
+
+#ifndef M_PI4
+#define M_PI4 ( M_PI / 4.0 )
+#endif
+
+
+struct VERTEX_3D
+{
+ double x;
+ double y;
+ int i; // vertex index
+ int o; // vertex order
+ bool pth; // true for plate-through hole
+};
+
+struct TRIPLET_3D
+{
+ int i1, i2, i3;
+
+ TRIPLET_3D( int p1, int p2, int p3 )
+ {
+ i1 = p1;
+ i2 = p2;
+ i3 = p3;
+ }
+};
+
+
+class VRML_LAYER
+{
+private:
+ // Arc parameters
+ int maxArcSeg; // maximum number of arc segments in a small circle
+ double minSegLength; // min. segment length
+ double maxSegLength; // max. segment length
+
+ // Vertex offsets to work around a suspected GLU tesselator bug
+ double offsetX;
+ double offsetY;
+
+ bool fix; // when true, no more vertices may be added by the user
+ int idx; // vertex index (number of contained vertices)
+ int ord; // vertex order (number of ordered vertices)
+ std::vector<VERTEX_3D*> vertices; // vertices of all contours
+ std::vector<std::list<int>*> contours; // lists of vertices for each contour
+ std::vector<bool>pth; // indicates whether a 'contour' is a PTH or not
+ std::vector<bool>solid; // indicates whether a 'contour' is a solid or a hole
+ std::vector< double > areas; // area of the contours (positive if winding is CCW)
+ std::list<TRIPLET_3D> triplets; // output facet triplet list (triplet of ORDER values)
+ std::list<std::list<int>*> outline; // indices for outline outputs (index by ORDER values)
+ std::vector<int> ordmap; // mapping of ORDER to INDEX
+
+ std::string error; // error message
+
+ int hidx; // number of vertices in the holes
+ int eidx; // index for extra vertices
+ std::vector<VERTEX_3D*> extra_verts; // extra vertices added for outlines and facets
+ std::vector<VERTEX_3D*> vlist; // vertex list for the GL command in progress
+ VRML_LAYER* pholes; // pointer to another layer object used for tesselation;
+ // this object is normally expected to hold only holes
+
+ GLUtesselator* tess; // local instance of the GLU tesselator
+
+ GLenum glcmd; // current GL command type ( fan, triangle, tri-strip, loop )
+
+ void clearTmp( void ); // clear ephemeral data used by the tesselation routine
+
+ // add a triangular facet (triplet) to the output index list
+ bool addTriplet( VERTEX_3D* p0, VERTEX_3D* p1, VERTEX_3D* p2 );
+
+ // retrieve a vertex given its index; the vertex may be contained in the
+ // vertices vector, extra_verts vector, or foreign VRML_LAYER object
+ VERTEX_3D* getVertexByIndex( int aPointIndex, VRML_LAYER* holes );
+
+ void processFan( void ); // process a GL_TRIANGLE_FAN list
+ void processStrip( void ); // process a GL_TRIANGLE_STRIP list
+ void processTri( void ); // process a GL_TRIANGLES list
+
+ void pushVertices( bool holes ); // push the internal vertices
+ bool pushOutline( VRML_LAYER* holes ); // push the outline vertices
+
+ // calculate number of sides on an arc (angle is in radians)
+ int calcNSides( double aRadius, double aAngle );
+
+ // returns the number of solid or hole contours
+ int checkNContours( bool holes );
+
+public:
+ /// set to true when a fault is encountered during tesselation
+ bool Fault;
+
+ VRML_LAYER();
+ virtual ~VRML_LAYER();
+
+ /**
+ * Function GetArcParams
+ * retieves the parameters used in calculating the number of vertices in an arc
+ *
+ * @param aMaxSeg is the maximum number of segments for an arc with cords of length aMinLength
+ * @param aMinLength is the minimum length of cords in an arc
+ * @param aMaxLength is the maximum length of cords in an arc
+ */
+ void GetArcParams( int& aMaxSeg, double& aMinLength, double& aMaxLength );
+
+ /**
+ * Function SetArcParams
+ * sets the parameters used in calculating the number of vertices in an arc.
+ * The default settings are reasonable for rendering for unit lengths of 1mm
+ *
+ * @param aMaxSeg is the maximum number of segments for an arc with cords of length aMinLength
+ * @param aMinLength is the minimum length of cords in an arc
+ * @param aMaxLength is the maximum length of cords in an arc
+ *
+ * @return bool: true if the parameters were accepted
+ */
+ bool SetArcParams( int aMaxSeg, double aMinLength, double aMaxLength );
+
+ /**
+ * Function Clear
+ * erases all data except for arc parameters.
+ */
+ void Clear( void );
+
+ /**
+ * Function GetSize
+ * returns the total number of vertices indexed
+ */
+ int GetSize( void );
+
+ /**
+ * Function GetNConours
+ * returns the number of stored contours
+ */
+ int GetNContours( void )
+ {
+ return contours.size();
+ }
+
+ /**
+ * Function NewContour
+ * creates a new list of vertices and returns an index to the list
+ *
+ * @param aPlatedHole is true if the new contour will represent a plated hole
+ *
+ * @return int: index to the list or -1 if the operation failed
+ */
+ int NewContour( bool aPlatedHole = false );
+
+ /**
+ * Function AddVertex
+ * adds a point to the requested contour
+ *
+ * @param aContour is an index previously returned by a call to NewContour()
+ * @param aXpos is the X coordinate of the vertex
+ * @param aYpos is the Y coordinate of the vertex
+ *
+ * @return bool: true if the vertex was added
+ */
+ bool AddVertex( int aContourID, double aXpos, double aYpos );
+
+ /**
+ * Function EnsureWinding
+ * checks the winding of a contour and ensures that it is a hole or
+ * a solid depending on the value of @param hole
+ *
+ * @param aContour is an index to a contour as returned by NewContour()
+ * @param aHoleFlag determines if the contour must be a hole
+ *
+ * @return bool: true if the operation suceeded
+ */
+ bool EnsureWinding( int aContourID, bool aHoleFlag );
+
+ /**
+ * Function AppendCircle
+ * adds a circular contour to the specified (empty) contour
+ *
+ * @param aXpos is the X coordinate of the hole center
+ * @param aYpos is the Y coordinate of the hole center
+ * @param aRadius is the radius of the hole
+ * @param aContourID is the contour index
+ * @param aHoleFlag determines if the contour to be created is a cutout
+ *
+ * @return bool: true if the new contour was successfully created
+ */
+ bool AppendCircle( double aXpos, double aYpos, double aRadius,
+ int aContourID, bool aHoleFlag = false );
+
+ /**
+ * Function AddCircle
+ * creates a circular contour and adds it to the internal list
+ *
+ * @param aXpos is the X coordinate of the hole center
+ * @param aYpos is the Y coordinate of the hole center
+ * @param aRadius is the radius of the hole
+ * @param aHoleFlag determines if the contour to be created is a cutout
+ * @param aPlatedHole is true if this is a plated hole
+ *
+ * @return bool: true if the new contour was successfully created
+ */
+ bool AddCircle( double aXpos, double aYpos, double aRadius,
+ bool aHoleFlag = false, bool aPlatedHole = false );
+
+ /**
+ * Function AddSlot
+ * creates and adds a slot feature to the list of contours
+ *
+ * @param aCenterX is the X coordinate of the slot's center
+ * @param aCenterY is the Y coordinate of the slot's center
+ * @param aSlotLength is the length of the slot along the major axis
+ * @param aSlotWidth is the width of the slot along the minor axis
+ * @param aAngle (degrees) is the orientation of the slot
+ * @param aHoleFlag determines whether the slot is a hole or a solid
+ * @param aPlatedHole is true if this is a plated slot
+ *
+ * @return bool: true if the slot was successfully created
+ */
+ bool AddSlot( double aCenterX, double aCenterY, double aSlotLength, double aSlotWidth,
+ double aAngle, bool aHoleFlag = false, bool aPlatedHole = false );
+
+ /**
+ * Function AppendArc
+ * adds an arc to the specified contour
+ *
+ * @param aCenterX is the X coordinate of the arc's center
+ * @param aCenterY is the Y coordinate of the arc's center
+ * @param aRadius is the radius of the arc
+ * @param aStartAngle (degrees) is the starting angle of the arc
+ * @param aAngle (degrees) is the measure of the arc
+ * @param aContourID is the contour's index
+ *
+ * @return bool: true if the slot was successfully created
+ */
+ bool AppendArc( double aCenterX, double aCenterY, double aRadius,
+ double aStartAngle, double aAngle, int aContourID );
+
+ /**
+ * Function AddArc
+ * creates a slotted arc and adds it to the internal list of contours
+ *
+ * @param aCenterX is the X coordinate of the arc's center
+ * @param aCenterY is the Y coordinate of the arc's center
+ * @param aStartX is the X coordinate of the starting point
+ * @param aStartY is the Y coordinate of the starting point
+ * @param aArcWidth is the width of the arc
+ * @param aAngle is the included angle (degrees)
+ * @param aHoleFlag determines whether the arc is to be a hole or a solid
+ * @param aPlatedHole is true if this is a plated slotted arc
+ *
+ * @return bool: true if the feature was successfully created
+ */
+ bool AddArc( double aCenterX, double aCenterY,
+ double aStartX, double aStartY,
+ double aArcWidth, double aAngle,
+ bool aHoleFlag = false, bool aPlatedHole = false );
+
+ /**
+ * Function Tesselate
+ * creates a list of outline vertices as well as the
+ * vertex sets required to render the surface.
+ *
+ * @param holes is an optional pointer to cutouts to be imposed on the
+ * surface.
+ * @param aHolesOnly is true if the outline contains only holes
+ *
+ * @return bool: true if the operation succeeded
+ */
+ bool Tesselate( VRML_LAYER* holes = NULL, bool aHolesOnly = false );
+
+ /**
+ * Function WriteVertices
+ * writes out the list of vertices required to render a
+ * planar surface.
+ *
+ * @param aZcoord is the Z coordinate of the plane
+ * @param aOutFile is the file to write to
+ * @param aPrecision is the precision of the output coordinates
+ *
+ * @return bool: true if the operation succeeded
+ */
+ bool WriteVertices( double aZcoord, std::ofstream& aOutFile, int aPrecision );
+
+ /**
+ * Function Write3DVertices
+ * writes out the list of vertices required to render an extruded solid
+ *
+ * @param aTopZ is the Z coordinate of the top plane
+ * @param aBottomZ is the Z coordinate of the bottom plane
+ * @param aOutFile is the file to write to
+ * @param aPrecision is the precision of the output coordinates
+ *
+ * @return bool: true if the operation succeeded
+ */
+ bool Write3DVertices( double aTopZ, double aBottomZ, std::ofstream& aOutFile, int aPrecision );
+
+ /**
+ * Function WriteIndices
+ * writes out the vertex sets required to render a planar
+ * surface.
+ *
+ * @param aTopFlag is true if the surface is to be visible from above;
+ * if false the surface will be visible from below.
+ * @param aOutFile is the file to write to
+ *
+ * @return bool: true if the operation succeeded
+ */
+ bool WriteIndices( bool aTopFlag, std::ofstream& aOutFile );
+
+ /**
+ * Function Write3DIndices
+ * writes out the vertex sets required to render an extruded solid
+ *
+ * @param aOutFile is the file to write to
+ * @param aIncludePlatedHoles is true if holes marked as plated should
+ * be rendered. Default is false since the user will usually
+ * render these holes in a different color
+ *
+ * @return bool: true if the operation succeeded
+ */
+ bool Write3DIndices( std::ofstream& aOutFile, bool aIncludePlatedHoles = false );
+
+ /**
+ * Function AddExtraVertex
+ * adds an extra vertex as required by the GLU tesselator.
+ *
+ * @param aXpos is the X coordinate of the newly created point
+ * @param aYpos is the Y coordinate of the newly created point
+ * @param aPlatedHole is true if this point is part of a plated hole
+ *
+ * @return VERTEX_3D*: is the new vertex or NULL if a vertex
+ * could not be created.
+ */
+ VERTEX_3D* AddExtraVertex( double aXpos, double aYpos, bool aPlatedHole );
+
+ /**
+ * Function glStart
+ * is invoked by the GLU tesselator callback to notify this object
+ * of the type of GL command which is applicable to the upcoming
+ * vertex list.
+ *
+ * @param cmd is the GL command
+ */
+ void glStart( GLenum cmd );
+
+ /**
+ * Function glPushVertex
+ * is invoked by the GLU tesselator callback; the supplied vertex is
+ * added to the internal list of vertices awaiting processing upon
+ * execution of glEnd()
+ *
+ * @param vertex is a vertex forming part of the GL command as previously
+ * set by glStart
+ */
+ void glPushVertex( VERTEX_3D* vertex );
+
+ /**
+ * Function glEnd
+ * is invoked by the GLU tesselator callback to notify this object
+ * that the vertex list is complete and ready for processing
+ */
+ void glEnd( void );
+
+ /**
+ * Function SetGLError
+ * sets the error message according to the specified OpenGL error
+ */
+ void SetGLError( GLenum error_id );
+
+ /**
+ * Function Import
+ * inserts all contours into the given tesselator; this
+ * results in the renumbering of all vertices from @param start.
+ * Take care when using this call since tesselators cannot work on
+ * the internal data concurrently.
+ *
+ * @param start is the starting number for vertex indices
+ * @param tess is a pointer to a GLU Tesselator object
+ *
+ * @return int: the number of vertices exported
+ */
+ int Import( int start, GLUtesselator* tess );
+
+ /**
+ * Function GetVertexByIndex
+ * returns a pointer to the requested vertex or
+ * NULL if no such vertex exists.
+ *
+ * @param aPointIndex is a vertex index
+ *
+ * @return VERTEX_3D*: the requested vertex or NULL
+ */
+ VERTEX_3D* GetVertexByIndex( int aPointIndex );
+
+ /*
+ * Function GetError
+ * Returns the error message related to the last failed operation
+ */
+ const std::string& GetError( void );
+
+ void SetVertexOffsets( double aXoffset, double aYoffset );
+};
+
+#endif // VRML_LAYER_H