From 1fa449fed953fa11f6bd0ea82cc2d3b115ee0781 Mon Sep 17 00:00:00 2001 From: saurabhb17 Date: Wed, 26 Feb 2020 16:36:01 +0530 Subject: Remaining files transfered --- AUTHORS.txt | 2 + CTestConfig.cmake | 12 + Doxyfile | 1799 ++++++++ README.txt | 5 +- copyright.h | 34 + polygon/CMakeLists.txt | 17 + polygon/PolyLine.cpp | 1374 ++++++ polygon/PolyLine.h | 490 +++ polygon/SutherlandHodgmanClipPoly.h | 273 ++ polygon/clipper.cpp | 4642 ++++++++++++++++++++ polygon/clipper.hpp | 404 ++ polygon/determine_if_point_inside_polygon.odt | Bin 0 -> 26791 bytes polygon/math_for_graphics.cpp | 520 +++ polygon/math_for_graphics.h | 71 + polygon/poly2tri/common/shapes.cc | 500 +++ polygon/poly2tri/common/shapes.h | 351 ++ polygon/poly2tri/common/utils.h | 133 + polygon/poly2tri/poly2tri.h | 39 + polygon/poly2tri/sweep/advancing_front.cc | 109 + polygon/poly2tri/sweep/advancing_front.h | 118 + polygon/poly2tri/sweep/cdt.cc | 72 + polygon/poly2tri/sweep/cdt.h | 105 + polygon/poly2tri/sweep/sweep.cc | 817 ++++ polygon/poly2tri/sweep/sweep.h | 284 ++ polygon/poly2tri/sweep/sweep_context.cc | 216 + polygon/poly2tri/sweep/sweep_context.h | 186 + polygon/polygon_test_point_inside.cpp | 171 + polygon/polygon_test_point_inside.h | 59 + polygon/polygons_defs.h | 89 + potrace/AUTHORS | 5 + potrace/CMakeLists.txt | 16 + potrace/auxiliary.h | 89 + potrace/bitmap.h | 118 + potrace/bitmap_io.cpp | 969 ++++ potrace/bitmap_io.h | 23 + potrace/bitops.h | 50 + potrace/curve.cpp | 122 + potrace/curve.h | 80 + potrace/decompose.cpp | 600 +++ potrace/decompose.h | 18 + potrace/greymap.cpp | 1108 +++++ potrace/greymap.h | 58 + potrace/lists.h | 293 ++ potrace/platform.h | 39 + potrace/potrace_version.h | 1 + potrace/potracelib.cpp | 129 + potrace/potracelib.h | 139 + potrace/progress.h | 96 + potrace/render.cpp | 294 ++ potrace/render.h | 35 + potrace/trace.cpp | 1464 ++++++ potrace/trace.h | 15 + qa/CMakeLists.txt | 15 + qa/data/complex_hierarchy.kicad_pcb | 3795 ++++++++++++++++ qa/test.py | 18 + qa/testcases/test_000_qa_works.py | 17 + qa/testcases/test_001_pcb_load.py | 48 + qa/testcases/test_002_board_class.py | 132 + .../mime/applications/bitmap2component.desktop | 10 + resources/linux/mime/applications/cvpcb.desktop | 10 + resources/linux/mime/applications/eeschema.desktop | 11 + resources/linux/mime/applications/gerbview.desktop | 10 + resources/linux/mime/applications/kicad.desktop | 11 + .../linux/mime/applications/pcbcalculator.desktop | 10 + resources/linux/mime/applications/pcbnew.desktop | 10 + .../linux/mime/icons/hicolor/128x128/apps/3d.png | Bin 0 -> 10917 bytes .../hicolor/128x128/apps/bitmap2component.png | Bin 0 -> 13176 bytes .../mime/icons/hicolor/128x128/apps/cvpcb.png | Bin 0 -> 12149 bytes .../mime/icons/hicolor/128x128/apps/eeschema.png | Bin 0 -> 11777 bytes .../mime/icons/hicolor/128x128/apps/gerbview.png | Bin 0 -> 21775 bytes .../mime/icons/hicolor/128x128/apps/kicad.png | Bin 0 -> 5144 bytes .../icons/hicolor/128x128/apps/pcbcalculator.png | Bin 0 -> 9585 bytes .../mime/icons/hicolor/128x128/apps/pcbnew.png | Bin 0 -> 12292 bytes .../128x128/mimetypes/application-x-3d-project.png | Bin 0 -> 10917 bytes .../application-x-bitmap2component-project.png | Bin 0 -> 13176 bytes .../mimetypes/application-x-cvpcb-project.png | Bin 0 -> 12149 bytes .../mimetypes/application-x-eeschema-project.png | Bin 0 -> 11777 bytes .../mimetypes/application-x-gerbview-project.png | Bin 0 -> 21775 bytes .../mimetypes/application-x-kicad-project.png | Bin 0 -> 5144 bytes .../application-x-pcbcalculator-project.png | Bin 0 -> 9585 bytes .../mimetypes/application-x-pcbnew-project.png | Bin 0 -> 12292 bytes .../mimetypes/application-x-kicad-project.png | Bin 0 -> 679 bytes .../linux/mime/icons/hicolor/22x22/apps/kicad.png | Bin 0 -> 1083 bytes .../mimetypes/application-x-kicad-project.png | Bin 0 -> 1083 bytes .../linux/mime/icons/hicolor/24x24/apps/kicad.png | Bin 0 -> 1160 bytes .../mimetypes/application-x-kicad-project.png | Bin 0 -> 1160 bytes .../linux/mime/icons/hicolor/32x32/apps/kicad.png | Bin 0 -> 1448 bytes .../mimetypes/application-x-kicad-project.png | Bin 0 -> 1448 bytes .../icons/hicolor/48x48/apps/bitmap2component.png | Bin 0 -> 3598 bytes .../linux/mime/icons/hicolor/48x48/apps/cvpcb.png | Bin 0 -> 2435 bytes .../mime/icons/hicolor/48x48/apps/eeschema.png | Bin 0 -> 3347 bytes .../mime/icons/hicolor/48x48/apps/gerbview.png | Bin 0 -> 5333 bytes .../linux/mime/icons/hicolor/48x48/apps/kicad.png | Bin 0 -> 2178 bytes .../icons/hicolor/48x48/apps/pcbcalculator.png | Bin 0 -> 3256 bytes .../linux/mime/icons/hicolor/48x48/apps/pcbnew.png | Bin 0 -> 3366 bytes .../application-x-bitmap2component-project.png | Bin 0 -> 3598 bytes .../mimetypes/application-x-cvpcb-project.png | Bin 0 -> 2435 bytes .../mimetypes/application-x-eeschema-project.png | Bin 0 -> 3347 bytes .../mimetypes/application-x-gerbview-project.png | Bin 0 -> 5333 bytes .../mimetypes/application-x-kicad-project.png | Bin 0 -> 2178 bytes .../application-x-pcbcalculator-project.png | Bin 0 -> 3256 bytes .../mimetypes/application-x-pcbnew-project.png | Bin 0 -> 3366 bytes .../linux/mime/icons/hicolor/scalable/apps/3d.svg | 32 + .../hicolor/scalable/apps/bitmap2component.svg | 1125 +++++ .../mime/icons/hicolor/scalable/apps/cvpcb.svg | 41 + .../mime/icons/hicolor/scalable/apps/eeschema.svg | 394 ++ .../mime/icons/hicolor/scalable/apps/gerbview.svg | 109 + .../mime/icons/hicolor/scalable/apps/kicad.svg | 276 ++ .../icons/hicolor/scalable/apps/pcbcalculator.svg | 1100 +++++ .../mime/icons/hicolor/scalable/apps/pcbnew.svg | 1554 +++++++ .../mimetypes/application-x-3d-project.svg | 32 + .../application-x-bitmap2component-project.svg | 1125 +++++ .../mimetypes/application-x-cvpcb-project.svg | 41 + .../mimetypes/application-x-eeschema-project.svg | 394 ++ .../mimetypes/application-x-gerbview-project.svg | 109 + .../mimetypes/application-x-kicad-project.svg | 276 ++ .../application-x-pcbcalculator-project.svg | 1100 +++++ .../mimetypes/application-x-pcbnew-project.svg | 1554 +++++++ resources/linux/mime/mime/packages/kicad.xml | 21 + .../mime/mimelnk/application/x-kicad-pcb.desktop | 8 + .../mimelnk/application/x-kicad-project.desktop | 7 + .../mimelnk/application/x-kicad-schematic.desktop | 7 + rules | 27 + scripting/build_tools/extract_docstrings.py | 405 ++ scripting/build_tools/fix_swig_imports.py | 77 + scripting/dlist.i | 67 + scripting/kicad.i | 160 + scripting/kicadplugins.i | 271 ++ scripting/python_scripting.cpp | 364 ++ scripting/python_scripting.h | 62 + scripting/wx.i | 301 ++ scripting/wx_python_helpers.cpp | 196 + scripting/wx_python_helpers.h | 18 + scripts/kicad-install.sh | 408 ++ scripts/lib_convert.py | 51 + scripts/library-repos-install.bat | 89 + scripts/library-repos-install.sh | 273 ++ scripts/osx_build_wx.sh | 166 + scripts/osx_fixbundle.sh | 129 + scripts/test_kicad_plugin.py | 85 + scripts/test_plugin.py | 48 + template/CMakeLists.txt | 10 + template/gost_landscape.kicad_wks | 87 + template/gost_portrait.kicad_wks | 87 + template/kicad.pro | 65 + template/pagelayout_default.kicad_wks | 34 + template/pagelayout_logo.kicad_wks | 191 + tools/CMakeLists.txt | 80 + tools/Info.plist | 0 tools/checkcoding.py | 134 + tools/container_test.cpp | 132 + tools/property_tree.cpp | 91 + tools/test-nm-biu-to-ascii-mm-round-tripping.cpp | 120 + uncrustify.cfg | 1612 +++++++ utils/CMakeLists.txt | 2 + utils/idftools/CMakeLists.txt | 36 + utils/idftools/dxf2idf.cpp | 460 ++ utils/idftools/dxf2idf.h | 102 + utils/idftools/dxf2idfmain.cpp | 190 + utils/idftools/idf2vrml.cpp | 990 +++++ utils/idftools/idf_common.cpp | 1387 ++++++ utils/idftools/idf_common.h | 711 +++ utils/idftools/idf_cylinder.cpp | 681 +++ .../idf_examples/Arduino_MEGA_2560-Rev3.emn | 149 + .../idf_examples/Arduino_MEGA_2560-Rev3.emp | 4 + utils/idftools/idf_examples/idf_example.emn | 269 ++ utils/idftools/idf_examples/idf_example.emp | 69 + utils/idftools/idf_examples/test_donut.emn | 45 + utils/idftools/idf_examples/test_donut.emp | 5 + utils/idftools/idf_examples/test_idf2.emn | 71 + utils/idftools/idf_examples/test_idf2.emp | 290 ++ utils/idftools/idf_helpers.cpp | 323 ++ utils/idftools/idf_helpers.h | 175 + utils/idftools/idf_outlines.cpp | 3614 +++++++++++++++ utils/idftools/idf_outlines.h | 771 ++++ utils/idftools/idf_parser.cpp | 4288 ++++++++++++++++++ utils/idftools/idf_parser.h | 721 +++ utils/idftools/idf_rect.cpp | 433 ++ utils/idftools/vrml_layer.cpp | 1788 ++++++++ utils/idftools/vrml_layer.h | 461 ++ 180 files changed, 55929 insertions(+), 1 deletion(-) create mode 100644 CTestConfig.cmake create mode 100644 Doxyfile create mode 100644 copyright.h create mode 100644 polygon/CMakeLists.txt create mode 100644 polygon/PolyLine.cpp create mode 100644 polygon/PolyLine.h create mode 100644 polygon/SutherlandHodgmanClipPoly.h create mode 100644 polygon/clipper.cpp create mode 100644 polygon/clipper.hpp create mode 100644 polygon/determine_if_point_inside_polygon.odt create mode 100644 polygon/math_for_graphics.cpp create mode 100644 polygon/math_for_graphics.h create mode 100644 polygon/poly2tri/common/shapes.cc create mode 100644 polygon/poly2tri/common/shapes.h create mode 100644 polygon/poly2tri/common/utils.h create mode 100644 polygon/poly2tri/poly2tri.h create mode 100644 polygon/poly2tri/sweep/advancing_front.cc create mode 100644 polygon/poly2tri/sweep/advancing_front.h create mode 100644 polygon/poly2tri/sweep/cdt.cc create mode 100644 polygon/poly2tri/sweep/cdt.h create mode 100644 polygon/poly2tri/sweep/sweep.cc create mode 100644 polygon/poly2tri/sweep/sweep.h create mode 100644 polygon/poly2tri/sweep/sweep_context.cc create mode 100644 polygon/poly2tri/sweep/sweep_context.h create mode 100644 polygon/polygon_test_point_inside.cpp create mode 100644 polygon/polygon_test_point_inside.h create mode 100644 polygon/polygons_defs.h create mode 100644 potrace/AUTHORS create mode 100644 potrace/CMakeLists.txt create mode 100644 potrace/auxiliary.h create mode 100644 potrace/bitmap.h create mode 100644 potrace/bitmap_io.cpp create mode 100644 potrace/bitmap_io.h create mode 100644 potrace/bitops.h create mode 100644 potrace/curve.cpp create mode 100644 potrace/curve.h create mode 100644 potrace/decompose.cpp create mode 100644 potrace/decompose.h create mode 100644 potrace/greymap.cpp create mode 100644 potrace/greymap.h create mode 100644 potrace/lists.h create mode 100644 potrace/platform.h create mode 100644 potrace/potrace_version.h create mode 100644 potrace/potracelib.cpp create mode 100644 potrace/potracelib.h create mode 100644 potrace/progress.h create mode 100644 potrace/render.cpp create mode 100644 potrace/render.h create mode 100644 potrace/trace.cpp create mode 100644 potrace/trace.h create mode 100644 qa/CMakeLists.txt create mode 100644 qa/data/complex_hierarchy.kicad_pcb create mode 100644 qa/test.py create mode 100644 qa/testcases/test_000_qa_works.py create mode 100644 qa/testcases/test_001_pcb_load.py create mode 100644 qa/testcases/test_002_board_class.py create mode 100644 resources/linux/mime/applications/bitmap2component.desktop create mode 100644 resources/linux/mime/applications/cvpcb.desktop create mode 100644 resources/linux/mime/applications/eeschema.desktop create mode 100644 resources/linux/mime/applications/gerbview.desktop create mode 100644 resources/linux/mime/applications/kicad.desktop create mode 100644 resources/linux/mime/applications/pcbcalculator.desktop create mode 100644 resources/linux/mime/applications/pcbnew.desktop create mode 100644 resources/linux/mime/icons/hicolor/128x128/apps/3d.png create mode 100644 resources/linux/mime/icons/hicolor/128x128/apps/bitmap2component.png create mode 100644 resources/linux/mime/icons/hicolor/128x128/apps/cvpcb.png create mode 100644 resources/linux/mime/icons/hicolor/128x128/apps/eeschema.png create mode 100644 resources/linux/mime/icons/hicolor/128x128/apps/gerbview.png create mode 100644 resources/linux/mime/icons/hicolor/128x128/apps/kicad.png create mode 100644 resources/linux/mime/icons/hicolor/128x128/apps/pcbcalculator.png create mode 100644 resources/linux/mime/icons/hicolor/128x128/apps/pcbnew.png create mode 100644 resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-3d-project.png create mode 100644 resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-bitmap2component-project.png create mode 100644 resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-cvpcb-project.png create mode 100644 resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-eeschema-project.png create mode 100644 resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-gerbview-project.png create mode 100644 resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-kicad-project.png create mode 100644 resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-pcbcalculator-project.png create mode 100644 resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-pcbnew-project.png create mode 100644 resources/linux/mime/icons/hicolor/16x16/mimetypes/application-x-kicad-project.png create mode 100644 resources/linux/mime/icons/hicolor/22x22/apps/kicad.png create mode 100644 resources/linux/mime/icons/hicolor/22x22/mimetypes/application-x-kicad-project.png create mode 100644 resources/linux/mime/icons/hicolor/24x24/apps/kicad.png create mode 100644 resources/linux/mime/icons/hicolor/24x24/mimetypes/application-x-kicad-project.png create mode 100644 resources/linux/mime/icons/hicolor/32x32/apps/kicad.png create mode 100644 resources/linux/mime/icons/hicolor/32x32/mimetypes/application-x-kicad-project.png create mode 100644 resources/linux/mime/icons/hicolor/48x48/apps/bitmap2component.png create mode 100644 resources/linux/mime/icons/hicolor/48x48/apps/cvpcb.png create mode 100644 resources/linux/mime/icons/hicolor/48x48/apps/eeschema.png create mode 100644 resources/linux/mime/icons/hicolor/48x48/apps/gerbview.png create mode 100644 resources/linux/mime/icons/hicolor/48x48/apps/kicad.png create mode 100644 resources/linux/mime/icons/hicolor/48x48/apps/pcbcalculator.png create mode 100644 resources/linux/mime/icons/hicolor/48x48/apps/pcbnew.png create mode 100644 resources/linux/mime/icons/hicolor/48x48/mimetypes/application-x-bitmap2component-project.png create mode 100644 resources/linux/mime/icons/hicolor/48x48/mimetypes/application-x-cvpcb-project.png create mode 100644 resources/linux/mime/icons/hicolor/48x48/mimetypes/application-x-eeschema-project.png create mode 100644 resources/linux/mime/icons/hicolor/48x48/mimetypes/application-x-gerbview-project.png create mode 100644 resources/linux/mime/icons/hicolor/48x48/mimetypes/application-x-kicad-project.png create mode 100644 resources/linux/mime/icons/hicolor/48x48/mimetypes/application-x-pcbcalculator-project.png create mode 100644 resources/linux/mime/icons/hicolor/48x48/mimetypes/application-x-pcbnew-project.png create mode 100644 resources/linux/mime/icons/hicolor/scalable/apps/3d.svg create mode 100644 resources/linux/mime/icons/hicolor/scalable/apps/bitmap2component.svg create mode 100644 resources/linux/mime/icons/hicolor/scalable/apps/cvpcb.svg create mode 100644 resources/linux/mime/icons/hicolor/scalable/apps/eeschema.svg create mode 100644 resources/linux/mime/icons/hicolor/scalable/apps/gerbview.svg create mode 100644 resources/linux/mime/icons/hicolor/scalable/apps/kicad.svg create mode 100644 resources/linux/mime/icons/hicolor/scalable/apps/pcbcalculator.svg create mode 100644 resources/linux/mime/icons/hicolor/scalable/apps/pcbnew.svg create mode 100644 resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-3d-project.svg create mode 100644 resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-bitmap2component-project.svg create mode 100644 resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-cvpcb-project.svg create mode 100644 resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-eeschema-project.svg create mode 100644 resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-gerbview-project.svg create mode 100644 resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-kicad-project.svg create mode 100644 resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-pcbcalculator-project.svg create mode 100644 resources/linux/mime/icons/hicolor/scalable/mimetypes/application-x-pcbnew-project.svg create mode 100644 resources/linux/mime/mime/packages/kicad.xml create mode 100644 resources/linux/mime/mimelnk/application/x-kicad-pcb.desktop create mode 100644 resources/linux/mime/mimelnk/application/x-kicad-project.desktop create mode 100644 resources/linux/mime/mimelnk/application/x-kicad-schematic.desktop create mode 100644 rules create mode 100644 scripting/build_tools/extract_docstrings.py create mode 100644 scripting/build_tools/fix_swig_imports.py create mode 100644 scripting/dlist.i create mode 100644 scripting/kicad.i create mode 100644 scripting/kicadplugins.i create mode 100644 scripting/python_scripting.cpp create mode 100644 scripting/python_scripting.h create mode 100644 scripting/wx.i create mode 100644 scripting/wx_python_helpers.cpp create mode 100644 scripting/wx_python_helpers.h create mode 100755 scripts/kicad-install.sh create mode 100755 scripts/lib_convert.py create mode 100644 scripts/library-repos-install.bat create mode 100755 scripts/library-repos-install.sh create mode 100755 scripts/osx_build_wx.sh create mode 100755 scripts/osx_fixbundle.sh create mode 100755 scripts/test_kicad_plugin.py create mode 100755 scripts/test_plugin.py create mode 100644 template/CMakeLists.txt create mode 100644 template/gost_landscape.kicad_wks create mode 100644 template/gost_portrait.kicad_wks create mode 100644 template/kicad.pro create mode 100644 template/pagelayout_default.kicad_wks create mode 100644 template/pagelayout_logo.kicad_wks create mode 100644 tools/CMakeLists.txt create mode 100644 tools/Info.plist create mode 100755 tools/checkcoding.py create mode 100644 tools/container_test.cpp create mode 100644 tools/property_tree.cpp create mode 100644 tools/test-nm-biu-to-ascii-mm-round-tripping.cpp create mode 100644 uncrustify.cfg create mode 100644 utils/CMakeLists.txt create mode 100644 utils/idftools/CMakeLists.txt create mode 100644 utils/idftools/dxf2idf.cpp create mode 100644 utils/idftools/dxf2idf.h create mode 100644 utils/idftools/dxf2idfmain.cpp create mode 100644 utils/idftools/idf2vrml.cpp create mode 100644 utils/idftools/idf_common.cpp create mode 100644 utils/idftools/idf_common.h create mode 100644 utils/idftools/idf_cylinder.cpp create mode 100644 utils/idftools/idf_examples/Arduino_MEGA_2560-Rev3.emn create mode 100644 utils/idftools/idf_examples/Arduino_MEGA_2560-Rev3.emp create mode 100644 utils/idftools/idf_examples/idf_example.emn create mode 100644 utils/idftools/idf_examples/idf_example.emp create mode 100644 utils/idftools/idf_examples/test_donut.emn create mode 100644 utils/idftools/idf_examples/test_donut.emp create mode 100644 utils/idftools/idf_examples/test_idf2.emn create mode 100644 utils/idftools/idf_examples/test_idf2.emp create mode 100644 utils/idftools/idf_helpers.cpp create mode 100644 utils/idftools/idf_helpers.h create mode 100644 utils/idftools/idf_outlines.cpp create mode 100644 utils/idftools/idf_outlines.h create mode 100644 utils/idftools/idf_parser.cpp create mode 100644 utils/idftools/idf_parser.h create mode 100644 utils/idftools/idf_rect.cpp create mode 100644 utils/idftools/vrml_layer.cpp create mode 100644 utils/idftools/vrml_layer.h diff --git a/AUTHORS.txt b/AUTHORS.txt index f447c6d..aa27d0b 100644 --- a/AUTHORS.txt +++ b/AUTHORS.txt @@ -40,6 +40,8 @@ Mario Luzeiro Mateusz Skowroński See also CHANGELOG.txt for contributors. +== eSim Contributors +Saurabh Bansode == Document Writers Jean-Pierre Charras 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 , where is the value of +# the FILE_VERSION_FILTER tag, and 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 , where +# is the value of the INPUT_FILTER tag, and 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 +# +# Qt Help Project / Custom Filters. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's +# filter section matches. +# +# Qt Help Project / Filter Attributes. + +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 + * 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 +#include +#include + +#include +#include // KiROUND + +#include +#include +#include +#include +#include + + +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 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* 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 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 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= 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 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 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 cr; + cr.reserve( n_cont ); + + for( int icont = 0; icont 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 + * Copyright (C) 2008-2013 Wayne Stambaugh + * 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 + +#include // for wxPoint definition +#include // for LAYER_NUM definition +#include // for EDA_RECT definition + +#include // 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 m_cornersList; // array of points for corners +public: + CPOLYGONS_LIST() {}; + + CPolyPt& operator [](int aIdx) { return m_cornersList[aIdx]; } + + // Accessor: + const std::vector & 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 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* 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 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 +#include + +#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 pointVector; +typedef std::vector::iterator pointIterator; +typedef std::vector::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 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 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 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 > BoundaryRight; + typedef BoundaryHor > BoundaryTop; + typedef BoundaryVert > BoundaryLeft; + typedef BoundaryHor > BoundaryBottom; + + // Next typedefs define the four stages. First template parameter is the boundary, + // second template parameter is the next stage. + typedef ClipStage ClipBottom; + typedef ClipStage ClipLeft; + typedef ClipStage ClipTop; + typedef ClipStage 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 +#include +#include +#include +#include +#include +#include +#include + +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(val - 0.5); + else return static_cast(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 +#include +#include +#include +#include +#include +#include +#include +#include + +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 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 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 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 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 Binary files /dev/null and b/polygon/determine_if_point_inside_polygon.odt 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 + +#include +#include +#include +#include +#include + +#include +#include + +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=xi && xx>xf) || (xx<=xi && xxyi && yy>yf) || (yy=yi && yy>yf) || (yy<=yi && yy 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 + +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 +#include +#include +#include + +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_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 +#include + +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
+ * Positive if CCW
+ * Negative if CW
+ * 0 if collinear
+ *
+ * A[P1,P2,P3]  =  (x1*y2 - y1*x2) + (x2*y3 - y2*x3) + (x3*y1 - y3*x1)
+ *              =  (x1-x3)*(y2-y3) - (y1-y3)*(x2-x3)
+ * 
+ */ +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 polyline) +{ + sweep_context_ = new SweepContext(polyline); + sweep_ = new Sweep; +} + +void CDT::AddHole(std::vector polyline) +{ + sweep_context_->AddHole(polyline); +} + +void CDT::AddPoint(Point* point) { + sweep_context_->AddPoint(point); +} + +void CDT::Triangulate() +{ + sweep_->Triangulate(*sweep_context_); +} + +std::vector CDT::GetTriangles() +{ + return sweep_context_->GetTriangles(); +} + +std::list 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 + * + */ + +namespace p2t { + +class CDT +{ +public: + + /** + * Constructor - add polyline with non repeating points + * + * @param polyline + */ + CDT(std::vector polyline); + + /** + * Destructor - clean up memory + */ + ~CDT(); + + /** + * Add a hole + * + * @param polyline + */ + void AddHole(std::vector 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 GetTriangles(); + + /** + * Get triangle map + */ + std::list 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 +#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 hln, thahlen@gmail.com + */ + +#ifndef SWEEP_H +#define SWEEP_H + +#include + +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); + + /** + * Requirement:
+ * 1. a,b and c form a triangle.
+ * 2. a and d is know to be on opposite side of bc
+ *
+   *                a
+   *                +
+   *               / \
+   *              /   \
+   *            b/     \c
+   *            +-------+
+   *           /    d    \
+   *          /           \
+   * 
+ * Fact: d has to be in area B to have a chance to be inside the circle formed by + * a,b and c
+ * d is outside B if orient2d(a,b,d) or orient2d(c,a,d) is CW
+ * 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 + *
+   *       n2                    n2
+   *  P +-----+             P +-----+
+   *    | t  /|               |\  t |
+   *    |   / |               | \   |
+   *  n1|  /  |n3           n1|  \  |n3
+   *    | /   |    after CW   |   \ |
+   *    |/ oT |               | oT \|
+   *    +-----+ oP            +-----+
+   *       n4                    n4
+   * 
+ */ + 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.
+ * 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
+ * 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 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 +#include "advancing_front.h" + +namespace p2t { + +SweepContext::SweepContext(std::vector 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 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 SweepContext::GetTriangles() +{ + return triangles_; +} + +std::list 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 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 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 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 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 +#include +#include + +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 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 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 polyline); + +void AddPoint(Point* point); + +AdvancingFront* front(); + +void MeshClean(Triangle& triangle); + +std::vector GetTriangles(); +std::list GetMap(); + +std::vector 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 triangles_; +std::list map_; +std::vector 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 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 +#include +#include + +/* 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 + +// 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 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_SET; + +/** + * KI_POLY_POINT defines a point for boost::polygon. + * KI_POLY_POINT store x and y coordinates (int) + */ +typedef bpl::point_data 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 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_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 (author) + TA1 Tor Andersson 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 +#endif + +/* ---------------------------------------------------------------------- */ +/* point arithmetic */ + +#include + +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 +#endif + +#include +#include + +/* The bitmap type is defined in potracelib.h */ +#include + +/* 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 + +#include + +#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=0; y-- ) + { + for( x = 0; x 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 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=0; y-- ) + { + for( x = 0; x=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=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> 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> (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> 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=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=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=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=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> ( 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; xxw / 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 +#include + +/* 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 +#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 +#include +#include + +#include +#include +#include + +#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 + +/* 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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +/* ---------------------------------------------------------------------- */ +/* 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; yh; 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; yy1; y++ ) + { + for( i = imin; i> 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( xhipriv->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; kpriv->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; kpriv->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=0; y-- ) + { + for( x = 0; xw; 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 +#include + +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 +#include +#include +#include + +#include + +#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; iw * 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=0; y-- ) + { + for( x = 0; x=0; y-- ) + { + for( x = 0; x=0; y-- ) + { + for( i = 0; i> 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=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=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> 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> 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> (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> 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=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=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=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=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; xw; 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; xxw / 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 + +/* 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 +#endif + +/* in Windows, set all file i/o to binary */ +#ifdef __MINGW32__ +#include +unsigned int _CRT_fmode = _O_BINARY; +#endif + +#ifdef __CYGWIN__ +#include +#include +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 +#include + +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include +#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, ¶m_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 +#include +#include +#include + +#include +#include +#include + +/* ---------------------------------------------------------------------- */ +/* 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; igm, i, y, -b ); + } + } + else + { + for( i = x; igm, 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=tn || (ix1,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 + +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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#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 && blen; + 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; ipt[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= 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 ilon, n, int ); + + /* determine pivot points: for each i, let pivk[i] be the furthest k + * such that all j with 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'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=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; ilon[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; i0; 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; iQ[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; ivertex[i]; + curve->vertex[i] = curve->vertex[j]; + curve->vertex[j] = tmp; + } + } + + /* examine each vertex and find its best fit */ + for( i = 0; ivertex[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 icurve.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; icurve.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; icurve.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; iocurve.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 +#include + +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 Binary files /dev/null and b/resources/linux/mime/icons/hicolor/128x128/apps/3d.png 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 Binary files /dev/null and b/resources/linux/mime/icons/hicolor/128x128/apps/bitmap2component.png 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 Binary files /dev/null and b/resources/linux/mime/icons/hicolor/128x128/apps/cvpcb.png 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 Binary files /dev/null and b/resources/linux/mime/icons/hicolor/128x128/apps/eeschema.png 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 Binary files /dev/null and b/resources/linux/mime/icons/hicolor/128x128/apps/gerbview.png 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 Binary files /dev/null and b/resources/linux/mime/icons/hicolor/128x128/apps/kicad.png 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 Binary files /dev/null and b/resources/linux/mime/icons/hicolor/128x128/apps/pcbcalculator.png 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 Binary files /dev/null and b/resources/linux/mime/icons/hicolor/128x128/apps/pcbnew.png 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 Binary files /dev/null and b/resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-3d-project.png 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 Binary files /dev/null and b/resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-bitmap2component-project.png 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 Binary files /dev/null and b/resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-cvpcb-project.png 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 Binary files /dev/null and b/resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-eeschema-project.png 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 Binary files /dev/null and b/resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-gerbview-project.png 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 Binary files /dev/null and b/resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-kicad-project.png 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 Binary files /dev/null and b/resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-pcbcalculator-project.png 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 Binary files /dev/null and b/resources/linux/mime/icons/hicolor/128x128/mimetypes/application-x-pcbnew-project.png 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 Binary files /dev/null and b/resources/linux/mime/icons/hicolor/16x16/mimetypes/application-x-kicad-project.png 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 Binary files /dev/null and b/resources/linux/mime/icons/hicolor/22x22/apps/kicad.png 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 Binary files /dev/null and b/resources/linux/mime/icons/hicolor/22x22/mimetypes/application-x-kicad-project.png 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 Binary files /dev/null and b/resources/linux/mime/icons/hicolor/24x24/apps/kicad.png 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 Binary files /dev/null and b/resources/linux/mime/icons/hicolor/24x24/mimetypes/application-x-kicad-project.png 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 Binary files /dev/null and b/resources/linux/mime/icons/hicolor/32x32/apps/kicad.png 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 Binary files /dev/null and b/resources/linux/mime/icons/hicolor/32x32/mimetypes/application-x-kicad-project.png 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 Binary files /dev/null and b/resources/linux/mime/icons/hicolor/48x48/apps/bitmap2component.png 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 Binary files /dev/null and b/resources/linux/mime/icons/hicolor/48x48/apps/cvpcb.png 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 Binary files /dev/null and b/resources/linux/mime/icons/hicolor/48x48/apps/eeschema.png 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 Binary files /dev/null and b/resources/linux/mime/icons/hicolor/48x48/apps/gerbview.png 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 Binary files /dev/null and b/resources/linux/mime/icons/hicolor/48x48/apps/kicad.png 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 Binary files /dev/null and b/resources/linux/mime/icons/hicolor/48x48/apps/pcbcalculator.png 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 Binary files /dev/null and b/resources/linux/mime/icons/hicolor/48x48/apps/pcbnew.png 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 Binary files /dev/null and b/resources/linux/mime/icons/hicolor/48x48/mimetypes/application-x-bitmap2component-project.png 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 Binary files /dev/null and b/resources/linux/mime/icons/hicolor/48x48/mimetypes/application-x-cvpcb-project.png 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 Binary files /dev/null and b/resources/linux/mime/icons/hicolor/48x48/mimetypes/application-x-eeschema-project.png 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 Binary files /dev/null and b/resources/linux/mime/icons/hicolor/48x48/mimetypes/application-x-gerbview-project.png 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 Binary files /dev/null and b/resources/linux/mime/icons/hicolor/48x48/mimetypes/application-x-kicad-project.png 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 Binary files /dev/null and b/resources/linux/mime/icons/hicolor/48x48/mimetypes/application-x-pcbcalculator-project.png 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 Binary files /dev/null and b/resources/linux/mime/icons/hicolor/48x48/mimetypes/application-x-pcbnew-project.png 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + 3 + + + + D + + + 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 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + 3 + + + + D + + + 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 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 @@ + + + + + KiCad Project + Projet KiCad + + + + + KiCad Schematic + Schéma électronique KiCad + + + + + KiCad PCB + Circuit imprimé KiCad + + + 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\\Application Data\Bazaar\2.0\rules +# Win7 C:\Users\\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_` 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_` 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 + * 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 + * 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 +%include +%include +%include + +/* 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 + #include + #include + #include + #include + #include + #include + #include + #include + + #include + #include + #include + #include + #include + #include +%} + +/* all the wx wrappers for wxString, wxPoint, wxRect, wxChar .. */ +%include + +/* 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 +%include +%include +%include +%include +%include +%include +%include +%include +%include +%include + +/* special iteration wrapper for DLIST objects */ +%include "dlist.i" + +/* std template mappings */ +%template(intVector) std::vector; +%template(str_utf8_Map) std::map< std::string,UTF8 >; + +// wrapper of BASE_SEQ (see typedef std::vector BASE_SEQ;) +%template(base_seqVect) std::vector; + +// TODO: wrapper of BASE_SET (see std::bitset 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 +%include + +%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 + * 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 + * 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 +#include +#include + +#include +#include +#include +#include +#include + +/* 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 +#ifndef NO_WXPYTHON_EXTENSION_HEADERS +#ifdef KICAD_SCRIPTING_WXPYTHON + #include +#endif +#endif + +#include +#include + +/* 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 + * 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 +%} + +// 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; iAdd(*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; 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 + * 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 +#include +#include +#include + + +#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 +#include +#include +#include + + +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: 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 " + echo " where 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 " + } +} + + +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=. /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 /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 " + echo " where 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 /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 [makeopts]" + echo " wxWidgets/wxPython source folder" + echo " Destination folder" + echo " KiCad folder" + echo " 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 + +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 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=. /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=. /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 []" ) + 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 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 +#include +#include +#include +#include +#include + +#define TEST_NODES 100000000 + + +//typedef std::vector EDA_ITEMV; +//typedef std::deque EDA_ITEMV; +typedef boost::ptr_vector 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 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; iType() == -22 ) + { + printf( "never this\n" ); + break; + } + } + + vIterateStop = GetRunningMicroSecs(); + +#if 0 + for( int i=0; iNext() ) + { + 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 + * 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 +#include +#include +#include +#include +#include + + +void usage() +{ + fprintf( stderr, "Usage: parser_gen \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 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 +#include +#include +#include +#include +#include +#include + + +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>=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();' +sp_angle_paren = ignore # ignore/add/remove/force + +# Add or remove space between '<>' and a word as in 'List 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 >' (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 ( ; ; ). +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 +#include +#include +#include + +// 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::iterator bo; + std::list::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::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 +#include +#include + +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 +#include +#include +#include +#include +#include + +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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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& 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* drills = &board.GetBoardDrills(); + + std::list::const_iterator sd = drills->begin(); + std::list::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::iterator sseg; + std::list::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& 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::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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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& 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::iterator bl = aLines.begin(); + std::list::iterator el = aLines.end(); + std::list::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 +#include +#include +#include +#include + +// 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& 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 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::iterator begin( void ) + { + return outline.begin(); + } + + /** + * Function end + * returns the end() iterator of the internal segment list + */ + std::list::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 +#include +#include +#include +#include +#include +#include +#include +#include + +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 +#include +#include + +#include +#include + +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 +#include +#include +#include + +/** + * 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 +#include +#include +#include + +#include +#include +#include + +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::iterator bo; + std::list::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::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 +#include +#include +#include +#include + +#include + +/* + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include + +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::iterator olAs = opA->begin(); + std::list::iterator olAe = opA->end(); + std::list::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::iterator its = olnRoute.begin(); + std::list::iterator ite = olnRoute.end(); + + while( its != ite ) + { + (*its)->SetUnit( aUnit ); + ++its; + } + + } while( 0 ); + + do + { + std::list::iterator its = olnPlace.begin(); + std::list::iterator ite = olnPlace.end(); + + while( its != ite ) + { + (*its)->SetUnit( aUnit ); + ++its; + } + + } while( 0 ); + + do + { + std::list::iterator its = olnRouteKeepout.begin(); + std::list::iterator ite = olnRouteKeepout.end(); + + while( its != ite ) + { + (*its)->SetUnit( aUnit ); + ++its; + } + + } while( 0 ); + + do + { + std::list::iterator its = olnViaKeepout.begin(); + std::list::iterator ite = olnViaKeepout.end(); + + while( its != ite ) + { + (*its)->SetUnit( aUnit ); + ++its; + } + + } while( 0 ); + + do + { + std::list::iterator its = olnPlaceKeepout.begin(); + std::list::iterator ite = olnPlaceKeepout.end(); + + while( its != ite ) + { + (*its)->SetUnit( aUnit ); + ++its; + } + + } while( 0 ); + + do + { + std::multimap::iterator its = olnGroup.begin(); + std::multimap::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::iterator its = compOutlines.begin(); + std::map::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::iterator its = comments.begin(); + std::list::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::iterator its = comments.begin(); + std::list::iterator ite = comments.end(); + + while( its != ite ) + { + op->AddComment( *its ); + ++its; + } + } + + if( olnOther.insert( pair(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::iterator its = comments.begin(); + std::list::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::iterator its = comments.begin(); + std::list::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::iterator its = comments.begin(); + std::list::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::iterator its = comments.begin(); + std::list::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::iterator its = comments.begin(); + std::list::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::iterator its = comments.begin(); + std::list::iterator ite = comments.end(); + + while( its != ite ) + { + op->AddComment( *its ); + ++its; + } + } + + olnGroup.insert( pair(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::iterator its = comments.begin(); + std::list::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::iterator its = comments.begin(); + std::list::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::iterator its = comments.begin(); + std::list::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::iterator its = comments.begin(); + std::list::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( 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::iterator its = olnOther.begin(); + std::map::iterator ite = olnOther.end(); + + while(its != ite ) + { + its->second->writeData( brd ); + ++its; + } + + } while( 0 ); + + // ROUTE outlines + do + { + std::list::iterator its = olnRoute.begin(); + std::list::iterator ite = olnRoute.end(); + + while( its != ite ) + { + (*its)->writeData( brd ); + ++its; + } + + } while( 0 ); + + // PLACEMENT outlines + do + { + std::list::iterator its = olnPlace.begin(); + std::list::iterator ite = olnPlace.end(); + + while( its != ite ) + { + (*its)->writeData( brd ); + ++its; + } + + } while( 0 ); + + // ROUTE KEEPOUT outlines + do + { + std::list::iterator its = olnRouteKeepout.begin(); + std::list::iterator ite = olnRouteKeepout.end(); + + while( its != ite ) + { + (*its)->writeData( brd ); + ++its; + } + + } while( 0 ); + + // VIA KEEPOUT outlines + do + { + std::list::iterator its = olnViaKeepout.begin(); + std::list::iterator ite = olnViaKeepout.end(); + + while( its != ite ) + { + (*its)->writeData( brd ); + ++its; + } + + } while( 0 ); + + // PLACE KEEPOUT outlines + do + { + std::list::iterator its = olnPlaceKeepout.begin(); + std::list::iterator ite = olnPlaceKeepout.end(); + + while( its != ite ) + { + (*its)->writeData( brd ); + ++its; + } + + } while( 0 ); + + // PLACEMENT GROUP outlines + do + { + std::multimap::iterator its = olnGroup.begin(); + std::multimap::iterator ite = olnGroup.end(); + + while( its != ite ) + { + its->second->writeData( brd ); + ++its; + } + + } while( 0 ); + + // Drilled holes + do + { + std::list::iterator itds = drillComments.begin(); + std::list::iterator itde = drillComments.end(); + + while( itds != itde ) + { + brd << "# " << *itds << "\n"; + ++itds; + } + + brd << ".DRILLED_HOLES\n"; + + std::list::iterator itbs = board_drills.begin(); + std::list::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::iterator itncs = noteComments.begin(); + std::list::iterator itnce = noteComments.end(); + + while( itncs != itnce ) + { + brd << "# " << *itncs << "\n"; + ++itncs; + } + + brd << ".NOTES\n"; + + std::list::iterator itns = notes.begin(); + std::list::iterator itne = notes.end(); + + while( itns != itne ) + { + (*itns)->writeNote( brd, unit ); + ++itns; + } + + brd << ".END_NOTES\n\n"; + + } + + // Placement + if( !components.empty() ) + { + std::list::iterator itpcs = placeComments.begin(); + std::list::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::iterator sp = board_drills.begin(); + std::list::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::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::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::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 + ( 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::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::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::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( 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(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::iterator ds = board_drills.begin(); + std::list::iterator de = board_drills.end(); + + while( ds != de ) + { + delete *ds; + ++ds; + } + + board_drills.clear(); + } while(0); + + + // delete components + do + { + std::map::iterator cs = components.begin(); + std::map::iterator ce = components.end(); + + while( cs != ce ) + { + delete cs->second; + ++cs; + } + + components.clear(); + } while(0); + + + // delete component outlines + do + { + std::map::iterator cs = compOutlines.begin(); + std::map::iterator ce = compOutlines.end(); + + while( cs != ce ) + { + delete cs->second; + ++cs; + } + + compOutlines.clear(); + } while(0); + + + // delete OTHER outlines + do + { + std::map::iterator os = olnOther.begin(); + std::map::iterator oe = olnOther.end(); + + while( os != oe ) + { + delete os->second; + ++os; + } + + olnOther.clear(); + } while(0); + + + // delete ROUTE outlines + do + { + std::list::iterator os = olnRoute.begin(); + std::list::iterator oe = olnRoute.end(); + + while( os != oe ) + { + delete *os; + ++os; + } + + olnRoute.clear(); + } while(0); + + + // delete PLACE outlines + do + { + std::list::iterator os = olnPlace.begin(); + std::list::iterator oe = olnPlace.end(); + + while( os != oe ) + { + delete *os; + ++os; + } + + olnPlace.clear(); + } while(0); + + + // delete ROUTE KEEPOUT outlines + do + { + std::list::iterator os = olnRouteKeepout.begin(); + std::list::iterator oe = olnRouteKeepout.end(); + + while( os != oe ) + { + delete *os; + ++os; + } + + olnRouteKeepout.clear(); + } while(0); + + + // delete VIA KEEPOUT outlines + do + { + std::list::iterator os = olnViaKeepout.begin(); + std::list::iterator oe = olnViaKeepout.end(); + + while( os != oe ) + { + delete *os; + ++os; + } + + olnViaKeepout.clear(); + } while(0); + + + // delete PLACEMENT KEEPOUT outlines + do + { + std::list::iterator os = olnPlaceKeepout.begin(); + std::list::iterator oe = olnPlaceKeepout.end(); + + while( os != oe ) + { + delete *os; + ++os; + } + + olnPlaceKeepout.clear(); + } while(0); + + + // delete PLACEMENT GROUP outlines + do + { + std::multimap::iterator os = olnGroup.begin(); + std::multimap::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*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 + +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 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 olnOther; + // ROUTE outlines + std::list olnRoute; + // PLACEMENT outlines + std::list olnPlace; + // ROUTE KEEPOUT outlines + std::list olnRouteKeepout; + // VIA KEEPOUT outlines + std::list olnViaKeepout; + // PLACE KEEPOUT outlines + std::list olnPlaceKeepout; + // PLACEMENT GROUP outlines + std::multimap 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*const GetOtherOutlines( void ); + + /// XXX - TO BE IMPLEMENTED + // + // SetBoardOutlineOwner() + // + // AddDrillComment + // AddPlacementComment + // GetDrillComments() + // GetPlacementComments() + // ClearDrillComments() + // ClearPlacementComments() + // AddNoteComment + // GetNoteComments + // AddNote + // + // [IMPLEMENTED] const std::map*const GetOtherOutlines( void ) + // size_t GetOtherOutlinesSize() + // OTHER_OUTLINE* AddOtherOutline( OTHER_OUTLINE* aOtherOutline ) + // bool DelOtherOutline( int aIndex ) + // bool DelOtherOutline( OTHER_OUTLINE* aOtherOutline ) + // + // const std::list*const GetRouteOutlines() + // size_t GetRouteOutlinesSize() + // ROUTE_OUTLINE* AddRouteOutline( ROUTE_OUTLINE* aRouteOutline ) + // bool DelRouteOutline( int aIndex ) + // bool DelRouteOutline( ROUTE_OUTLINE* aRouteOutline ) + // + // const std::list*const GetPlacementOutlines() + // size_t GetPlacementOutlinesSize() + // PLACE_OUTLINE* AddPlacementOutline( PLACE_OUTLINE* aPlaceOutline ) + // bool DelPlacementOutline( int aIndex ) + // bool DelPlacementOutline( PLACE_OUTLINE* aPlaceOutline ) + // + // const std::list*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*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*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*const GetGroupOutlines() + // size_t GetGroupOutlinesSize() + // GROUP_OUTLINE* AddGroupOutline( GROUP_OUTLINE* aGroupOutline ) + // bool DelGroupOutline( int aIndex ) + // bool DelGroupOutline( GROUP_OUTLINE* aGroupOutline ) + + std::list& 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 +#include +#include +#include +#include +#include +#include +#include +#include + +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 +#include +#include +#include +#include + +#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* contour = new std::list; + + 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* 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*>::const_iterator obeg = outline.begin(); + std::list*>::const_iterator oend = outline.end(); + + int nc = 0; // number of contours pushed + + int pi; + std::list::const_iterator begin; + std::list::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::const_iterator tbeg = triplets.begin(); + std::list::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::const_iterator tbeg = triplets.begin(); + std::list::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*>::const_iterator obeg = outline.begin(); + std::list*>::const_iterator oend = outline.end(); + std::list* cp; + std::list::const_iterator cbeg; + std::list::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* loop = new std::list; + + 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::const_iterator begin; + std::list::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::const_iterator begin; + std::list::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::const_iterator cbeg; + std::list::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 // CALLBACK definition, needed on Windows + // alse needed on OSX to define __DARWIN__ + +#ifdef __WXMAC__ +# ifdef __DARWIN__ +# include +# else +# include +# endif +#else +# include +#endif + +#include +#include +#include +#include + +#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 vertices; // vertices of all contours + std::vector*> contours; // lists of vertices for each contour + std::vectorpth; // indicates whether a 'contour' is a PTH or not + std::vectorsolid; // 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 triplets; // output facet triplet list (triplet of ORDER values) + std::list*> outline; // indices for outline outputs (index by ORDER values) + std::vector 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 extra_verts; // extra vertices added for outlines and facets + std::vector 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 -- cgit