Full Code of zippy84/vtkbool for AI

master 167d78255bc0 cached
37 files
571.4 KB
234.5k tokens
135 symbols
1 requests
Download .txt
Showing preview only (591K chars total). Download the full file or copy to clipboard to get everything.
Repository: zippy84/vtkbool
Branch: master
Commit: 167d78255bc0
Files: 37
Total size: 571.4 KB

Directory structure:
gitextract_hxnqu4v5/

├── .github/
│   ├── FUNDING.yml
│   └── workflows/
│       └── cmake.yml
├── CITATION.cff
├── CMakeLists.txt
├── Contact.cxx
├── Contact.h
├── LICENSE
├── Merger.cxx
├── Merger.h
├── Optimize.cxx
├── Optimize.h
├── README.md
├── Utilities.cxx
├── Utilities.h
├── module/
│   ├── CMakeLists.txt
│   └── vtk.module
├── paraview/
│   ├── CMakeLists.txt
│   ├── module/
│   │   ├── CMakeLists.txt
│   │   ├── vtk.module
│   │   └── vtkPolyDataBooleanFilter.xml
│   └── paraview.plugin
├── run_some_tests.sh
├── testing/
│   ├── data/
│   │   ├── branched.vtk
│   │   ├── branched3.vtk
│   │   ├── branched4.vtk
│   │   ├── branched6.vtk
│   │   ├── cross.vtk
│   │   ├── merger.vtk
│   │   └── non-manifold.vtk
│   ├── generate_frieze.py
│   ├── pytest.ini
│   ├── test_congruence.cxx
│   ├── test_filter.py
│   ├── test_merger.cxx
│   └── test_python.py
├── vtkPolyDataBooleanFilter.cxx
└── vtkPolyDataBooleanFilter.h

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/FUNDING.yml
================================================
ko_fi: zippy84


================================================
FILE: .github/workflows/cmake.yml
================================================
name: CMake

on:
  push:
    branches:
      - '*'

jobs:
  Build:
    runs-on: ${{matrix.os}}
    strategy:
      matrix:
        os: [ubuntu-22.04, ubuntu-latest]
        cxx: [g++, clang++]
        build_type: [Debug, Release]

    steps:
    - uses: actions/checkout@v3

    - name: Install Deps
      run: |
        sudo apt-get update
        sudo apt-get -y upgrade
        sudo DEBIAN_FRONTEND=noninteractive apt-get install -y tzdata
        sudo apt-get -y install libvtk9-dev python3-vtk9 python3-pytest

    - name: Configure CMake
      run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{matrix.build_type}}
      env:
        CXX: ${{matrix.cxx}}
        CXXFLAGS: ${{matrix.cxxflags}}

    - name: Build
      run: cmake --build ${{github.workspace}}/build --config ${{matrix.build_type}}

    - name: Test
      working-directory: ${{github.workspace}}/build
      run: ctest --output-on-failure -C ${{matrix.build_type}}

  Coverage:
    if: github.ref == 'refs/heads/master'
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3

    - name: Install Deps
      run: |
        sudo apt-get update
        sudo apt-get -y upgrade
        sudo DEBIAN_FRONTEND=noninteractive apt-get install -y tzdata
        sudo apt-get -y install libvtk9-dev python3-vtk9 python3-pytest gcovr

    - name: Configure CMake
      run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=Profile -DVTKBOOL_COVERAGE=ON

    - name: Build
      run: cmake --build ${{github.workspace}}/build

    - name: Test
      working-directory: ${{github.workspace}}/build
      run: |
        ctest --output-on-failure
        gcovr -r .. . --exclude-throw-branches --xml -o coverage.xml

    - name: Upload coverage reports to Codecov
      uses: codecov/codecov-action@v3
      with:
        token: ${{secrets.CODECOV_TOKEN}}



================================================
FILE: CITATION.cff
================================================
cff-version: 1.2.0
title: vtkbool
message: 'If you use this software, please cite it as below.'
type: software
authors:
  - family-names: Römer
    given-names: Ronald
identifiers:
  - type: doi
    value: 10.5281/zenodo.10461186
repository-code: 'https://github.com/zippy84/vtkbool'
license: Apache-2.0
version: 3.2.0


================================================
FILE: CMakeLists.txt
================================================
# Copyright 2012-2025 Ronald Römer
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

cmake_minimum_required(VERSION 3.12 FATAL_ERROR)
project(vtkbool
    VERSION 3.2
    HOMEPAGE_URL https://github.com/zippy84/vtkbool)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

set(CMAKE_CXX_OUTPUT_EXTENSION_REPLACE 1)

if(MSVC)
    add_compile_options(/EHsc)
    add_compile_definitions(_SCL_SECURE_NO_WARNINGS)
    add_compile_definitions(_USE_MATH_DEFINES)
    set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
else()
    add_compile_options(-Wall -Wextra -fPIC -Wconversion)
endif()

option(VTKBOOL_PARAVIEW "" OFF)
option(VTKBOOL_DEBUG "" OFF)
option(VTKBOOL_COVERAGE "" OFF)

mark_as_advanced(VTKBOOL_DEBUG)
mark_as_advanced(VTKBOOL_COVERAGE)

if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND VTKBOOL_COVERAGE)
    set(CMAKE_C_FLAGS_PROFILE --coverage)
    set(CMAKE_CXX_FLAGS_PROFILE --coverage)
    add_link_options("--coverage")
endif()

include_directories(".")

if(VTKBOOL_DEBUG)
    add_definitions(-DDEBUG)
endif()

if(VTKBOOL_PARAVIEW)
    find_package(ParaView REQUIRED)

    if(ParaView_FOUND)
        paraview_plugin_scan(
            PLUGIN_FILES "${CMAKE_CURRENT_SOURCE_DIR}/paraview/paraview.plugin"
            PROVIDES_PLUGINS plugins
            ENABLE_BY_DEFAULT ON)

        set(BUILD_SHARED_LIBS ON)

        include(GNUInstallDirs)

        paraview_plugin_build(
            PLUGINS ${plugins})

    endif()

else()

    find_package(VTK REQUIRED COMPONENTS FiltersSources IOLegacy FiltersExtraction FiltersGeometry FiltersModeling FiltersFlowPaths OPTIONAL_COMPONENTS WrappingPythonCore NO_MODULE)

    if(VTK_FOUND)

        if(VTK_VERSION VERSION_LESS "9.0.0")
            message(FATAL_ERROR "vtkbool requires VTK 9.0.0 or newer.")
        endif()

        vtk_module_scan(
            MODULE_FILES "${CMAKE_CURRENT_SOURCE_DIR}/module/vtk.module"
            REQUEST_MODULES vtkbool
            PROVIDES_MODULES modules
            ENABLE_TESTS ON)

        set(BUILD_SHARED_LIBS ON)

        include(GNUInstallDirs)

        vtk_module_build(MODULES ${modules})

        if (VTK_WrappingPythonCore_FOUND)

            vtk_module_wrap_python(
                MODULES ${modules}
                PYTHON_PACKAGE "vtkbool"
                BUILD_STATIC OFF
                INSTALL_HEADERS OFF)

            include(CTest)

            vtk_module_python_default_destination(python_default_destination)

            add_test(NAME "import_vtkbool"
                COMMAND ${Python3_EXECUTABLE} ${CMAKE_SOURCE_DIR}/testing/test_python.py)

            set_property(TEST "import_vtkbool" APPEND PROPERTY ENVIRONMENT "PYTHONPATH=${CMAKE_BINARY_DIR}/${python_default_destination}/vtkbool")

            add_test(NAME "test_filter"
                COMMAND ${Python3_EXECUTABLE} -m pytest
                WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/testing)

            set_property(TEST "test_filter" APPEND PROPERTY ENVIRONMENT "PYTHONPATH=${CMAKE_BINARY_DIR}/${python_default_destination}/vtkbool")

            add_executable(test_merger testing/test_merger.cxx)
            target_link_libraries(test_merger PRIVATE vtkbool ${VTK_LIBRARIES})

            add_executable(test_congruence testing/test_congruence.cxx)
            target_link_libraries(test_congruence PRIVATE vtkbool ${VTK_LIBRARIES})

            vtk_module_autoinit(
                TARGETS test_merger test_congruence
                MODULES ${VTK_LIBRARIES}
            )

            add_test(NAME "test_merger"
                COMMAND test_merger
                WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/testing)

            add_test(NAME "test_congruence"
                COMMAND test_congruence
                WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/testing)

            add_test(NAME "generate_frieze"
                COMMAND ${Python3_EXECUTABLE} ${CMAKE_SOURCE_DIR}/testing/generate_frieze.py
                WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/testing)

            set_property(TEST "generate_frieze" APPEND PROPERTY ENVIRONMENT "PYTHONPATH=${CMAKE_BINARY_DIR}/${python_default_destination}/vtkbool")

        endif()

    endif()

endif()


================================================
FILE: Contact.cxx
================================================
/*
Copyright 2012-2025 Ronald Römer

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#include "Contact.h"
#include "Optimize.h"

#include <vtkCleanPolyData.h>
#include <vtkCellIterator.h>
#include <vtkCellData.h>
#include <vtkTriangleStrip.h>
#include <vtkArrayIteratorTemplate.h>

// #define _debA 0
// #define _debB 0

#if (defined(_debA) && defined(_debB))
vtkIdType _idA, _idB;
#endif

vtkSmartPointer<vtkPolyData> Clean (vtkPolyData *pd) {

    auto clean = vtkSmartPointer<vtkCleanPolyData>::New();
    clean->SetOutputPointsPrecision(vtkAlgorithm::DOUBLE_PRECISION);
    clean->SetTolerance(1e-6);

    clean->ConvertLinesToPointsOff();
    clean->ConvertPolysToLinesOff();
    clean->ConvertStripsToPolysOff();

    clean->SetInputData(pd);

    clean->Update();

    auto cleaned = clean->GetOutput();

    vtkIdType numCells = cleaned->GetNumberOfCells();

    auto newPd = vtkSmartPointer<vtkPolyData>::New();
    newPd->SetPoints(cleaned->GetPoints());
    newPd->Allocate(numCells);

    auto cellIds = vtkSmartPointer<vtkIdTypeArray>::New();
    cellIds->SetName("OrigCellIds");
    cellIds->Allocate(numCells);

    vtkCellIterator *cellItr = cleaned->NewCellIterator();

    vtkIdType cellId;
    vtkIdList *ptIds;

    vtkIdType num;
    const vtkIdType *pts;

    for (cellItr->InitTraversal(); !cellItr->IsDoneWithTraversal(); cellItr->GoToNextCell()) {
        cellId = cellItr->GetCellId();
        ptIds = cellItr->GetPointIds();

        if (cellItr->GetCellType() == VTK_TRIANGLE || cellItr->GetCellType() == VTK_POLYGON) {
            newPd->InsertNextCell(cellItr->GetCellType(), ptIds);
            cellIds->InsertNextValue(cellId);

        } else if (cellItr->GetCellType() == VTK_TRIANGLE_STRIP) {
            auto cells = vtkSmartPointer<vtkCellArray>::New();

            vtkTriangleStrip::DecomposeStrip(cellItr->GetNumberOfPoints(), ptIds->GetPointer(0), cells);

            for (cells->InitTraversal(); cells->GetNextCell(num, pts);) {
                if (pts[0] != pts[1] && pts[1] != pts[2] && pts[2] != pts[0]) {
                    newPd->InsertNextCell(VTK_TRIANGLE, num, pts);
                    cellIds->InsertNextValue(cellId);
                }
            }

        } else if (cellItr->GetCellType() == VTK_QUAD) {

            double pA[3], pB[3], pC[3], pD[3];

            cleaned->GetPoint(ptIds->GetId(0), pA);
            cleaned->GetPoint(ptIds->GetId(1), pB);
            cleaned->GetPoint(ptIds->GetId(2), pC);
            cleaned->GetPoint(ptIds->GetId(3), pD);

            double det = vtkMath::Determinant3x3(pB[0]-pA[0], pC[0]-pA[0], pD[0]-pA[0],
                pB[1]-pA[1], pC[1]-pA[1], pD[1]-pA[1],
                pB[2]-pA[2], pC[2]-pA[2], pD[2]-pA[2]);

            if (std::abs(det) < 1e-10) {
                newPd->InsertNextCell(VTK_POLYGON, ptIds);
                cellIds->InsertNextValue(cellId);
            } else {
                const vtkIdType cellA[] = {ptIds->GetId(0), ptIds->GetId(1), ptIds->GetId(2)};
                const vtkIdType cellB[] = {ptIds->GetId(2), ptIds->GetId(3), ptIds->GetId(0)};

                newPd->InsertNextCell(VTK_TRIANGLE, 3, cellA);
                cellIds->InsertNextValue(cellId);

                newPd->InsertNextCell(VTK_TRIANGLE, 3, cellB);
                cellIds->InsertNextValue(cellId);
            }
        }
    }

    cellItr->Delete();

    newPd->GetCellData()->SetScalars(cellIds);
    newPd->Squeeze();

    return newPd;
}

Contact::Contact (vtkPolyData *newPdA, vtkPolyData *newPdB) : newPdA(newPdA), newPdB(newPdB) {
    assert(newPdA->GetCellData()->GetScalars("OrigCellIds") != nullptr);
    assert(newPdB->GetCellData()->GetScalars("OrigCellIds") != nullptr);

    pts = vtkSmartPointer<vtkPoints>::New();
    pts->SetDataTypeToDouble();

    lines = vtkSmartPointer<vtkPolyData>::New();
    lines->SetPoints(pts);
    lines->Allocate(1000);

    contA = vtkSmartPointer<vtkIdTypeArray>::New();
    contB = vtkSmartPointer<vtkIdTypeArray>::New();

    contA->Allocate(1000);
    contB->Allocate(1000);

    contA->SetName("cA");
    contB->SetName("cB");

    lines->GetCellData()->AddArray(contA);
    lines->GetCellData()->AddArray(contB);

    sourcesA = vtkSmartPointer<vtkIdTypeArray>::New();
    sourcesB = vtkSmartPointer<vtkIdTypeArray>::New();

    sourcesA->Allocate(1000);
    sourcesB->Allocate(1000);

    sourcesA->SetNumberOfComponents(2);
    sourcesB->SetNumberOfComponents(2);

    sourcesA->SetName("sourcesA");
    sourcesB->SetName("sourcesB");

    lines->GetCellData()->AddArray(sourcesA);
    lines->GetCellData()->AddArray(sourcesB);

    touchesEdgesA = false;
    touchesEdgesB = false;

    GetNonManifoldEdges(newPdA, edgesA);
    GetNonManifoldEdges(newPdB, edgesB);

    treeA = vtkSmartPointer<vtkOBBTree>::New();
    treeA->SetDataSet(newPdA);
    treeA->BuildLocator();

    treeB = vtkSmartPointer<vtkOBBTree>::New();
    treeB->SetDataSet(newPdB);
    treeB->BuildLocator();
}

vtkSmartPointer<vtkPolyData> Contact::GetLines (vtkPolyData *pdA, vtkLinearTransform *transA, vtkPolyData *pdB, vtkLinearTransform *transB) {

    auto matrix = vtkSmartPointer<vtkMatrix4x4>::New();

    if (pdA != nullptr) {
        newPdA = pdA;
    }

    if (pdB != nullptr) {
        newPdB = pdB;
    }

    auto matrixA = vtkSmartPointer<vtkMatrix4x4>::New();

    if (transA != nullptr) {
        matrixA = transA->GetMatrix();
    }

    auto matrixB = vtkSmartPointer<vtkMatrix4x4>::New();

    if (transB != nullptr) {
        matrixB = transB->GetMatrix();
    }

    auto tmpMatrix = vtkSmartPointer<vtkMatrix4x4>::New();

    vtkMatrix4x4::Invert(matrixA, tmpMatrix);
    vtkMatrix4x4::Multiply4x4(tmpMatrix, matrixB, matrix);

    sourcesA->Reset();
    sourcesB->Reset();
    contA->Reset();
    contB->Reset();

    lines->Reset();

    treeA->IntersectWithOBBTree(treeB, matrix, InterNodes, this);

    IntersectReplacements();

    if (touchesEdgesA || touchesEdgesB) {
        throw std::runtime_error("Intersection goes through non-manifold edges.");
    }

    auto clean = vtkSmartPointer<vtkCleanPolyData>::New();
    clean->SetInputData(lines);
    clean->ToleranceIsAbsoluteOn();
    clean->SetAbsoluteTolerance(1e-5);
    clean->Update();

    auto cleaned = clean->GetOutput();

    vtkCellIterator *cellItr = cleaned->NewCellIterator();

    vtkIdType cellId;

    for (cellItr->InitTraversal(); !cellItr->IsDoneWithTraversal(); cellItr->GoToNextCell()) {
        cellId = cellItr->GetCellId();

        if (cellItr->GetCellType() != VTK_LINE) {
            cleaned->DeleteCell(cellId);
        }
    }

    cellItr->Delete();

    cleaned->RemoveDeletedCells();

    return cleaned;
}

void Contact::GetNonManifoldEdges (vtkPolyData *pd, NonManifoldEdgesType &edges) {

    pd->BuildLinks();

    vtkCellIterator *cellItr = pd->NewCellIterator();

    vtkIdType cellId;
    vtkIdList *ptIds;

    auto neigs = vtkSmartPointer<vtkIdList>::New();

    vtkIdType i, j, k;

    vtkIdType idA, idB;

    vtkIdType n;

    vtkIdType num;
    const vtkIdType *pts;

    vtkIdType a, b;

    for (cellItr->InitTraversal(); !cellItr->IsDoneWithTraversal(); cellItr->GoToNextCell()) {
        cellId = cellItr->GetCellId();
        ptIds = cellItr->GetPointIds();

        for (i = 0; i < ptIds->GetNumberOfIds(); i++) {
            j = i+1;

            if (j == ptIds->GetNumberOfIds()) {
                j = 0;
            }

            idA = ptIds->GetId(i);
            idB = ptIds->GetId(j);

            pd->GetCellEdgeNeighbors(cellId, idA, idB, neigs);

            if (neigs->GetNumberOfIds() > 1) {

                n = 0;

                for (k = 0; k < neigs->GetNumberOfIds(); k++) {
                    pd->GetCellPoints(neigs->GetId(k), num, pts);

                    for (a = 0; a < num; a++) {
                        b = a+1;

                        if (b == num) {
                            b = 0;
                        }

                        if (pts[a] == idB && pts[b] == idA) {
                            n++;
                        }
                    }
                }

                if (n > 1) {
                    edges.emplace(idA, idB);
                    edges.emplace(idB, idA);
                }
            }
        }
    }

    cellItr->Delete();
}

void Contact::InterEdgeLine (InterPtsType &interPts, const Point3d &pA, const Point3d &pB, Src src) {
    // schnitt mit x-achse

    double v[3];
    Point3d::GetVec(pA, pB, v);

    double w[] = {-v[1], v[0]};

    double c = w[0]*pA.x+w[1]*pA.y;

    if (std::abs(w[0]) < 1e-10) {
        if (std::abs(pA.y) < 1e-5 && std::abs(pB.y) < 1e-5) {
            interPts.emplace_back(pA.x, 0, 0, pA.x, pA.id, pB.id, End::A, src, PointSrc::Copied);
            interPts.emplace_back(pB.x, 0, 0, pB.x, pA.id, pB.id, End::B, src, PointSrc::Copied);
        }
    } else {
        double yA = pA.y-1e-6*v[1];
        double yB = pB.y+1e-6*v[1];

        if (std::signbit(yA) != std::signbit(yB)) {
            double x = c/w[0];

            double sA[] = {pA.x-x, pA.y};
            double sB[] = {pB.x-x, pB.y};

            double dA = sA[0]*sA[0]+sA[1]*sA[1];
            double dB = sB[0]*sB[0]+sB[1]*sB[1];

            End end = dA < 1e-12 ? End::A : (dB < 1e-12 ? End::B : End::None);

            interPts.emplace_back(x, 0, 0, x, pA.id, pB.id, end, src, PointSrc::Calculated);
        }
    }

}

bool Contact::InterPolyLine (InterPtsType &interPts, const Base2 &base, const Poly &poly, Src src) {

#if (defined(_debA) && defined(_debB))
    if (_idA == _debA && _idB == _debB) {
        std::cout << "InterPolyLine()" << std::endl;
    }
#endif

    Poly::const_iterator itrA, itrB;

    double q[3];

    for (itrA = poly.begin(); itrA != poly.end(); ++itrA) {
        itrB = itrA+1;

        if (itrB == poly.end()) {
            itrB = poly.begin();
        }

        InterEdgeLine(interPts, *itrA, *itrB, src); // schnitt mit x-achse
    }

    for (auto& p : interPts) {
        base.BackTransform(p.pt, q);

        std::copy_n(q, 3, p.pt);
    }

    std::sort(interPts.begin(), interPts.end(), [](auto &a, auto &b) { return a.t < b.t; });

    struct Cmp {
        const double tol = 1e-5;

        bool operator() (const InterPt &lhs, const InterPt &rhs) const {
            if (lhs.pointSrc == PointSrc::Copied && rhs.pointSrc == PointSrc::Copied) {
                if (lhs.GetEnd() == rhs.GetEnd()) {
                    return false;
                }
            } else if (lhs.pointSrc == PointSrc::Copied) {
                const vtkIdType end = lhs.GetEnd();

                if (end == rhs.edge.f || end == rhs.edge.g) {
                    return false;
                }
            } else if (rhs.pointSrc == PointSrc::Copied) {
                const vtkIdType end = rhs.GetEnd();

                if (end == lhs.edge.f || end == lhs.edge.g) {
                    return false;
                }
            }

            const double d = lhs.t-rhs.t;

            if (std::abs(d) < tol) {
                return false;
            } else {
                return d < 0;
            }
        }
    };

    std::map<InterPt, InterPtsType, Cmp> grouped;

    std::vector<InterPtsType> sortedPts;

    for (auto &p : interPts) {
        grouped[p].push_back(p);
    }

    for (auto& [k, v] : grouped) {
        std::sort(v.begin(), v.end(), [](const InterPt &lhs, const InterPt &rhs) { return lhs.pointSrc > rhs.pointSrc; });

        sortedPts.push_back(v);
    }

    std::map<vtkIdType, double> allEnds;

    decltype(sortedPts)::iterator itr;

    for (itr = sortedPts.begin(); itr != sortedPts.end(); ++itr) {
        if (itr == sortedPts.begin()) {
            if (itr->size() == 2) {
                itr->pop_back();
            }
        } else if (itr == sortedPts.end()-1) {
            if (itr->size() == 2) {
                itr->pop_back();
            }
        } else if (itr->size() == 1 && itr->back().end != End::None) {
            itr->push_back(itr->back());
        }

        if (itr->back().end != End::None) {
            auto p = itr->back();

            allEnds.emplace(p.end == End::A ? p.edge.f : p.edge.g, p.t);
        }

        // hier schneidet sich das polygon selbst

        if (itr->size() > 2) {
            return false;
        }

        if (itr->size() == 2) {
            // schnitt durch kongruente kanten

            if (itr->back().end == End::None) {
                return false;
            }

            auto &edgeA = itr->front().edge;
            auto &edgeB = itr->back().edge;

            if (edgeA.f == edgeB.g && edgeB.f == edgeA.g) {
                return false;
            }

        }

    }

    std::map<vtkIdType, std::reference_wrapper<const Point3d>> pts;

    for (auto &p : poly) {
        pts.emplace(p.id, p);
    }

    vtkIdType ind;

    vtkIdType prev, next;

    for (itr = sortedPts.begin(); itr != sortedPts.end(); ++itr) {
        auto p = itr->back();

        if (p.end == End::None) {
            continue;
        }

        if (p.end == End::A) {
            ind = p.edge.f;

            next = p.edge.g;

            auto _itr = std::find_if(poly.begin(), poly.end(), [&ind](auto &p) { return p.id == ind; });

            if (_itr == poly.begin()) {
                prev = poly.back().id;
            } else {
                prev = std::prev(_itr)->id;
            }

        } else {
            ind = p.edge.g;

            prev = p.edge.f;

            auto _itr = std::find_if(poly.begin(), poly.end(), [&ind](auto &p) { return p.id == ind; });

            if (_itr == poly.end()-1) {
                next = poly.front().id;
            } else {
                next = std::next(_itr)->id;
            }
        }

        if (itr->size() == 2) {
            if (allEnds.count(prev) == 0 && allEnds.count(next) == 1) {
                const Point3d &q = pts.at(prev);

                if ((allEnds.at(next) < p.t && q.y < 0) || (allEnds.at(next) > p.t && q.y > 0)) {
                    itr->pop_back();
                }

            } else if (allEnds.count(prev) == 1 && allEnds.count(next) == 0) {
                const Point3d &q = pts.at(next);

                if ((allEnds.at(prev) < p.t && q.y > 0) || (allEnds.at(prev) > p.t && q.y < 0)) {
                    itr->pop_back();
                }
            }
        }

        if (allEnds.count(prev) == 0 && allEnds.count(next) == 0) {
            const Point3d &a = pts.at(prev);
            const Point3d &b = pts.at(next);

            if (std::signbit(a.y) != std::signbit(b.y)) {
                if (itr->size() == 2) {
                    itr->pop_back();
                }
            } else {
                if ((a.x > b.x) == std::signbit(a.y)) {
                    itr->clear();
                }
            }

        }

    }

    InterPtsType _interPts;

    for (const auto &pts : sortedPts) {
        std::copy(pts.begin(), pts.end(), std::back_inserter(_interPts));
    }

    interPts.swap(_interPts);

#if (defined(_debA) && defined(_debB))
    if (_idA == _debA && _idB == _debB) {
        for (auto &p : interPts) {
            std::cout << p << std::endl;
        }
    }
#endif

    return true;
}

void Contact::InterPolys (vtkIdType idA, vtkIdType idB) {

#if (defined(_debA) && defined(_debB))
    _idA = idA; _idB = idB;

    if (_idA == _debA && _idB == _debB) {
        std::cout << "InterPolys(" << idA << ", " << idB << ")" << std::endl;
    }
#endif

    vtkIdType numA, numB;
    const vtkIdType *ptsA, *ptsB;

    newPdA->GetCellPoints(idA, numA, ptsA);
    newPdB->GetCellPoints(idB, numB, ptsB);

    Poly polyA, polyB;
    GetPoly(newPdA->GetPoints(), numA, ptsA, polyA);
    GetPoly(newPdB->GetPoints(), numB, ptsB, polyB);

    double nA[3], nB[3], r[3], s[3];

    ComputeNormal(polyA, nA);
    ComputeNormal(polyB, nB);

#if (defined(_debA) && defined(_debB))
    if (_idA == _debA && _idB == _debB) {
        std::cout << "nA [" << nA[0] << ", " << nA[1] << ", " << nA[2] << "]" << std::endl;
        std::cout << "nB [" << nB[0] << ", " << nB[1] << ", " << nB[2] << "]" << std::endl;
    }
#endif

    if (vtkMath::Dot(nA, nB) > .9999999999) {
        return;
    }

    double dA = polyA[0].x*nA[0]+polyA[0].y*nA[1]+polyA[0].z*nA[2];
    double dB = polyB[0].x*nB[0]+polyB[0].y*nB[1]+polyB[0].z*nB[2];

    vtkMath::Cross(nA, nB, r);
    vtkMath::Normalize(r);

    std::array<std::tuple<int, int, int, double>, 3> dets {
        std::make_tuple(0, 1, 2, nA[0]*nB[1]-nB[0]*nA[1]),
        std::make_tuple(0, 2, 1, nA[0]*nB[2]-nB[0]*nA[2]),
        std::make_tuple(1, 2, 0, nA[1]*nB[2]-nB[1]*nA[2])
    };

    const auto& [i, j, k, det] = *std::max_element(dets.begin(), dets.end(), [](auto &a, auto &b) { return std::abs(std::get<3>(a)) < std::abs(std::get<3>(b)); });

    s[i] = (dA*nB[j]-dB*nA[j])/det;
    s[j] = (dB*nA[i]-dA*nB[i])/det;
    s[k] = 0;

#if (defined(_debA) && defined(_debB))
    if (_idA == _debA && _idB == _debB) {
        std::cout << "det " << det << std::endl;
        std::cout << "r [" << r[0] << ", " << r[1] << ", " << r[2] << "]" << std::endl;
        std::cout << "s [" << s[0] << ", " << s[1] << ", " << s[2] << "]" << std::endl;
    }
#endif

    Base2 baseA(s, r, nA);
    Base2 baseB(s, r, nB);

    Poly transA, transB;

    FlattenPoly2(polyA, transA, baseA);
    FlattenPoly2(polyB, transB, baseB);

#if (defined(_debA) && defined(_debB))
    if (_idA == _debA && _idB == _debB) {
        std::cout << "transA {";
        for (auto& p : transA) {
            std::cout << p << ", ";
        }
        std::cout << std::endl;

        std::cout << "transB {";
        for (auto& p : transB) {
            std::cout << p << ", ";
        }
        std::cout << std::endl;
    }
#endif

    bool isPlanarA = std::find_if(transA.begin(), transA.end(), [](auto &p) { return std::abs(p.z) > 1e-6; }) == transA.end();
    bool isPlanarB = std::find_if(transB.begin(), transB.end(), [](auto &p) { return std::abs(p.z) > 1e-6; }) == transB.end();

    bool hasReplA = replsA.count(idA) == 1;
    bool hasReplB = replsB.count(idB) == 1;

    if (!isPlanarA && !hasReplA && newPdA->GetCellType(idA) != VTK_TRIANGLE) {
        try {
            auto newIds = PreventEqualCaptPoints::TriangulateCell(newPdA, idA, {});
            replsA.emplace(idA, newIds);
            hasReplA = true;
        } catch (...) {}
    }

    if (!isPlanarB && !hasReplB && newPdB->GetCellType(idB) != VTK_TRIANGLE) {
        try {
            auto newIds = PreventEqualCaptPoints::TriangulateCell(newPdB, idB, {});
            replsB.emplace(idB, newIds);
            hasReplB = true;
        } catch (...) {}
    }

    if (hasReplA || hasReplB) {
        pairs.emplace_back(idA, idB);
        return;
    }

    InterPtsType intersPtsA, intersPtsB;

    if (!InterPolyLine(intersPtsA, baseA, transA, Src::A)) {
        throw std::runtime_error("Found invalid intersection points.");
    }

    if (!InterPolyLine(intersPtsB, baseB, transB, Src::B)) {
        throw std::runtime_error("Found invalid intersection points.");
    }

    if (!CheckInters(intersPtsA, newPdA)) {
        std::stringstream ss;
        ss << "Intersection points do not lie on the edges (cells " << idA << ", " << idB << ").";

        throw std::runtime_error(ss.str());
    }

    if (!CheckInters(intersPtsB, newPdB)) {
        std::stringstream ss;
        ss << "Intersection points do not lie on the edges (cells " << idA << ", " << idB << ").";

        throw std::runtime_error(ss.str());
    }

    if ((intersPtsA.size() & 1) == 0 && (intersPtsB.size() & 1) == 0) {
        AddContactLines(intersPtsA, intersPtsB, idA, idB);
    }

}

bool Contact::CheckInters (const InterPtsType &interPts, vtkPolyData *pd) {
#if (defined(_debA) && defined(_debB))
    if (_idA == _debA && _idB == _debB) {
        std::cout << "CheckInters()" << std::endl;
    }
#endif

    double ptA[3],
        ptB[3],
        v[3],
        w[3],
        k,
        l,
        alpha,
        d;

    for (auto &p : interPts) {

#if (defined(_debA) && defined(_debB))
        if (_idA == _debA && _idB == _debB) {
            std::cout << p << std::endl;
        }
#endif

        pd->GetPoint(p.edge.f, ptA);
        pd->GetPoint(p.edge.g, ptB);

        vtkMath::Subtract(ptA, ptB, v);
        vtkMath::Normalize(v);
        vtkMath::Subtract(ptA, p.pt, w);

        k = vtkMath::Norm(w);
        l = vtkMath::Dot(v, w);
        alpha = std::acos(l/k);

#if (defined(_debA) && defined(_debB))
        if (_idA == _debA && _idB == _debB) {
            std::cout << "alpha " << alpha << std::endl;
        }
#endif

        if (std::isnan(alpha)) {
            continue;
        }

        d = std::sin(alpha)*k;

#if (defined(_debA) && defined(_debB))
        if (_idA == _debA && _idB == _debB) {
            std::cout << "d " << d << std::endl;
        }
#endif

        if (d < 1e-5) {
            continue;
        }

        return false;

    }

    return true;

}

void Contact::OverlapLines (OverlapsType &overlaps, InterPtsType &intersA, InterPtsType &intersB) {

    auto Add = [](InterPt &a, InterPt &b, InterPt &c, InterPt &d) {
        a.Merge(c);
        b.Merge(d);

        return std::make_tuple(a, b);
    };

    InterPtsType::iterator itr, itr2;

    for (itr = intersA.begin(); itr != intersA.end(); itr += 2) {
        for (itr2 = intersB.begin(); itr2 != intersB.end(); itr2 += 2) {
            if (itr->t <= itr2->t && (itr+1)->t > itr2->t) {
                if ((itr2+1)->t < (itr+1)->t) {
                    overlaps.push_back(Add(*itr2, *(itr2+1), *itr, *(itr+1)));
                } else {
                    overlaps.push_back(Add(*itr2, *(itr+1), *itr, *(itr2+1)));
                }
            } else if (itr2->t <= itr->t && (itr2+1)->t > itr->t) {
                if ((itr+1)->t < (itr2+1)->t) {
                    overlaps.push_back(Add(*itr, *(itr+1), *itr2, *(itr2+1)));
                } else {
                    overlaps.push_back(Add(*itr, *(itr2+1), *itr2, *(itr+1)));
                }
            }
        }
    }

}

void Contact::AddContactLines (InterPtsType &intersA, InterPtsType &intersB, vtkIdType idA, vtkIdType idB) {

    if (intersA.size() == 0 || intersB.size() == 0) {
        return;
    }

    OverlapsType overlaps;
    OverlapLines(overlaps, intersA, intersB);

    OverlapsType::const_iterator itr;

    for (itr = overlaps.begin(); itr != overlaps.end(); ++itr) {
        auto &f = std::get<0>(*itr);
        auto &s = std::get<1>(*itr);

        if ((f.src == Src::A && edgesA.count(f.edge) == 1) || (s.src == Src::A && edgesA.count(s.edge) == 1)) {
            touchesEdgesA = true;
        }

        if ((f.src == Src::B && edgesB.count(f.edge) == 1) || (s.src == Src::B && edgesB.count(s.edge) == 1)) {
            touchesEdgesB = true;
        }

        vtkIdList *linePts = vtkIdList::New();

        linePts->InsertNextId(pts->InsertNextPoint(f.pt));
        linePts->InsertNextId(pts->InsertNextPoint(s.pt));

        lines->InsertNextCell(VTK_LINE, linePts);

        linePts->Delete();

        const vtkIdType tupleA[] = {f.srcA, s.srcA};
        const vtkIdType tupleB[] = {f.srcB, s.srcB};

        sourcesA->InsertNextTypedTuple(tupleA);
        sourcesB->InsertNextTypedTuple(tupleB);

        contA->InsertNextValue(idA);
        contB->InsertNextValue(idB);
    }

}

int Contact::InterNodes (vtkOBBNode *nodeA, vtkOBBNode *nodeB, vtkMatrix4x4 *vtkNotUsed(matrix), void *ptr) {
    auto _this = reinterpret_cast<Contact*>(ptr);

    vtkIdList *cellsA = nodeA->Cells;
    vtkIdList *cellsB = nodeB->Cells;

    vtkIdType numCellsA = cellsA->GetNumberOfIds();
    vtkIdType numCellsB = cellsB->GetNumberOfIds();

    vtkIdType i, j, cellA, cellB;

    for (i = 0; i < numCellsA; i++) {
        cellA = cellsA->GetId(i);

        for (j = 0; j < numCellsB; j++) {
            cellB = cellsB->GetId(j);

            _this->InterPolys(cellA, cellB);
        }
    }

    return 0;
}

void Contact::IntersectReplacements () {
    if (pairs.empty()) {
        return;
    }

    while (!pairs.empty()) {
        vtkIdType i;

        auto iterA = vtkArrayIteratorTemplate<vtkIdType>::New();
        iterA->Initialize(contA);

        auto iterB = vtkArrayIteratorTemplate<vtkIdType>::New();
        iterB->Initialize(contB);

        for (i = 0; i < iterA->GetNumberOfValues(); i++) {
            if (replsA.count(iterA->GetValue(i)) == 1 || replsB.count(iterB->GetValue(i)) == 1) {
                lines->DeleteCell(i);

                pairs.emplace_back(iterA->GetValue(i), iterB->GetValue(i));
            }
        }

        PairsType current;

        current.swap(pairs);

        for (auto& [a, b] : current) {
            auto itrA = replsA.find(a);
            auto itrB = replsB.find(b);

            IdsType cellsA, cellsB;

            if (itrA == replsA.end()) {
                cellsA.push_back(a);
            } else {
                auto ids = itrA->second;
                std::copy(ids.begin(), ids.end(), std::back_inserter(cellsA));
            }

            if (itrB == replsB.end()) {
                cellsB.push_back(b);
            } else {
                auto ids = itrB->second;
                std::copy(ids.begin(), ids.end(), std::back_inserter(cellsB));
            }

            for (auto &idA : cellsA) {
                for (auto &idB : cellsB) {
                    InterPolys(idA, idB);
                }
            }
        }

    }

    lines->RemoveDeletedCells();

    contA = vtkIdTypeArray::SafeDownCast(lines->GetCellData()->GetScalars("cA"));
    contB = vtkIdTypeArray::SafeDownCast(lines->GetCellData()->GetScalars("cB"));

    sourcesA = vtkIdTypeArray::SafeDownCast(lines->GetCellData()->GetScalars("sourcesA"));
    sourcesB = vtkIdTypeArray::SafeDownCast(lines->GetCellData()->GetScalars("sourcesB"));

    // contA und contB aktualisieren

    auto oldCellIdsA = vtkSmartPointer<vtkIdTypeArray>::New();
    auto oldCellIdsB = vtkSmartPointer<vtkIdTypeArray>::New();

    oldCellIdsA->SetName("OldCellIds");
    oldCellIdsB->SetName("OldCellIds");

    vtkIdType numCellsA = newPdA->GetNumberOfCells();
    vtkIdType numCellsB = newPdB->GetNumberOfCells();

    oldCellIdsA->SetNumberOfValues(numCellsA);
    oldCellIdsB->SetNumberOfValues(numCellsB);

    vtkIdType i;

    for (i = 0; i < numCellsA; i++) {
        oldCellIdsA->SetValue(i, i);
    }

    for (i = 0; i < numCellsB; i++) {
        oldCellIdsB->SetValue(i, i);
    }

    newPdA->GetCellData()->AddArray(oldCellIdsA);
    newPdB->GetCellData()->AddArray(oldCellIdsB);

    for (auto& [k, v] : replsA) {
        newPdA->DeleteCell(k);
    }

    for (auto& [k, v] : replsB) {
        newPdB->DeleteCell(k);
    }

    newPdA->RemoveDeletedCells();
    newPdB->RemoveDeletedCells();

    numCellsA = newPdA->GetNumberOfCells();
    numCellsB = newPdB->GetNumberOfCells();

    oldCellIdsA = vtkIdTypeArray::SafeDownCast(newPdA->GetCellData()->GetScalars("OldCellIds"));
    oldCellIdsB = vtkIdTypeArray::SafeDownCast(newPdB->GetCellData()->GetScalars("OldCellIds"));

    std::map<vtkIdType, vtkIdType> newCellIdsA, newCellIdsB;

    auto iterA = vtkArrayIteratorTemplate<vtkIdType>::New();
    iterA->Initialize(oldCellIdsA);

    for (i = 0; i < numCellsA; i++) {
        newCellIdsA.emplace(iterA->GetValue(i), i);
    }

    auto iterB = vtkArrayIteratorTemplate<vtkIdType>::New();
    iterB->Initialize(oldCellIdsB);

    for (i = 0; i < numCellsB; i++) {
        newCellIdsB.emplace(iterB->GetValue(i), i);
    }

    vtkIdType numLines = lines->GetNumberOfCells();

    try {

        auto _iterA = vtkArrayIteratorTemplate<vtkIdType>::New();
        _iterA->Initialize(contA);

        for (i = 0; i < numLines; i++) {
            _iterA->SetValue(i, newCellIdsA.at(_iterA->GetValue(i)));
        }

        auto _iterB = vtkArrayIteratorTemplate<vtkIdType>::New();
        _iterB->Initialize(contB);

        for (i = 0; i < numLines; i++) {
            _iterB->SetValue(i, newCellIdsB.at(_iterB->GetValue(i)));
        }

    } catch (const std::out_of_range &e) {
        throw std::runtime_error("");
    }

    newPdA->GetCellData()->RemoveArray("OldCellIds");
    newPdB->GetCellData()->RemoveArray("OldCellIds");

}


================================================
FILE: Contact.h
================================================
/*
Copyright 2012-2025 Ronald Römer

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#ifndef __Contact_h
#define __Contact_h

#include "Utilities.h"

#include <vtkOBBTree.h>
#include <vtkMatrix4x4.h>
#include <vtkLinearTransform.h>

enum class Src {
    A,
    B
};

enum class End {
    None,
    A,
    B
};

enum class PointSrc {
    Calculated,
    Copied
};

class InterPt {
public:
    InterPt () = delete;

    InterPt (double x, double y, double z, double t, vtkIdType a, vtkIdType b, End end, Src src, PointSrc pointSrc) : t(t), edge(a, b), end(end), src(src), srcA(NOTSET), srcB(NOTSET), pointSrc(pointSrc) {
        pt[0] = x;
        pt[1] = y;
        pt[2] = z;
    }

    double pt[3], t;
    Pair edge;
    End end;
    Src src;
    vtkIdType srcA, srcB;

    PointSrc pointSrc;

    friend std::ostream& operator<< (std::ostream &out, const InterPt &s) {
        out << "pt [" << s.pt[0] << ", " << s.pt[1] << ", " << s.pt[2] << "]"
            << ", t " << s.t
            << ", edge " << s.edge
            << ", end " << s.end
            << ", src " << s.src
            << ", pointSrc " << s.pointSrc;

        return out;
    }

    void Merge (const InterPt &other) {
        assert(src != other.src);

        if (src == Src::A) {
            srcA = GetEnd();
        } else {
            srcB = GetEnd();
        }

        if (std::abs(other.t-t) < 1e-5) {
            if (other.src == Src::A) {
                srcA = other.GetEnd();
            } else {
                srcB = other.GetEnd();
            }
        }
    }

    inline vtkIdType GetEnd () const {
        if (end == End::A) {
            return edge.f;
        }

        if (end == End::B) {
            return edge.g;
        }

        return NOTSET;
    }

};

typedef std::vector<InterPt> InterPtsType;
typedef std::vector<std::tuple<InterPt, InterPt>> OverlapsType;

typedef std::set<Pair> NonManifoldEdgesType;

typedef std::vector<std::pair<vtkIdType, vtkIdType>> PairsType;

vtkSmartPointer<vtkPolyData> Clean (vtkPolyData *pd);

class Contact {
public:
    Contact () = delete;
    Contact (vtkPolyData *newPdA, vtkPolyData *newPdB);

    vtkPolyData *newPdA, *newPdB;

    vtkSmartPointer<vtkPoints> pts;
    vtkSmartPointer<vtkPolyData> lines;
    vtkSmartPointer<vtkIdTypeArray> contA, contB, sourcesA, sourcesB;

    bool touchesEdgesA, touchesEdgesB;

    NonManifoldEdgesType edgesA, edgesB;

    vtkSmartPointer<vtkOBBTree> treeA, treeB;

    vtkSmartPointer<vtkPolyData> GetLines (vtkPolyData *pdA = nullptr, vtkLinearTransform *transA = nullptr, vtkPolyData *pdB = nullptr, vtkLinearTransform *transB = nullptr);

    void GetNonManifoldEdges (vtkPolyData *pd, NonManifoldEdgesType &edges);

    void InterEdgeLine (InterPtsType &interPts, const Point3d &pA, const Point3d &pB, Src src);

    bool InterPolyLine (InterPtsType &interPts, const Base2 &base, const Poly &poly, Src src);

    void InterPolys (vtkIdType idA, vtkIdType idB);

    bool CheckInters (const InterPtsType &interPts, vtkPolyData *pd);

    void OverlapLines (OverlapsType &overlaps, InterPtsType &intersA, InterPtsType &intersB);

    void AddContactLines (InterPtsType &intersA, InterPtsType &intersB, vtkIdType idA, vtkIdType idB);

    static int InterNodes (vtkOBBNode *nodeA, vtkOBBNode *nodeB, vtkMatrix4x4 *vtkNotUsed(matrix), void *ptr);

    PairsType pairs;

    std::map<vtkIdType, IdsType> replsA, replsB;

    void IntersectReplacements ();
};

#endif


================================================
FILE: LICENSE
================================================

                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS



================================================
FILE: Merger.cxx
================================================
/*
Copyright 2012-2025 Ronald Römer

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#include "Merger.h"

#include <vtkCellData.h>

Merger::Merger (vtkPolyData *pd, const PStrips &pStrips, const StripsType &strips, const IdsType &descIds, vtkIdType origId) : pd(pd), pStrips(pStrips), origId(origId) {

    const StripPtsType &pts = pStrips.pts;
    const Base &base = pStrips.base;

    vtkIdType i, num;
    const vtkIdType *cell;

    double pt[3];

    for (auto id : descIds) {
        pd->GetCellPoints(id, num, cell);

        Poly p;

        for (i = 0; i < num; i++) {
            pd->GetPoint(cell[i], pt);

            double proj[2];
            Transform(pt, proj, base);

            p.emplace_back(proj[0], proj[1], 0, cell[i]);

        }

        polys.push_back(p);

        pd->DeleteCell(id);
    }

    for (auto &strip : strips) {
        Poly p;

        for (auto &sp : strip) {
            const double *pt = pts.at(sp.ind).pt;

            double proj[2];
            Transform(pt, proj, base);

            p.emplace_back(proj[0], proj[1], 0, NOTSET, sp.ind);
        }

        p.pop_back();

        double n[3];
        ComputeNormal(p, n);

        if (n[2] < 0) {
            Poly q(p.rbegin(), p.rend());
            p.swap(q);
        }

        innerIds.push_back(polys.size());

        polys.push_back(p);
    }

}

void Merger::Run () {
    // mergen mit hilfe von vtkKdTree und vtkModifiedBSPTree

    vtkPoints *pdPts = pd->GetPoints();
    vtkIdTypeArray *origCellIds = vtkIdTypeArray::SafeDownCast(pd->GetCellData()->GetScalars("OrigCellIds"));

    assert(origCellIds != nullptr);

    std::vector<GroupType> groups(polys.size());

    PolysType::const_iterator itrA, itrB;

    std::size_t i {0};

    for (itrA = polys.begin(); itrA != polys.end(); ++itrA) {
        if (std::find(innerIds.begin(), innerIds.end(), i) != innerIds.end()) {
            std::size_t j {0};
            for (itrB = polys.begin(); itrB != polys.end(); ++itrB) {
                if (itrA != itrB && PointInPoly(*itrB, *itrA->begin())) {
                    groups[j].push_back(i);
                }
                j++;
            }
        }
        i++;
    }

    std::size_t parent = 0;

    for (auto &group : groups) {
        GroupType parents;

        for (auto &index : group) {
            const GroupType &_group = groups[index];
            parents.insert(parents.end(), _group.begin(), _group.end());
        }

        std::sort(group.begin(), group.end());
        std::sort(parents.begin(), parents.end());

        GroupType _group {parent++};
        std::set_difference(group.begin(), group.end(), parents.begin(), parents.end(), std::back_inserter(_group));

#ifdef DEBUG
        std::cout << "[";
        for (auto &index : _group) {
            std::cout << index << ", ";
        }
        std::cout << "]" << std::endl;
#endif

        PolysType merged;

        MergeGroup(_group, merged);

        std::map<vtkIdType, vtkIdType> newIds;

        for (auto &poly : merged) {
            auto newCell = vtkSmartPointer<vtkIdList>::New();

            for (auto &p : poly) {
                vtkIdType id = p.id;

                if (id == NOTSET) {
                    auto itr = newIds.find(p.otherId);

                    if (itr == newIds.end()) {
                        auto &q = pStrips.pts.at(p.otherId);

                        id = pdPts->InsertNextPoint(q.pt);
                        newIds.emplace(p.otherId, id);
                    } else {
                        id = itr->second;
                    }
                }

                newCell->InsertNextId(id);

            }

            pd->InsertNextCell(VTK_POLYGON, newCell);
            origCellIds->InsertNextValue(origId);
        }

    }

}

void Merger::MergeGroup (const GroupType &group, PolysType &merged) {
    if (group.size() == 1) {
        merged.push_back(polys.at(group.back()));

        return;
    }

    auto pts = vtkSmartPointer<vtkPoints>::New();
    pts->SetDataTypeToDouble();

    IndexedPolysType indexedPolys;

    ReferencedPointsType refPts;

    SourcesType sources;
    std::size_t src = 0;

    for (auto &index : group) {
        const Poly &poly = polys.at(index);

        IndexedPoly ids;

        for (auto &p : poly) {
            vtkIdType id = pts->InsertNextPoint(p.x, p.y, p.z);

            ids.push_back(id);
            sources.emplace(id, src);

            refPts.emplace(id, p);
        }

        indexedPolys.push_back(std::move(ids));
        src++;
    }

    auto kdTree = vtkSmartPointer<vtkKdTree>::New();
    kdTree->OmitZPartitioning();
    kdTree->BuildLocatorFromPoints(pts);

    auto linesA = vtkSmartPointer<vtkPolyData>::New();
    linesA->SetPoints(pts);
    linesA->Allocate(1);

    IndexedPoly::const_iterator itrA, itrB;

    for (const auto &ids : indexedPolys) {
        for (itrA = ids.begin(); itrA != ids.end(); ++itrA) {
            itrB = itrA+1;
            if (itrB == ids.end()) {
                itrB = ids.begin();
            }

            vtkIdList *line = vtkIdList::New();
            line->InsertNextId(*itrA);
            line->InsertNextId(*itrB);

            linesA->InsertNextCell(VTK_LINE, line);

            line->Delete();
        }
    }

#ifdef DEBUG
    WriteVTK("linesA.vtk", linesA);
#endif

    auto bspTreeA = vtkSmartPointer<vtkModifiedBSPTree>::New();
    bspTreeA->SetDataSet(linesA);

    int n = 0;

    PolyConnsType polyConns;

    FindConns(linesA, kdTree, bspTreeA, polyConns, indexedPolys, sources, n);

    PolyConnsType connected {{0, {}}};
    _IdsType restricted; // keine der conns darf im gleichen punkt beginnen

    auto linesB = vtkSmartPointer<vtkPolyData>::New();
    linesB->SetPoints(pts);
    linesB->Allocate(1);

    auto bspTreeB = vtkSmartPointer<vtkModifiedBSPTree>::New();
    bspTreeB->SetDataSet(linesB);

    ConnsType firstConns;

    std::size_t i, numPolys = indexedPolys.size();

    double ptA[3], ptB[3];

    while (connected.size() < numPolys) {

        bool foundOne = false;

        for (i = 1; i < numPolys; i++) {
            if (connected.count(i) == 0) {
                const ConnsType &conns = polyConns[i];

                for (auto &conn : conns) {
                    if (connected.count(sources.at(conn.j)) == 1
                        && restricted.count(conn.j) == 0) {

                        pts->GetPoint(conn.i, ptA);
                        pts->GetPoint(conn.j, ptB);

                        if (bspTreeB->IntersectWithLine(ptA, ptB, 1e-5, nullptr, nullptr) == 0) {
                            connected[sources.at(conn.i)].push_back(conn);

                            // das andere poly auch aktualisieren
                            connected[sources.at(conn.j)].emplace_back(conn.d, conn.j, conn.i);

                            restricted.insert(conn.i);
                            restricted.insert(conn.j);

                            vtkIdList *line = vtkIdList::New();
                            line->InsertNextId(conn.i);
                            line->InsertNextId(conn.j);

                            linesB->InsertNextCell(VTK_LINE, line);

                            line->Delete();

                            bspTreeB->Modified();

                            foundOne = true;

                            firstConns.push_back(conn);

                            break;
                        }

                    }
                }
            }
        }

        if (!foundOne) {
            if (!FindConns(linesA, kdTree, bspTreeA, polyConns, indexedPolys, sources, n)) {
                throw std::runtime_error("Merging failed.");
            }
        }
    }

    std::map<std::size_t, std::vector<std::size_t>> chains;

    PolyConnsType::const_iterator itrC;

    for (itrC = connected.begin(); itrC != connected.end(); ++itrC) {
        auto &chain = chains[itrC->first];
        chain.push_back(itrC->first);

        while (chain.back() != 0) {
            chain.push_back(sources.at(connected.at(chain.back()).front().j));
        }
    }

#ifdef DEBUG
    std::cout << connected;

    decltype(chains)::const_iterator itrD;

    for (itrD = chains.begin(); itrD != chains.end(); ++itrD) {
        std::cout << itrD->first << ": [";
        for (auto &id : itrD->second) {
            std::cout << id << ", ";
        }
        std::cout << "]" << std::endl;
    }
#endif

    std::set<std::size_t> solved {0};

    std::deque<std::size_t> searchInds;

    for (i = 1; i < numPolys; i++) {
        if (connected.at(i).size() == 1) {
            searchInds.push_back(i);
        }
    }

    while (!searchInds.empty()) {
        PriosType prios;

        for (auto ind : searchInds) {
            PolyPriosType polyPrios;

#ifdef DEBUG
            std::cout << "ind " << ind << std::endl;
#endif

            const Conn &first = connected.at(ind).back();

            for (auto &conn : polyConns.at(ind)) {
                auto &src = sources.at(conn.j);

                if (polyPrios.count(src) == 1) {
                    continue;
                }

                if (conn.i != first.i
                    && conn.j != first.j
                    && restricted.count(conn.i) == 0
                    && restricted.count(conn.j) == 0) {

                    pts->GetPoint(conn.i, ptA);
                    pts->GetPoint(conn.j, ptB);

                    if (bspTreeB->IntersectWithLine(ptA, ptB, 1e-5, nullptr, nullptr) == 0) {
                        auto &chainA = chains.at(ind),
                            &chainB = chains.at(src);

                        std::set<std::size_t> _chainA(chainA.begin(), chainA.end()),
                            _chainB(chainB.begin(), chainB.end());

                        // gemeinsame eltern
                        std::set<std::size_t> shared;

                        std::set_intersection(_chainA.begin(), _chainA.end(), _chainB.begin(), _chainB.end(), std::inserter(shared, shared.end()));

                        // gemeinsame eltern müssen sich alle in solved befinden
                        if (std::includes(solved.begin(), solved.end(), shared.begin(), shared.end())) {
                            std::set<std::size_t> solvable;

                            std::set_difference(_chainA.begin(), _chainA.end(), solved.begin(), solved.end(), std::inserter(solvable, solvable.end()));
                            std::set_difference(_chainB.begin(), _chainB.end(), solved.begin(), solved.end(), std::inserter(solvable, solvable.end()));

                            polyPrios.emplace(std::piecewise_construct,
                                std::forward_as_tuple(src),
                                std::forward_as_tuple(conn, solvable, -conn.d));
                        }
                    }
                }
            }

            PolyPriosType::const_iterator itr;
            for (itr = polyPrios.begin(); itr != polyPrios.end(); ++itr) {
                prios.insert(itr->second);
            }
        }

        if (!prios.empty()) {
            auto &prio = *prios.rbegin();

#ifdef DEBUG
            std::cout << "found " << prio << std::endl;
#endif

            auto &conns = connected.at(sources.at(prio.conn.i));

            conns.push_back(prio.conn);

            connected.at(sources.at(prio.conn.j)).emplace_back(prio.conn.d, prio.conn.j, prio.conn.i);

            restricted.insert(prio.conn.i);
            restricted.insert(prio.conn.j);

            vtkIdList *line = vtkIdList::New();
            line->InsertNextId(prio.conn.i);
            line->InsertNextId(prio.conn.j);

            linesB->InsertNextCell(VTK_LINE, line);

            line->Delete();

            bspTreeB->Modified();

            solved.insert(prio.solvable.begin(), prio.solvable.end());

            searchInds.erase(std::find(searchInds.begin(), searchInds.end(), sources.at(prio.conn.i)));

            auto itr = std::find(searchInds.begin(), searchInds.end(), sources.at(prio.conn.j));

            if (itr != searchInds.end()) {
                searchInds.erase(itr);
            }
        } else {
            if (!FindConns(linesA, kdTree, bspTreeA, polyConns, indexedPolys, sources, n)) {
                break;
            }
        }
    }

#ifdef DEBUG
    std::cout << connected;
#endif

    // fallback

    double pt[3];

    if (!searchInds.empty()) {
        for (auto ind : searchInds) {
            std::vector<std::size_t> newChain;

            for (auto c : chains.at(ind)) {
                if (solved.find(c) != solved.end()) {
                    break;
                }

                newChain.push_back(c);
            }

            ConnsType &conns = connected.at(ind);

            decltype(newChain)::const_reverse_iterator itr;

            for (itr = newChain.rbegin(); itr != newChain.rend(); itr++) {
                // gesucht ist hier die kürzeste verbindung

#ifdef DEBUG
                std::cout << "itr " << *itr << std::endl;
#endif

                // polyConns.at(*itr) ist nach d sortiert

                std::shared_ptr<Conn> found;

                for (auto &conn : polyConns.at(*itr)) {
                    auto &src = sources.at(conn.j);

                    if (solved.find(src) != solved.end()) {
                        if (restricted.count(conn.i) == 0
                            // && restricted.count(conn.j) == 0
                            && std::find_if(conns.begin(), conns.end(), [&conn](const Conn &other) { return conn.i == other.i || conn.j == other.j; }) == conns.end()) {

                            pts->GetPoint(conn.i, ptA);
                            pts->GetPoint(conn.j, ptB);

                            auto intersPts = vtkSmartPointer<vtkPoints>::New();
                            intersPts->SetDataTypeToDouble();

                            auto c = bspTreeB->IntersectWithLine(ptA, ptB, 1e-5, intersPts, nullptr);

                            if (c == 0) {
                                found = std::make_shared<Conn>(conn);

                                break;
                            }

                            // wenn schnittpunkte existieren, dann müssen alle mit ptB übereinstimmen

                            vtkIdType i, numPts = intersPts->GetNumberOfPoints();

                            std::set<Point3d> foundPts {{ptB[0], ptB[1], ptB[2]}};

                            for (i = 0; i < numPts; i++) {
                                intersPts->GetPoint(i, pt);
                                foundPts.emplace(pt[0], pt[1], pt[2]);
                            }

                            if (foundPts.size() == 1) {
                                found = std::make_shared<Conn>(conn);

                                break;
                            }

                        }
                    }
                }

                if (found) {
#ifdef DEBUG
                    std::cout << "found " << *found << std::endl;
#endif

                    conns.push_back(*found);

                    connected.at(sources.at(found->j)).emplace_back(found->d, found->j, found->i);

                    restricted.insert(found->i);
                    restricted.insert(found->j);

                    vtkIdList *line = vtkIdList::New();
                    line->InsertNextId(found->i);
                    line->InsertNextId(found->j);

                    linesB->InsertNextCell(VTK_LINE, line);

                    line->Delete();

                    bspTreeB->Modified();

                    solved.insert(*itr);

                } else {
                    throw std::runtime_error("Merging failed.");
                }

            }
        }
    }

#ifdef DEBUG
    WriteVTK("linesB.vtk", linesB);
#endif

    ConnsType2 usedConns(firstConns.begin(), firstConns.end());

    IndexedPoly polyA {indexedPolys.front()};

    MergeStage1(indexedPolys, refPts, sources, firstConns, polyA);

    IndexedPolysType splitted {polyA};

    ConnsType2 leftConns;

    for (itrC = connected.begin(); itrC != connected.end(); ++itrC) {
        if (itrC->first == 0) {
            continue;
        }

        auto &conns = itrC->second;

        ConnsType::const_iterator itr;

        for (itr = conns.begin()+1; itr != conns.end(); ++itr) {
            Conn conn(0, itr->j, itr->i);

            if (usedConns.find(conn) == usedConns.end()) {
                if (itr->i < itr->j) {
                    leftConns.emplace(0, itr->i, itr->j);
                } else {
                    leftConns.insert(std::move(conn));
                }
            }
        }

    }

#ifdef DEBUG
    std::cout << "leftConns: [";
    for (auto &conn : leftConns) {
        std::cout << conn << ", ";
    }
    std::cout << "]" << std::endl;
#endif

    MergeStage2(leftConns, refPts, usedConns, splitted);

    PolysType newPolys;
    GetPolys(refPts, splitted, newPolys);

#ifdef DEBUG
    WritePolys("merged_stage2.vtk", newPolys);
#endif

    std::move(newPolys.begin(), newPolys.end(), std::back_inserter(merged));

}

bool Merger::FindConns (vtkPolyData *lines, vtkSmartPointer<vtkKdTree> kdTree, vtkSmartPointer<vtkModifiedBSPTree> bspTree, PolyConnsType &polyConns, const IndexedPolysType &indexedPolys, const SourcesType &sources, int &n) {

    vtkPoints *pts = lines->GetPoints();

    if (n > pts->GetNumberOfPoints()) {
        return false;
    }

    n += 10;

    auto foundPts = vtkSmartPointer<vtkIdList>::New();

    vtkIdType i, numPts;

    vtkIdType idB;

    auto lineIds = vtkSmartPointer<vtkIdList>::New();

    double ptA[3], ptB[3];

    bool good;

    vtkIdType j;
    vtkIdType _idA, _idB;

    std::map<std::size_t, std::set<Conn, ConnCmp>> _polyConns;

    auto line = vtkSmartPointer<vtkIdList>::New();

    for (const auto &ids : indexedPolys) {
        for (vtkIdType idA : ids) {
            pts->GetPoint(idA, ptA);

            kdTree->FindClosestNPoints(n, ptA, foundPts);

            numPts = foundPts->GetNumberOfIds();

            for (i = 0; i < numPts; i++) {
                idB = foundPts->GetId(i);

                auto srcA = sources.at(idA),
                    srcB = sources.at(idB);

                if (srcA == srcB) {
                    continue;
                }

                pts->GetPoint(idB, ptB);

                good = true;

                if (bspTree->IntersectWithLine(ptA, ptB, 1e-5, nullptr, lineIds) == 1) {
                    for (j = 0; j < lineIds->GetNumberOfIds(); j++) {
                        lines->GetCellPoints(lineIds->GetId(j), line);

                        _idA = line->GetId(0);
                        _idB = line->GetId(1);

                        if (_idA != idA && _idA != idB
                            && _idB != idA && _idB != idB) {

                            good = false;
                            break;
                        }
                    }
                }

                if (good) {
                    double d = vtkMath::Distance2BetweenPoints(ptA, ptB);

                    _polyConns[srcA].emplace(d, idA, idB);
                    _polyConns[srcB].emplace(d, idB, idA);
                }
            }
        }
    }

    decltype(_polyConns)::const_iterator itr;

    for (itr = _polyConns.begin(); itr != _polyConns.end(); ++itr) {
        auto &_conns = itr->second;

        ConnsType conns(_conns.begin(), _conns.end());
        std::sort(conns.begin(), conns.end());

        polyConns[itr->first].swap(conns);

    }

    return true;
}

void Merger::MergeStage1 (const IndexedPolysType &indexedPolys, [[maybe_unused]] const ReferencedPointsType &refPts, const SourcesType &sources, const ConnsType &conns, IndexedPoly &polyA) {

    for (const auto &conn : conns) {
        auto itrA = std::find(polyA.begin(), polyA.end(), conn.j);

        assert(itrA != polyA.end());

        IndexedPoly polyB(indexedPolys.at(sources.at(conn.i)));

        auto itrB = std::find(polyB.begin(), polyB.end(), conn.i);

        assert(itrB != polyB.end());

        std::rotate(polyA.begin(), itrA, polyA.end());
        std::rotate(polyB.begin(), itrB, polyB.end());

        IndexedPoly newPoly {polyA};
        newPoly.push_back(polyA.front());
        newPoly.push_back(polyB.front());

        newPoly.insert(newPoly.end(), polyB.rbegin(), polyB.rend());

        polyA.swap(newPoly);

    }

#ifdef DEBUG
    PolysType newPolys;
    GetPolys(refPts, {polyA}, newPolys);

    WritePolys("merged_stage1.vtk", newPolys);
#endif

}

void Merger::MergeStage2 (const ConnsType2 &conns, const ReferencedPointsType &refPts, const ConnsType2 &usedConns, IndexedPolysType &splitted) {
    std::set<Point3d> endPts;

    for (const Conn &conn : usedConns) {
        endPts.emplace(refPts.at(conn.i));
        endPts.emplace(refPts.at(conn.j));
    }

    IndexedPolysType::iterator itr;

    double vA[3], vB[3], w[3], ang, phi;

    const double n[] = {0, 0, 1};

    IndexedPoly::iterator itrA, itrB;

    IndexedPoly::iterator prev, next;

    for (auto &conn : conns) {
        for (itr = splitted.begin(); itr != splitted.end(); ++itr) {
            IndexedPoly poly(itr->begin(), itr->end());

            if (endPts.count(refPts.at(conn.i)) == 0)  {
                itrA = std::find(poly.begin(), poly.end(), conn.i);
            } else {
                Point3d::GetVec(refPts.at(conn.i), refPts.at(conn.j), w);

                itrA = poly.begin();
                while ((itrA = std::find(itrA, poly.end(), conn.i)) != poly.end()) {
                    next = itrA+1;
                    if (next == poly.end()) {
                        next = poly.begin();
                    }

                    if (itrA == poly.begin()) {
                        prev = poly.end()-1;
                    } else {
                        prev = itrA-1;
                    }

                    Point3d::GetVec(refPts.at(conn.i), refPts.at(*next), vA);
                    Point3d::GetVec(refPts.at(conn.i), refPts.at(*prev), vB);

                    ang = GetAngle(vA, vB, n);
                    phi = GetAngle(vA, w, n);

                    if (phi < ang) {
                        break;
                    }

                    ++itrA;
                }
            }

            if (itrA == poly.end()) {
                continue;
            }

            std::rotate(poly.begin(), itrA, poly.end());

            if (endPts.count(refPts.at(conn.j)) == 0)  {
                itrB = std::find(poly.begin(), poly.end(), conn.j);
            } else {
                Point3d::GetVec(refPts.at(conn.j), refPts.at(conn.i), w);

                itrB = poly.begin();
                while ((itrB = std::find(itrB, poly.end(), conn.j)) != poly.end()) {
                    next = itrB+1;
                    if (next == poly.end()) {
                        next = poly.begin();
                    }

                    if (itrB == poly.begin()) {
                        prev = poly.end()-1;
                    } else {
                        prev = itrB-1;
                    }

                    Point3d::GetVec(refPts.at(conn.j), refPts.at(*next), vA);
                    Point3d::GetVec(refPts.at(conn.j), refPts.at(*prev), vB);

                    ang = GetAngle(vA, vB, n);
                    phi = GetAngle(vA, w, n);

                    if (phi < ang) {
                        break;
                    }

                    ++itrB;
                }
            }

            if (itrB == poly.end()) {
                continue;
            }

            IndexedPoly newPolyA(poly.begin(), itrB+1);
            IndexedPoly newPolyB(itrB, poly.end());

            newPolyB.push_back(poly.front());

            splitted.erase(itr);

            splitted.push_back(std::move(newPolyA));
            splitted.push_back(std::move(newPolyB));

            endPts.emplace(refPts.at(conn.i));
            endPts.emplace(refPts.at(conn.j));

            break;
        }
    }

}


================================================
FILE: Merger.h
================================================
/*
Copyright 2012-2025 Ronald Römer

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#ifndef __Merger_h
#define __Merger_h

#include "vtkPolyDataBooleanFilter.h"

typedef std::vector<std::size_t> GroupType;

typedef std::map<vtkIdType, std::size_t> SourcesType;

class Conn {
public:
    Conn () = delete;
    Conn (double d, vtkIdType i, vtkIdType j) : d(d), i(i), j(j) {}

    double d;
    vtkIdType i, j;

    bool operator< (const Conn &other) const {
        return d < other.d;
    }

    friend std::ostream& operator<< (std::ostream &out, const Conn &c) {
        out << "Conn(d=" << c.d
            << ", i=" << c.i
            << ", j=" << c.j
            << ")";
        return out;
    }

};

struct ConnCmp {
    bool operator() (const Conn &a, const Conn &b) const {
        return std::tie(a.i, a.j) < std::tie(b.i, b.j);
    }
};

typedef std::vector<Conn> ConnsType;
typedef std::map<std::size_t, ConnsType> PolyConnsType;

typedef std::set<Conn, ConnCmp> ConnsType2;

inline std::ostream& operator<< (std::ostream &out, const PolyConnsType& polyConns) {
    PolyConnsType::const_iterator itr;

    for (itr = polyConns.begin(); itr != polyConns.end(); ++itr) {
        out << itr->first << ": [";
        for (auto &conn : itr->second) {
            out << conn << ", ";
        }
        out << "]" << std::endl;
    }

    return out;
}

class Prio {
public:
    Prio () = delete;
    Prio (const Conn &conn, const std::set<std::size_t> &solvable, double d) : conn(conn), solvable(solvable), d(d) {}

    Conn conn;
    std::set<std::size_t> solvable;
    double d;

    friend std::ostream& operator<< (std::ostream &out, const Prio &p) {
        out << "Prio(conn=" << p.conn
            << ", d=" << p.d
            << ")";
        return out;
    }
};

struct Cmp {
    bool operator() (const Prio &a, const Prio &b) const {
        const auto _a = a.solvable.size(),
            _b = b.solvable.size();
        return std::tie(_a, a.d) < std::tie(_b, b.d);
    }
};

typedef std::set<Prio, Cmp> PriosType;

typedef std::map<std::size_t, Prio> PolyPriosType;

class Merger {
    vtkPolyData *pd;
    const PStrips &pStrips;
    vtkIdType origId;

    PolysType polys;
    std::vector<std::size_t> innerIds;
public:
    Merger () = delete;
    Merger (vtkPolyData *pd, const PStrips &pStrips, const StripsType &strips, const IdsType &descIds, vtkIdType origId);
    void Run ();

private:
    void MergeGroup (const GroupType &group, PolysType &merged);
    bool FindConns (vtkPolyData *lines, vtkSmartPointer<vtkKdTree> kdTree, vtkSmartPointer<vtkModifiedBSPTree> bspTree, PolyConnsType &polyConns, const IndexedPolysType &indexedPolys, const SourcesType &sources, int &n);

    void MergeStage1 (const IndexedPolysType &indexedPolys, const ReferencedPointsType &refPts, const SourcesType &sources, const ConnsType &conns, IndexedPoly &polyA);
    void MergeStage2 (const ConnsType2 &conns, const ReferencedPointsType &refPts, const ConnsType2 &usedConns, IndexedPolysType &splitted);
};

#endif


================================================
FILE: Optimize.cxx
================================================
/*
Copyright 2012-2025 Ronald Römer

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#include "Optimize.h"

#include <vtkCellArrayIterator.h>
#include <vtkModifiedBSPTree.h>
#include <vtkCellData.h>
#include <vtkPolygon.h>
#include <vtkSmartPointer.h>

// #define _DEBUG

PreventEqualCaptPoints::PreventEqualCaptPoints (vtkPolyData *pdA, vtkPolyData *pdB) : pdA(pdA), pdB(pdB) {}

void PreventEqualCaptPoints::Run () {
#ifdef _DEBUG
    WriteVTK("captA.vtk", pdA);
    WriteVTK("captB.vtk", pdB);
#endif

    pdA->BuildLinks();
    pdB->BuildLinks();

    Find(pdA, pdB, "A");

#ifdef _DEBUG
    WriteVTK("modB.vtk", pdB);
#endif

    Find(pdB, pdA, "B");

#ifdef _DEBUG
    WriteVTK("modA.vtk", pdA);
#endif
}

void PreventEqualCaptPoints::Find (vtkPolyData *pd, vtkPolyData *other, [[maybe_unused]] const std::string &name) {
#ifdef _DEBUG
    std::cout << "Find(" << name << ")" << std::endl;
#endif

    vtkIdType num;
    const vtkIdType *poly;

    vtkIdType i, j;

    std::set<Pair> lines;

    auto polyItr = vtk::TakeSmartPointer(pd->GetPolys()->NewIterator());

    for (polyItr->GoToFirstCell(); !polyItr->IsDoneWithTraversal(); polyItr->GoToNextCell()) {
        polyItr->GetCurrentCell(num, poly);

        for (i = 0; i < num; i++) {
            j = i+1;

            if (j == num) {
                j = 0;
            }

            if (poly[i] < poly[j]) {
                lines.emplace(poly[i], poly[j]);
            } else {
                lines.emplace(poly[j], poly[i]);
            }
        }
    }

    auto tree = vtkSmartPointer<vtkModifiedBSPTree>::New();
    tree->SetDataSet(other);
    tree->BuildLocator();

    auto pts = vtkSmartPointer<vtkPoints>::New();
    pts->SetDataTypeToDouble();

    auto cells = vtkSmartPointer<vtkIdList>::New();

    double pA[3], pB[3];

    vtkIdType cellId;

    double tr[2];

#ifdef _DEBUG
    auto pdVerts = vtkSmartPointer<vtkPolyData>::New();
    pdVerts->Allocate(1);

    auto ptsVerts = vtkSmartPointer<vtkPoints>::New();
    ptsVerts->SetDataTypeToDouble();
#endif

    std::map<vtkIdType, std::vector<SnapPoint>> pointSnaps;
    std::map<Point3d, std::vector<SnapEdge>> edgeSnaps;

    for (auto &line : lines) {
        pd->GetPoint(line.f, pA);
        pd->GetPoint(line.g, pB);

        if (tree->IntersectWithLine(pA, pB, 1e-5, pts, cells) == 0) {
            continue;
        }

        for (i = 0; i < pts->GetNumberOfPoints(); i++) {
            const double *pt = pts->GetPoint(i);

            Point3d sA(pt[0], pt[1], pt[2]);

            cellId = cells->GetId(i);

            other->GetCellPoints(cellId, num, poly);

            Base base(other->GetPoints(), num, poly);

            Poly polyA, polyB;

            GetPoly(other->GetPoints(), num, poly, polyA);

            FlattenPoly(polyA, polyB, base);

            Transform(pt, tr, base);

            Point3d sB(tr[0], tr[1], 0);

            if (PointInPoly(polyB, sB)) {
#ifdef _DEBUG
                auto vert = vtkSmartPointer<vtkIdList>::New();
                vert->InsertNextId(ptsVerts->InsertNextPoint(pt));

                pdVerts->InsertNextCell(VTK_VERTEX, vert);
#endif

                // snap auf ecke oder kante?

                auto snap = std::find_if(polyA.begin(), polyA.end(), [&](const Point3d &p) { return Point3d::GetDist(p, sA) < 1e-10; });

                if (snap != polyA.end()) {
                    double d = Point3d::GetDist(*snap, sA);

                    pointSnaps[snap->id].emplace_back(cellId, line, *snap, sA, d);

                } else {
                    // projektion auf kante

                    auto edgeProj = GetEdgeProj(polyA, sA);

                    if (edgeProj != nullptr) {
                        edgeSnaps[edgeProj->proj].emplace_back(cellId, line, edgeProj->edge, edgeProj->proj, sA, edgeProj->d);
                    }
                }
            }
        }
    }

    for (const auto& [id, snaps] : pointSnaps) {
        if (snaps.size() > 1) {
#ifdef _DEBUG
            std::cout << "id " << id << ", snaps" << std::endl;

            for (const auto &s : snaps) {
                std::cout << s << std::endl;
            }
#endif

            std::set<Pair> allLines;

            std::transform(snaps.begin(), snaps.end(), std::inserter(allLines, allLines.end()), [](const SnapPoint &p) { return p.line; });

#ifdef _DEBUG
            std::cout << "allLines" << std::endl;

            for (const auto &line : allLines) {
                std::cout << line << std::endl;
            }
#endif

            auto itrA = snaps.begin();
            decltype(itrA) itrB;

            double d;

            bool collapse = true;

            for (; itrA != snaps.end()-1 && collapse; ++itrA) {
                for (itrB = itrA+1; itrB < snaps.end(); ++itrB) {
                    d = Point3d::GetDist(itrA->inter, itrB->inter);

#ifdef _DEBUG
                    std::cout << d << std::endl;
#endif

                    if (d > 1e-10) {
                        collapse = false;
                        break;
                    }
                }
            }

            if (collapse) {
                continue;
            }

            if (allLines.size() == 1) {
                std::shared_ptr<Point3d> proj;
                ProjOnLine(pd, snaps.back().line, snaps.back().point, proj);

                PreventEqualCaptPoints::MovePoint(other, id, *proj);

            } else {
                // gemeinsamen punkt ermitteln

                std::set<vtkIdType> allIds;

                for (auto &line : allLines) {
                    allIds.insert(line.f);
                    allIds.insert(line.g);
                }

                if (allIds.size() == allLines.size()+1) {
                    std::vector<Pair> _allLines(allLines.begin(), allLines.end());

                    vtkIdType s = _allLines[0] & _allLines[1];

#ifdef _DEBUG
                    std::cout << "line "
                        << _allLines[0]
                        << " & "
                        << _allLines[1]
                        << " -> "
                        << s
                        << std::endl;
#endif

                    double pt[3];
                    pd->GetPoint(s, pt);

                    Point3d p(pt[0], pt[1], pt[2]);

                    PreventEqualCaptPoints::MovePoint(other, id, p);
                } else {
                    throw std::runtime_error("");
                }
            }
        }
    }

    using Snap = std::tuple<SnapEdge, Point3d, double>;

    struct Cmp {
        bool operator() (const Snap &l, const Snap &r) const {
            return std::get<2>(l) < std::get<2>(r);
        }
    };

    std::map<Pair, std::set<Snap, Cmp>> allEdgeSnaps;

    double pt[3], t;

    for (const auto& [proj, snaps] : edgeSnaps) {
        if (snaps.size() > 1) {
            if (snaps.size() > 2) {
#ifdef _DEBUG
                std::cout << proj << std::endl;

                for (const auto &s : snaps) {
                    std::cout << s << std::endl;

                    auto &line = s.line;

                    pd->GetPoint(line.f, pA);
                    pd->GetPoint(line.g, pB);

                    std::cout << Point3d(pA[0], pA[1], pA[2]) << std::endl;
                    std::cout << Point3d(pB[0], pB[1], pB[2]) << std::endl;
                }
#endif

                continue;
            }

            const auto &snapA = snaps[0];
            const auto &snapB = snaps[1];

            {
                Pair edge(snapA.edge);

                if (edge.f > edge.g) {
                    std::swap(edge.f, edge.g);
                }

                std::shared_ptr<Point3d> p;

                if (snapA.line == snapB.line) {
                    ProjOnLine(pd, snapA.line, snapA.proj, p);

                } else {
                    vtkIdType s = snapA.line & snapB.line;

                    pd->GetPoint(s, pt);

                    p = std::make_shared<Point3d>(pt[0], pt[1], pt[2]);
                }

                other->GetPoint(edge.f, pt);

                Point3d q(pt[0], pt[1], pt[2]);

                t = Point3d::GetDist(q, snapA.proj);

                allEdgeSnaps[edge].emplace(snapA, *p, t);
            }
        }
    }

    std::map<vtkIdType, Edges> newCells;

    for (const auto& [edge, data] : allEdgeSnaps) {
        Points pts;

        for (const auto &d : data) {
            const auto &p = std::get<1>(d);
            pts.emplace_back(p.x, p.y, p.z, other->GetPoints()->InsertNextPoint(p.x, p.y, p.z));
        }

        const auto &first = std::get<0>(*(data.begin()));

        auto neigs = vtkSmartPointer<vtkIdList>::New();

        other->GetCellEdgeNeighbors(first.cellId, first.edge.f, first.edge.g, neigs);

        if (neigs->GetNumberOfIds() != 1) {
            throw std::runtime_error("");
        }

        vtkIdType neig = neigs->GetId(0);

        auto _edge = Pair(first.edge.g, first.edge.f);

        if (edge == first.edge) {
            std::for_each(pts.begin(), pts.end(), [&](const Point3d &p) { newCells[first.cellId][first.edge].emplace_back(p); });
            std::for_each(pts.rbegin(), pts.rend(), [&](const Point3d &p) { newCells[neig][_edge].emplace_back(p); });
        } else {
            std::for_each(pts.rbegin(), pts.rend(), [&](const Point3d &p) { newCells[first.cellId][first.edge].emplace_back(p); });
            std::for_each(pts.begin(), pts.end(), [&](const Point3d &p) { newCells[neig][_edge].emplace_back(p); });
        }
    }

    for (const auto& [cellId, edges] : newCells) {
        PreventEqualCaptPoints::TriangulateCell(other, cellId, edges);
        other->DeleteCell(cellId);
    }

    other->RemoveDeletedCells();

#ifdef _DEBUG
    pdVerts->SetPoints(ptsVerts);

    auto fileName = "verts" + name + ".vtk";

    WriteVTK(fileName.c_str(), pdVerts);
#endif

}

IdsType PreventEqualCaptPoints::TriangulateCell (vtkPolyData *pd, vtkIdType cellId, const Edges &edges) {
    vtkIdTypeArray *origCellIds = vtkIdTypeArray::SafeDownCast(pd->GetCellData()->GetScalars("OrigCellIds"));

    IdsType newCellIds;

    vtkIdType num;
    const vtkIdType *poly;

    pd->GetCellPoints(cellId, num, poly);

    Poly _poly;

    double pt[3];

    vtkIdType i, j;

    vtkIdType newNum = num;

    for (i = 0; i < num; i++) {
        j = i+1;

        if (j == num) {
            j = 0;
        }

        pd->GetPoint(poly[i], pt);

        _poly.emplace_back(pt[0], pt[1], pt[2], poly[i]);

        Pair edge(poly[i], poly[j]);

        auto itr = edges.find(edge);

        if (itr != edges.end()) {
            auto &pts = itr->second;

            for (const auto &p : pts) {
                _poly.emplace_back(p);

                newNum++;
            }
        }
    }

    auto vtkPoly = vtkSmartPointer<vtkPolygon>::New();

    vtkPoly->GetPointIds()->SetNumberOfIds(newNum);
    vtkPoly->GetPoints()->SetNumberOfPoints(newNum);

    Base base(pd->GetPoints(), num, poly);

    Poly flattened;

    FlattenPoly(_poly, flattened, base);

    for (const auto &p : flattened) {
        vtkPoly->GetPointIds()->SetId(p.id, p.id);
        vtkPoly->GetPoints()->SetPoint(p.id, p.x, p.y, p.z);
    }

    auto triangles = vtkSmartPointer<vtkIdList>::New();

#if (VTK_MAJOR_VERSION >= 9 && VTK_MINOR_VERSION > 3)
    if (vtkPoly->TriangulateLocalIds(0, triangles) != 1) {
        throw std::runtime_error("");
    }
#else
    if (vtkPoly->Triangulate(triangles) != 1) {
        throw std::runtime_error("");
    }
#endif

    auto ids = vtkSmartPointer<vtkIdList>::New();

    for (const auto &p : _poly) {
        ids->InsertNextId(p.id);
    }

    double pA[3], pB[3], pC[3];

    for (i = 0; i < triangles->GetNumberOfIds(); i += 3) {
        pd->GetPoint(ids->GetId(triangles->GetId(i)), pA);
        pd->GetPoint(ids->GetId(triangles->GetId(i+1)), pB);
        pd->GetPoint(ids->GetId(triangles->GetId(i+2)), pC);

        Poly p = { {pA[0], pA[1], pA[2]}, {pB[0], pB[1], pB[2]}, {pC[0], pC[1], pC[2]} };

        double quality = GetTriangleQuality(p);

        if (quality < 0.001) {
            throw std::runtime_error("");
        }
    }

    vtkIdType origId = origCellIds->GetValue(cellId);

    auto triangle = vtkSmartPointer<vtkIdList>::New();
    triangle->SetNumberOfIds(3);

    for (i = 0; i < triangles->GetNumberOfIds(); i += 3) {
        triangle->SetId(0, ids->GetId(triangles->GetId(i)));
        triangle->SetId(1, ids->GetId(triangles->GetId(i+1)));
        triangle->SetId(2, ids->GetId(triangles->GetId(i+2)));

        newCellIds.push_back(pd->InsertNextCell(VTK_TRIANGLE, triangle));

        origCellIds->InsertNextValue(origId);
    }

    return newCellIds;

}

void PreventEqualCaptPoints::MovePoint (vtkPolyData *pd, vtkIdType ind, const Point3d &p) {
    auto cells = vtkSmartPointer<vtkIdList>::New();
    pd->GetPointCells(ind, cells);

    vtkIdType i, cellId;

    for (i = 0; i < cells->GetNumberOfIds(); i++) {
        cellId = cells->GetId(i);

        if (pd->GetCellType(cellId) == VTK_POLYGON) {
            PreventEqualCaptPoints::TriangulateCell(pd, cellId, {});
            pd->DeleteCell(cellId);
        }
    }

#ifdef _DEBUG
    double pt[3];
    pd->GetPoints()->GetPoint(ind, pt);

    Point3d q(pt[0], pt[1], pt[2]);

    std::cout << q
        << " -> "
        << p
        << std::endl;
#endif

    pd->GetPoints()->SetPoint(ind, p.x, p.y, p.z);
}


================================================
FILE: Optimize.h
================================================
/*
Copyright 2012-2025 Ronald Römer

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#ifndef __Optimize_h
#define __Optimize_h

#include "Utilities.h"

typedef std::map<Pair, Points> Edges;

class SnapPoint {
public:
    SnapPoint () = delete;
    SnapPoint (vtkIdType cellId, const Pair &line, const Point3d &point, const Point3d &inter, double d) : cellId(cellId), line(line), point(point), inter(inter), d(d) {}

    vtkIdType cellId;
    Pair line;
    Point3d point;
    Point3d inter;
    double d;

    friend std::ostream& operator<< (std::ostream &out, const SnapPoint &s) {
        out << "cellId " << s.cellId
            << ", line " << s.line
            << ", point " << s.point
            << ", inter " << s.inter
            << ", d " << s.d;

        return out;
    }
};

class SnapEdge {
public:
    SnapEdge () = delete;
    SnapEdge (vtkIdType cellId, const Pair &line, const Pair &edge, const Point3d &proj, const Point3d &inter, double d) : cellId(cellId), line(line), edge(edge), proj(proj), inter(inter), d(d) {}

    vtkIdType cellId;
    Pair line;
    Pair edge;
    Point3d proj;
    Point3d inter;
    double d;

    friend std::ostream& operator<< (std::ostream &out, const SnapEdge &s) {
        out << "cellId " << s.cellId
            << ", line " << s.line
            << ", edge " << s.edge
            << ", proj " << s.proj
            << ", inter " << s.inter
            << ", d " << s.d;

        return out;
    }
};

class PreventEqualCaptPoints {
    vtkPolyData *pdA, *pdB;
public:
    static IdsType TriangulateCell (vtkPolyData *pd, vtkIdType cellId, const Edges &edges);
    static void MovePoint (vtkPolyData *pd, vtkIdType ind, const Point3d &p);

    PreventEqualCaptPoints () = delete;
    PreventEqualCaptPoints (vtkPolyData *pdA, vtkPolyData *pdB);
    void Run ();
private:
    void Find (vtkPolyData *pd, vtkPolyData *other, const std::string &name);
};

#endif


================================================
FILE: README.md
================================================
# vtkbool [![CMake](https://github.com/zippy84/vtkbool/actions/workflows/cmake.yml/badge.svg)](https://github.com/zippy84/vtkbool/actions/workflows/cmake.yml) [![codecov](https://codecov.io/gh/zippy84/vtkbool/branch/master/graph/badge.svg?token=EUV9QKEW1M)](https://codecov.io/gh/zippy84/vtkbool) [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.10461186.svg)](https://zenodo.org/doi/10.5281/zenodo.10461186)

## About

This is an extension of the graphics library VTK. The goal of the extension is to equip the library with rebust boolean operations on polygonal meshes. I started the project at the end of my studies in mechanical engineering at the University of Applied Sciences ([HTWK](http://htwk-leipzig.de/)) in Leipzig. I used VTK to develop a program, which I had to create for a paper. At this time I would have wished, that this feature already exists. There was several implementations from third parties, but after some tests, I came to the conclusion, that none of them worked correct. I decided to start with my own implementation. This library is the result of my efforts.

## Features

- based on VTK
- 4 operation types available (union, intersection, difference and difference2 - difference with interchanged operands)
- triangulation is not needed
- all types of polygonal cells are supported (triangles, quads, polygons, triangle-strips)
- triangle-strips and quads will be transformed into triangles (quads only if their points are not on the same plane)
- non-convex polygons are allowed
- meshes can be stacked (coplanar polygons are right handled)
- the meshes don’t need to be watertight
- CellData is passed (attached by the rules of vtkAppendPolyData)
- contact-lines are available in the 3th output
- the filter is able to embed holes
- compileable as ParaView plugin
- Python wrapped

## Limitations

- the filter assumes well defined triangles, quads and polygons
- PointData is not preserved - you have to do your own mapping with *OrigCellIdsA* and *OrigCellIdsB*

## Requirements

- CMake >= 3.12
- VTK >= 9.0
- C++17 compiler

### Optional

- ParaView >= 5.0
- Python 3.x

## Library

To include vtkbool into your program, you have to compile it as a library. All you need is an installation of VTK with header files. If you have installed VTK over your package manager, CMake is able to find the required files. Otherwise you have to set **VTK\_DIR** manually. It must be a path like */home/zippy/VTK9/lib/cmake/vtk-9.1* or *C:/Users/zippy/VTK9/lib/cmake/vtk-9.1*.

The usage of the library is very simple. Look at the example in the section below. You can set the operation mode by calling one of the named methods:

- `SetOperModeToNone`
- `SetOperModeToUnion`
- `SetOperModeToIntersection`
- `SetOperModeToDifference`
- `SetOperModeToDifference2`

The alternative is the more generic `SetOperMode`. The method must be called with the number of the desired operation, an integer between 0 and 4, with the same meaning as mentioned before. The default is Union.

### C++ Example

Create a directory somewhere in your file system, download vtkbool and unpack it into that.

```
mkdir example
cd example
git clone https://github.com/zippy84/vtkbool.git
```

Then create the following two files:

**test.cxx**

```C++
#include <vtkSmartPointer.h>
#include <vtkCubeSource.h>
#include <vtkCylinderSource.h>
#include <vtkPolyDataWriter.h>

#include "vtkPolyDataBooleanFilter.h"

int main (int argc, char *argv[]) {
    auto cube = vtkSmartPointer<vtkCubeSource>::New();
    cube->SetYLength(.5);

    auto cyl = vtkSmartPointer<vtkCylinderSource>::New();
    cyl->SetResolution(32);
    cyl->SetHeight(.5);
    cyl->SetCenter(0, .5, 0);

    auto bf = vtkSmartPointer<vtkPolyDataBooleanFilter>::New();
    bf->SetInputConnection(0, cube->GetOutputPort());
    bf->SetInputConnection(1, cyl->GetOutputPort());
    bf->SetOperModeToDifference();

    auto writer = vtkSmartPointer<vtkPolyDataWriter>::New();
    writer->SetInputConnection(bf->GetOutputPort());
    writer->SetFileName("result.vtk");
    writer->Update();

    return 0;
}
```

**CMakeLists.txt**

```CMake
cmake_minimum_required(VERSION 3.12 FATAL_ERROR)
project(test)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# find_package(VTK REQUIRED COMPONENTS FiltersSources IOLegacy)

find_package(VTK REQUIRED COMPONENTS FiltersSources IOLegacy FiltersExtraction FiltersGeometry FiltersModeling FiltersFlowPaths WrappingPythonCore)

if(VTK_FOUND)
    include_directories(vtkbool)
    add_subdirectory(vtkbool)

    add_executable(test test.cxx)
    target_link_libraries(test PRIVATE vtkbool ${VTK_LIBRARIES})

    vtk_module_autoinit(
        TARGETS test
        MODULES ${VTK_LIBRARIES}
    )
endif(VTK_FOUND)
```

Inside the `example` directory, create a subdirectory called `build` and `cd` into it. You should have a directory structure that looks something like this:

```
example
├── build
├── CMakeLists.txt
├── test.cxx
└── vtkbool
    ├── CMakeLists.txt
    ├── ...
    └── vtkPolyDataBooleanFilter.h
```

From inside the `build` directory, run `ccmake ..`, follow the instructions, and finally type `make`.

Running `./test` will now produce the `result.vtk` file.

## ParaView Plugin

To build the plugin you have to compile ParaView from source. Download the current version from <http://www.paraview.org> and follow the compilation instructions. As soon as ParaView is compiled, it may take a while, you can build the plugin by activating the **VTKBOOL_PARAVIEW** option within CMake. In CMake you also have to point to **ParaView_DIR** if CMake can't found it and it is not installed in a common location like */usr/lib* or */usr/local/lib*. Make sure **PARAVIEW_INSTALL_DEVELOPMENT_FILES** is set.

When everything has been compiled successfully, you can install the plugin.

## Python

The Python module will be generated automatically, if three conditions are met:

- vtkbool is configured as a library
- Python 3 is installed with header files
- VTK itself is wrapped to Python

After a successful compilation, the module can be used as follows:

```python
#!/usr/bin/env python

import sys
sys.path.append('/path/to/your/build/directory') # also look into the python files in the testing directory

from vtkmodules.vtkFiltersSources import vtkCubeSource, vtkSphereSource
from vtkmodules.vtkIOLegacy import vtkPolyDataWriter
from vtkbool import vtkPolyDataBooleanFilter

cube = vtkCubeSource()

sphere = vtkSphereSource()
sphere.SetCenter(.5, .5, .5)
sphere.SetThetaResolution(20)
sphere.SetPhiResolution(20)

bf = vtkPolyDataBooleanFilter()
bf.SetInputConnection(0, cube.GetOutputPort())
bf.SetInputConnection(1, sphere.GetOutputPort())
bf.SetOperModeToDifference()

# write the result, if you want ...

writer = vtkPolyDataWriter()
writer.SetInputConnection(bf.GetOutputPort())
writer.SetFileName('result.vtk')

writer.Update()
```

Or with VTK >= 9.4:

```python
#!/usr/bin/env python

import sys
sys.path.append('/path/to/your/build/directory')

from vtkmodules.vtkFiltersSources import vtkCubeSource, vtkSphereSource
from vtkmodules.vtkIOLegacy import vtkPolyDataWriter
from vtkmodules.util.execution_model import select_ports

from vtkbool import vtkPolyDataBooleanFilter, OPER_DIFFERENCE

cube = vtkCubeSource()
sphere = vtkSphereSource(center=[.5, .5, .5], theta_resolution=20, phi_resolution=20)

bf = vtkPolyDataBooleanFilter(oper_mode=OPER_DIFFERENCE)
cube >> bf
sphere >> select_ports(1, bf)

(bf >> vtkPolyDataWriter(file_name='result.vtk')).update()

```

## Conda

The library is also available at [conda-forge](https://anaconda.org/conda-forge/vtkbool). In your virtual environment you can install the package with:

```
conda install -c conda-forge vtkbool
```

Unlike in the python example, you need to import it like this:

```python
from vtkbool.vtkbool import vtkPolyDataBooleanFilter
```

## Errors and their meaning

- *Contact failed with ...*

  - *Found invalid intersection points.*

    This problem occurs when an intersection point is located on congruent edges of a self-intersecting polygon.

  - *Intersection points do not lie on the edges (cells a, b).*

    At least one intersection point does not lie on the boundary of the intersected polygon. The points of the polygon do not lie on a plane. The polygon is degenerated.

  - *Intersection goes through non-manifold edges.*

    Non-manifold edges are generally not a problem. Unless they are part of the intersection.

- *Cannot prevent equal capture points.*

  A capture point is the projection of a point onto one of the edges of the intersected polygon. This point, not the projection, is usually used by two lines that are assigned to the two adjacent polygons, sharing this edge. There is a case where two different points have the same projection. The error occurs when the problem could not be solved.

- *There is no contact.*

  What it says.

- *At least one line-end has only one neighbor.*

  The intersection is incomplete. Therefore the cell cannot be divided.

- *Strips are invalid.*

  There are intersection lines that intersect themselves. Either one of the inputs contains an assembly or there is one self-intersecting polygon that is involved in the intersection.

- *CutCells failed.*

  Will be printed out only, if some holes couldn't be merged into their outer polygons.

- *Boolean operation failed.*

  A boolean operation can fail at the end, if some of the intersection lines are not part of the result.

## Copyright

2012-2025 Ronald Römer

## License

Apache License, Version 2.0


================================================
FILE: Utilities.cxx
================================================
/*
Copyright 2012-2025 Ronald Römer

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#include "Utilities.h"

#include <cmath>

#include <vtkPoints.h>
#include <vtkIdList.h>
#include <vtkMath.h>
#include <vtkPolyData.h>
#include <vtkDataWriter.h>
#include <vtkSmartPointer.h>

#include <vtkPolyDataWriter.h>

double ComputeNormal (vtkPoints *pts, double *n, vtkIdType num, const vtkIdType *poly) {
    n[0] = 0; n[1] = 0; n[2] = 0;

    double pA[3], pB[3];

    vtkIdType i, a, b;

    for (i = 0; i < num; i++) {
        a = poly[i];
        b = poly[i+1 == num ? 0 : i+1];

        pts->GetPoint(a, pA);
        pts->GetPoint(b, pB);

        n[0] += (pA[1]-pB[1])*(pA[2]+pB[2]);
        n[1] += (pA[2]-pB[2])*(pA[0]+pB[0]);
        n[2] += (pA[0]-pB[0])*(pA[1]+pB[1]);
    }

    return vtkMath::Normalize(n);
}

void FindPoints (vtkKdTreePointLocator *pl, const double *pt, vtkIdList *pts, double tol) {
    pts->Reset();

    vtkPolyData *pd = vtkPolyData::SafeDownCast(pl->GetDataSet());

    vtkIdList *closest = vtkIdList::New();

    pl->FindPointsWithinRadius(std::max(1e-3, tol), pt, closest);

    vtkIdType i, numPts = closest->GetNumberOfIds();

    double c[3], v[3];

    for (i = 0; i < numPts; i++) {
        pd->GetPoint(closest->GetId(i), c);
        vtkMath::Subtract(pt, c, v);

        if (vtkMath::Norm(v) < tol) {
            pts->InsertNextId(closest->GetId(i));
        }
    }

    closest->Delete();
}

#ifdef DEBUG
void WriteVTK (const char *name, vtkPolyData *pd) {
    std::cout << "Writing " << name << std::endl;

    vtkPolyDataWriter *w = vtkPolyDataWriter::New();
    w->SetInputData(pd);
    w->SetFileName(name);
    w->Update();
    w->Delete();
}
#endif

double GetAngle (const double *vA, const double *vB, const double *n) {
    // http://math.stackexchange.com/questions/878785/how-to-find-an-angle-in-range0-360-between-2-vectors

    double _vA[3];

    vtkMath::Cross(n, vA, _vA);
    double ang = std::atan2(vtkMath::Dot(_vA, vB), vtkMath::Dot(vA, vB));

    if (ang < 0) {
        ang += 2*M_PI;
    }

    return ang;
}

Base::Base (vtkPoints *pts, vtkIdType num, const vtkIdType *poly) {
    ComputeNormal(pts, n, num, poly);

    double ptA[3],
        ptB[3];

    pts->GetPoint(poly[0], ptA);
    pts->GetPoint(poly[1], ptB);

    ei[0] = ptB[0]-ptA[0];
    ei[1] = ptB[1]-ptA[1];
    ei[2] = ptB[2]-ptA[2];

    vtkMath::Normalize(ei);

    ej[0] = n[1]*ei[2]-n[2]*ei[1];
    ej[1] = -n[0]*ei[2]+n[2]*ei[0];
    ej[2] = n[0]*ei[1]-n[1]*ei[0];

    vtkMath::Normalize(ej);

    d = n[0]*ptA[0]+n[1]*ptA[1]+n[2]*ptA[2];
}

void Transform (const double *in, double *out, const Base &base) {
    double x = base.ei[0]*in[0]+base.ei[1]*in[1]+base.ei[2]*in[2],
        y = base.ej[0]*in[0]+base.ej[1]*in[1]+base.ej[2]*in[2];

    out[0] = x;
    out[1] = y;
}

/*void BackTransform (const double *in, double *out, const Base &base) {
    double x = in[0]*base.ei[0]+in[1]*base.ej[0]+base.d*base.n[0],
        y = in[0]*base.ei[1]+in[1]*base.ej[1]+base.d*base.n[1],
        z = in[0]*base.ei[2]+in[1]*base.ej[2]+base.d*base.n[2];

    out[0] = x;
    out[1] = y;
    out[2] = z;
}*/

double ComputeNormal (const Poly &poly, double *n) {
    n[0] = 0; n[1] = 0; n[2] = 0;

    Poly::const_iterator itrA, itrB;

    for (itrA = poly.begin(); itrA != poly.end(); ++itrA) {
        itrB = itrA+1;

        if (itrB == poly.end()) {
            itrB = poly.begin();
        }

        const Point3d &ptA = *itrA,
            &ptB = *itrB;

        n[0] += (ptA.y-ptB.y)*(ptA.z+ptB.z);
        n[1] += (ptA.z-ptB.z)*(ptA.x+ptB.x);
        n[2] += (ptA.x-ptB.x)*(ptA.y+ptB.y);
    }

    return vtkMath::Normalize(n);
}

bool PointInPoly (const Poly &poly, const Point3d &p) {
    bool in = false;

    Poly::const_iterator itrA, itrB;

    for (itrA = poly.begin(); itrA != poly.end(); ++itrA) {
        itrB = itrA+1;

        if (itrB == poly.end()) {
            itrB = poly.begin();
        }

        const Point3d &ptA = *itrA,
            &ptB = *itrB;

        if ((ptA.x <= p.x || ptB.x <= p.x)
            && ((ptA.y < p.y && ptB.y >= p.y)
                || (ptB.y < p.y && ptA.y >= p.y))) {

            // schnittpunkt mit bounding box und strahlensatz
            if (ptA.x+(p.y-ptA.y)*(ptB.x-ptA.x)/(ptB.y-ptA.y) < p.x) {
                in = !in;
            }
        }
    }

    return in;
}

#ifdef DEBUG
void WritePolys (const char *name, const PolysType &polys) {
    auto pts = vtkSmartPointer<vtkPoints>::New();
    pts->SetDataTypeToDouble();

    auto pd = vtkSmartPointer<vtkPolyData>::New();
    pd->SetPoints(pts);
    pd->Allocate(1);

    for (auto &poly : polys) {
        auto cell = vtkSmartPointer<vtkIdList>::New();

        for (auto &p : poly) {
            cell->InsertNextId(pts->InsertNextPoint(p.x, p.y, p.z));
        }

        pd->InsertNextCell(VTK_POLYGON, cell);
    }

    WriteVTK(name, pd);
}
#endif

void GetPolys (const ReferencedPointsType &pts, const IndexedPolysType &indexedPolys, PolysType &polys) {
    for (const auto &poly : indexedPolys) {
        Poly newPoly;

        for (auto &id : poly) {
            newPoly.push_back(pts.at(id));
        }

        polys.push_back(std::move(newPoly));
    }
}

void GetPoly (vtkPoints *pts, vtkIdType num, const vtkIdType *poly, Poly &out) {
    vtkIdType i;

    double p[3];

    for (i = 0; i < num; i++) {
        pts->GetPoint(poly[i], p);

        out.emplace_back(p[0], p[1], p[2], poly[i]);
    }
}

void FlattenPoly (const Poly &poly, Poly &out, const Base &base) {
    double pt[3], tr[2];

    vtkIdType i = 0;

    for (auto &p : poly) {
        pt[0] = p.x;
        pt[1] = p.y;
        pt[2] = p.z;

        Transform(pt, tr, base);

        out.emplace_back(tr[0], tr[1], 0, i++);
    }
}

void FlattenPoly2 (const Poly &poly, Poly &out, const Base2 &base) {
    double pt[3], tr[3];

    for (auto &p : poly) {
        pt[0] = p.x;
        pt[1] = p.y;
        pt[2] = p.z;

        base.Transform(pt, tr);

        out.emplace_back(tr[0], tr[1], tr[2], p.id);
    }
}

std::shared_ptr<Proj> GetEdgeProj (const Poly &poly, const Point3d &p) {
    Poly::const_iterator itrA, itrB;

    double d, t;

    std::shared_ptr<Point3d> proj;

    for (itrA = poly.begin(); itrA != poly.end(); ++itrA) {
        itrB = itrA+1;

        if (itrB == poly.end()) {
            itrB = poly.begin();
        }

        ProjOnLine(*itrA, *itrB, p, &d, &t, proj);

        if (d > 0 && d < 1e-5 && t > 0 && t < 1) {
            return std::make_shared<Proj>(itrA->id, itrB->id, *proj, d);
        }

    }

    return nullptr;
}

void ProjOnLine (const Point3d &a, const Point3d &b, const Point3d &p, double *d, double *t, std::shared_ptr<Point3d> &proj) {
    double v[3], w[3];

    double v_ = Point3d::GetVec(a, b, v);
    double w_ = Point3d::GetVec(a, p, w);

    double pr = std::min(std::max(vtkMath::Dot(v, w), -1.), 1.);

    *d = std::sin(std::acos(pr))*w_;

    vtkMath::MultiplyScalar(v, std::sqrt(w_*w_-*d**d));

    proj = std::make_shared<Point3d>(a.x+v[0], a.y+v[1], a.z+v[2]);

    *t = pr*w_/v_;
}

void ProjOnLine (vtkPolyData *pd, const Pair &line, const Point3d &p, std::shared_ptr<Point3d> &proj) {
    double pA[3], pB[3];

    pd->GetPoint(line.f, pA);
    pd->GetPoint(line.g, pB);

    Point3d a(pA[0], pA[1], pA[2]);
    Point3d b(pB[0], pB[1], pB[2]);

    double d, t;

    ProjOnLine(a, b, p, &d, &t, proj);
}

vtkSmartPointer<vtkPolyData> CreatePolyData (const PolysType &polys) {
    auto pts = vtkSmartPointer<vtkPoints>::New();
    pts->SetDataTypeToDouble();

    auto pd = vtkSmartPointer<vtkPolyData>::New();
    pd->SetPoints(pts);
    pd->Allocate(100);

    for (const auto &poly : polys) {
        auto cell = vtkSmartPointer<vtkIdList>::New();

        for (const auto &p : poly) {
            cell->InsertNextId(pts->InsertNextPoint(p.x, p.y, p.z));
        }

        pd->InsertNextCell(VTK_POLYGON, cell);
    }

    pd->Squeeze();

    return pd;
}

double GetTriangleQuality (const Poly &poly) {
    double n[3];

    double l = ComputeNormal(poly, n);

    double d = 0;

    Poly::const_iterator itrA, itrB;

    for (itrA = poly.begin(); itrA != poly.end(); ++itrA) {
        itrB = itrA+1;

        if (itrB == poly.end()) {
            itrB = poly.begin();
        }

        d += std::sqrt(Point3d::GetDist(*itrA, *itrB));
    }

    return 10.392304845413264*l/(d*d);
}


================================================
FILE: Utilities.h
================================================
/*
Copyright 2012-2025 Ronald Römer

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#ifndef __Utilities_h
#define __Utilities_h

#include <iostream>
#include <type_traits>
#include <functional>
#include <vector>
#include <deque>
#include <map>
#include <set>
#include <memory>

#include <vtkPolyData.h>
#include <vtkKdTreePointLocator.h>
#include <vtkPoints.h>
#include <vtkIdList.h>
#include <vtkMath.h>
#include <vtkSmartPointer.h>

#define NOTSET -1

double GetAngle (const double *vA, const double *vB, const double *n);

double ComputeNormal (vtkPoints *pts, double *n, vtkIdType num, const vtkIdType *poly);

void FindPoints (vtkKdTreePointLocator *pl, const double *pt, vtkIdList *pts, double tol = 1e-6);

#ifdef DEBUG
void WriteVTK (const char *name, vtkPolyData *pd);
#endif

class Point3d {
public:
    const double x, y, z;
    vtkIdType id, otherId;

    Point3d () = delete;
    Point3d (const double x, const double y, const double z, vtkIdType id = NOTSET, vtkIdType otherId = NOTSET) : x(x), y(y), z(z), id(id), otherId(otherId) {}
    bool operator< (const Point3d &other) const {
        const long x1 = std::lround(x*1e5),
            y1 = std::lround(y*1e5),
            z1 = std::lround(z*1e5),
            x2 = std::lround(other.x*1e5),
            y2 = std::lround(other.y*1e5),
            z2 = std::lround(other.z*1e5);

        return std::tie(x1, y1, z1) < std::tie(x2, y2, z2);
    }
    bool operator== (const Point3d &other) const {
        return !(*this < other) && !(other < *this);
    }

    friend std::ostream& operator<< (std::ostream &out, const Point3d &p) {
        out << "Point3d(x=" << p.x
            << ", y=" << p.y
            << ", z=" << p.z
            << ", id=" << p.id
            << ", otherId=" << p.otherId << ")";
        return out;
    }

    static double GetVec (const Point3d &a, const Point3d &b, double *v) {
        v[0] = b.x-a.x;
        v[1] = b.y-a.y;
        v[2] = b.z-a.z;

        return vtkMath::Normalize(v);
    }

    static double GetDist (const Point3d &a, const Point3d &b) {
        double dx = b.x-a.x;
        double dy = b.y-a.y;
        double dz = b.z-a.z;

        return dx*dx+dy*dy+dz*dz;
    }

};

typedef std::vector<vtkIdType> IdsType;
typedef std::set<vtkIdType> _IdsType;

class Pair {
public:
    vtkIdType f, g;
    Pair () = delete;
    Pair (vtkIdType f, vtkIdType g) : f(f), g(g) {}
    bool operator< (const Pair &other) const {
        return std::tie(f, g) < std::tie(other.f, other.g);
    }
    bool operator== (const Pair &other) const {
        return f == other.f && g == other.g;
    }
    vtkIdType operator& (const Pair &other) const {
        if (f == other.f || f == other.g) {
            return f;
        }
        return g;
    }
    friend std::ostream& operator<< (std::ostream &out, const Pair &p) {
        out << "(" << p.f << ", " << p.g << ")";
        return out;
    }
};

class Base {
public:
    Base () {}
    Base (vtkPoints *pts, vtkIdType num, const vtkIdType *poly);
    double n[3], ei[3], ej[3], d;
};

void Transform (const double *in, double *out, const Base &base);
// void BackTransform (const double *in, double *out, const Base &base);

class Base2 {
public:
    Base2 () {}
    Base2 (double *_t, double *_ei, double *_ek) {
        std::copy_n(_t, 3, t);
        std::copy_n(_ei, 3, ei);
        std::copy_n(_ek, 3, ek);

        ej[0] = ek[1]*ei[2]-ek[2]*ei[1];
        ej[1] = -ek[0]*ei[2]+ek[2]*ei[0];
        ej[2] = ek[0]*ei[1]-ek[1]*ei[0];

        vtkMath::Normalize(ej);
    }
    void Transform (const double *in, double *out) const {
        double R[][3] = {
            { ei[0], ei[1], ei[2] },
            { ej[0], ej[1], ej[2] },
            { ek[0], ek[1], ek[2] }
        };

        double _t[] = { in[0]-t[0], in[1]-t[1], in[2]-t[2] };

        out[0] = R[0][0]*_t[0]+R[0][1]*_t[1]+R[0][2]*_t[2];
        out[1] = R[1][0]*_t[0]+R[1][1]*_t[1]+R[1][2]*_t[2];
        out[2] = R[2][0]*_t[0]+R[2][1]*_t[1]+R[2][2]*_t[2];
    }
    void BackTransform (const double *in, double *out) const {
        double R[][3] = {
            { ei[0], ej[0], ek[0] },
            { ei[1], ej[1], ek[1] },
            { ei[2], ej[2], ek[2] }
        };

        double _out[] = {
            R[0][0]*in[0]+R[0][1]*in[1]+R[0][2]*in[2],
            R[1][0]*in[0]+R[1][1]*in[1]+R[1][2]*in[2],
            R[2][0]*in[0]+R[2][1]*in[1]+R[2][2]*in[2]
        };

        out[0] = _out[0]+t[0];
        out[1] = _out[1]+t[1];
        out[2] = _out[2]+t[2];
    }
    double t[3], ei[3], ej[3], ek[3];
};

template<typename T>
std::ostream& operator<< (typename std::enable_if<std::is_enum<T>::value, std::ostream>::type& stream, const T& e) {
    return stream << static_cast<typename std::underlying_type<T>::type>(e);
}

typedef std::vector<Point3d> Poly, Points;
typedef std::vector<Poly> PolysType;

double ComputeNormal (const Poly &poly, double *n);
bool PointInPoly (const Poly &poly, const Point3d &p);

#ifdef DEBUG
void WritePolys (const char *name, const PolysType &polys);
#endif

typedef std::deque<vtkIdType> IndexedPoly;
typedef std::vector<IndexedPoly> IndexedPolysType;

typedef std::map<vtkIdType, std::reference_wrapper<const Point3d>> ReferencedPointsType;

void GetPolys (const ReferencedPointsType &pts, const IndexedPolysType &indexedPolys, PolysType &polys);

void GetPoly (vtkPoints *pts, vtkIdType num, const vtkIdType *poly, Poly &out);
void FlattenPoly (const Poly &poly, Poly &out, const Base &base);
void FlattenPoly2 (const Poly &poly, Poly &out, const Base2 &base);

class Proj {
public:
    Proj (vtkIdType a, vtkIdType b, const Point3d &proj, double d) : edge(a, b), proj(proj), d(d) {}

    Pair edge;
    Point3d proj;
    double d;
};

std::shared_ptr<Proj> GetEdgeProj (const Poly &poly, const Point3d &p);

void ProjOnLine (const Point3d &a, const Point3d &b, const Point3d &p, double *d, double *t, std::shared_ptr<Point3d> &proj);
void ProjOnLine (vtkPolyData *pd, const Pair &line, const Point3d &p, std::shared_ptr<Point3d> &proj);

vtkSmartPointer<vtkPolyData> CreatePolyData (const PolysType &polys);

double GetTriangleQuality (const Poly &poly);

#endif


================================================
FILE: module/CMakeLists.txt
================================================
set(srcs
    ../Utilities.cxx
    ../Optimize.cxx
    ../Contact.cxx
    ../Merger.cxx
    ../vtkPolyDataBooleanFilter.cxx
)

set(headers
    ../vtkPolyDataBooleanFilter.h
)

vtk_module_add_module(vtkbool
    SOURCES ${srcs}
    HEADERS ${headers}
)


================================================
FILE: module/vtk.module
================================================
NAME
  vtkbool
DEPENDS
  VTK::CommonCore
  VTK::CommonExecutionModel
  VTK::FiltersPoints
  VTK::IOLegacy
  VTK::FiltersFlowPaths
TEST_DEPENDS
  VTK::FiltersSources
DESCRIPTION
  This module contains a class to do polydata boolean operations.


================================================
FILE: paraview/CMakeLists.txt
================================================
paraview_add_plugin(PolyDataBooleanFilter
  VERSION "${vtkbool_VERSION}"
  MODULES vtkbool
  MODULE_FILES "${CMAKE_CURRENT_SOURCE_DIR}/module/vtk.module")


================================================
FILE: paraview/module/CMakeLists.txt
================================================
set(srcs
    ../../Utilities.cxx
    ../../Optimize.cxx
    ../../Contact.cxx
    ../../Merger.cxx
    ../../vtkPolyDataBooleanFilter.cxx
)

set(headers
    ../../vtkPolyDataBooleanFilter.h
)

vtk_module_add_module(vtkbool
    FORCE_STATIC
    SOURCES ${srcs}
    HEADERS ${headers}
)

paraview_add_server_manager_xmls(
    XMLS vtkPolyDataBooleanFilter.xml
)


================================================
FILE: paraview/module/vtk.module
================================================
NAME
  vtkbool
DEPENDS
  VTK::CommonCore
  VTK::CommonExecutionModel
  VTK::FiltersPoints
  VTK::IOLegacy
  VTK::FiltersFlowPaths
TEST_DEPENDS
  VTK::FiltersSources


================================================
FILE: paraview/module/vtkPolyDataBooleanFilter.xml
================================================
<ServerManagerConfiguration>
    <ProxyGroup name="filters">
        <SourceProxy name="PolyDataBooleanFilter" class="vtkPolyDataBooleanFilter" label="PolyDataBooleanFilter">
            <Documentation short_help="A boolean filter for polydata inputs." long_help=""></Documentation>

            <InputProperty name="InputA" command="SetInputConnection" port_index="0">
                <ProxyGroupDomain name="groups">
                    <Group name="sources"/>
                    <Group name="filters"/>
                </ProxyGroupDomain>
                <DataTypeDomain name="input_type">
                    <DataType value="vtkPolyData"/>
                </DataTypeDomain>
                <Documentation>
                    First polydata.
                </Documentation>
            </InputProperty>

            <InputProperty name="InputB" command="SetInputConnection" port_index="1">
                <ProxyGroupDomain name="groups">
                    <Group name="sources"/>
                    <Group name="filters"/>
                </ProxyGroupDomain>
                <DataTypeDomain name="input_type">
                    <DataType value="vtkPolyData"/>
                </DataTypeDomain>
                <Documentation>
                    Second polydata.
                </Documentation>
            </InputProperty>

            <IntVectorProperty name="OperMode" command="SetOperMode" number_of_elements="1" default_values="1">
                <EnumerationDomain name="enum">
                    <Entry value="1" text="Union"/>
                    <Entry value="2" text="Intersection"/>
                    <Entry value="3" text="Difference"/>
                    <Entry value="4" text="Difference2"/>
                </EnumerationDomain>
                <Documentation>
                    Sets the operation mode.
                </Documentation>
            </IntVectorProperty>

            <Hints>
                <ShowInMenu category="Extensions"/>
            </Hints>

        </SourceProxy>
    </ProxyGroup>
</ServerManagerConfiguration>


================================================
FILE: paraview/paraview.plugin
================================================
NAME
  PolyDataBooleanFilter
REQUIRES_MODULES
  VTK::CommonCore
  VTK::FiltersCore
DESCRIPTION
  This module contains a class to do polydata boolean operations.


================================================
FILE: run_some_tests.sh
================================================
#!/bin/bash
export PYTHONPATH=/home/zippy/vtkbool/build/lib/python3.13/site-packages/vtkbool:$PYTHONPATH
pushd testing

# pytest test_filter.py::test_triangle_strips -s

# pytest test_filter.py::test_equal_capt_pts -s
# pytest test_filter.py::test_equal_capt_pts_2 -s
# pytest test_filter.py::test_equal_capt_pts_3 -s

# gdb --args python -m pytest test_filter.py::test_simple -s

pytest test_filter.py

# pytest test_filter.py::test_simple -s

popd


================================================
FILE: testing/data/branched.vtk
================================================
# vtk DataFile Version 5.1
vtk output
ASCII
DATASET POLYDATA
POINTS 622 double
0.082297295332 0 0.86818063259 0.078269377351 0.025431262329 0.86818063259 0 0 0.875 
0.066579908133 0.048373136669 0.86818063259 0.048373136669 0.066579908133 0.86818063259 0.025431262329 0.078269377351 0.86818063259 
5.0392558428e-18 0.082297295332 0.86818063259 -0.025431262329 0.078269377351 0.86818063259 -0.048373136669 0.066579908133 0.86818063259 
-0.066579908133 0.048373136669 0.86818063259 -0.078269377351 0.025431262329 0.86818063259 -0.082297295332 1.0078511686e-17 0.86818063259 
-0.078269377351 -0.025431262329 0.86818063259 -0.066579908133 -0.048373136669 0.86818063259 -0.048373136669 -0.066579908133 0.86818063259 
-0.025431262329 -0.078269377351 0.86818063259 -1.5117767942e-17 -0.082297295332 0.86818063259 0.025431262329 -0.078269377351 0.86818063259 
0.048373136669 -0.066579908133 0.86818063259 0.066579908133 -0.048373136669 0.86818063259 0.078269377351 -0.025431262329 0.86818063259 
0.16234973073 0 0.84790861607 0.15440377593 0.050168827176 0.84790861607 0.23797369003 0 0.81473690271 
0.2263264358 0.073537915945 0.81473690271 0.30710634589 0 0.76957023144 0.29207551479 0.0949010849 0.76957023144 
0.36786195636 0 0.71364080906 0.34985750914 0.11367559433 0.71364080906 0.41858324409 0 0.64847409725 
0.39809632301 0.12934933603 0.64847409725 0.45788666606 0 0.57584768534 0.43547609448 0.14149476588 0.57584768534 
0.48470014334 0 0.4977427423 0.4609772265 0.14978057146 0.4977427423 0.49829223752 0 0.41628968716 
0.47390407324 0.15398077667 0.41628968716 0.49829223752 0 0.33371031284 0.47390407324 0.15398077667 0.33371031284 
0.48470014334 0 0.2522572577 0.4609772265 0.14978057146 0.2522572577 0.45788666606 0 0.17415228486 
0.43547609448 0.14149476588 0.17415228486 0.41858324409 0 0.10152591765 0.39809632301 0.12934933603 0.10152591765 
0.36786195636 0 0.036359213293 0.34985750914 0.11367559433 0.036359213293 0.13134369254 0.095426782966 0.84790861607 
0.19252476096 0.13987742364 0.81473690271 0.24845425785 0.1805125922 0.76957023144 0.29760658741 0.21622383595 0.71364080906 
0.33864095807 0.24603705108 0.64847409725 0.37043809891 0.26913902164 0.57584768534 0.39213064313 0.2848995924 0.4977427423 
0.40312689543 0.29288882017 0.41628968716 0.40312689543 0.29288882017 0.33371031284 0.39213064313 0.2848995924 0.2522572577 
0.37043809891 0.26913902164 0.17415228486 0.33864095807 0.24603705108 0.10152591765 0.29760658741 0.21622383595 0.036359213293 
0.095426782966 0.13134369254 0.84790861607 0.13987742364 0.19252476096 0.81473690271 0.1805125922 0.24845425785 0.76957023144 
0.21622383595 0.29760658741 0.71364080906 0.24603705108 0.33864095807 0.64847409725 0.26913902164 0.37043809891 0.57584768534 
0.2848995924 0.39213064313 0.4977427423 0.29288882017 0.40312689543 0.41628968716 0.29288882017 0.40312689543 0.33371031284 
0.2848995924 0.39213064313 0.2522572577 0.26913902164 0.37043809891 0.17415228486 0.24603705108 0.33864095807 0.10152591765 
0.21622383595 0.29760658741 0.036359213293 0.050168827176 0.15440377593 0.84790861607 0.073537915945 0.2263264358 0.81473690271 
0.0949010849 0.29207551479 0.76957023144 0.11367559433 0.34985750914 0.71364080906 0.12934933603 0.39809632301 0.64847409725 
0.14149476588 0.43547609448 0.57584768534 0.14978057146 0.4609772265 0.4977427423 0.15398077667 0.47390407324 0.41628968716 
0.15398077667 0.47390407324 0.33371031284 0.14978057146 0.4609772265 0.2522572577 0.14149476588 0.43547609448 0.17415228486 
0.12934933603 0.39809632301 0.10152591765 0.11367559433 0.34985750914 0.036359213293 9.9410541201e-18 0.16234973073 0.84790861607 
1.4571686463e-17 0.23797369003 0.81473690271 1.880484115e-17 0.30710634589 0.76957023144 2.2525047905e-17 0.36786195636 0.71364080906 
2.5630831518e-17 0.41858324409 0.64847409725 2.8037472151e-17 0.45788666606 0.57584768534 2.9679323096e-17 0.48470014334 0.4977427423 
3.0511599142e-17 0.49829223752 0.41628968716 3.0511599142e-17 0.49829223752 0.33371031284 2.9679323096e-17 0.48470014334 0.2522572577 
2.8037472151e-17 0.45788666606 0.17415228486 2.5630831518e-17 0.41858324409 0.10152591765 2.2525047905e-17 0.36786195636 0.036359213293 
-0.050168827176 0.15440377593 0.84790861607 -0.073537915945 0.2263264358 0.81473690271 -0.0949010849 0.29207551479 0.76957023144 
-0.11367559433 0.34985750914 0.71364080906 -0.12934933603 0.39809632301 0.64847409725 -0.14149476588 0.43547609448 0.57584768534 
-0.14978057146 0.4609772265 0.4977427423 -0.15398077667 0.47390407324 0.41628968716 -0.15398077667 0.47390407324 0.33371031284 
-0.14978057146 0.4609772265 0.2522572577 -0.14149476588 0.43547609448 0.17415228486 -0.12934933603 0.39809632301 0.10152591765 
-0.11367559433 0.34985750914 0.036359213293 -0.095426782966 0.13134369254 0.84790861607 -0.13987742364 0.19252476096 0.81473690271 
-0.1805125922 0.24845425785 0.76957023144 -0.21622383595 0.29760658741 0.71364080906 -0.24603705108 0.33864095807 0.64847409725 
-0.26913902164 0.37043809891 0.57584768534 -0.2848995924 0.39213064313 0.4977427423 -0.29288882017 0.40312689543 0.41628968716 
-0.29288882017 0.40312689543 0.33371031284 -0.2848995924 0.39213064313 0.2522572577 -0.26913902164 0.37043809891 0.17415228486 
-0.24603705108 0.33864095807 0.10152591765 -0.21622383595 0.29760658741 0.036359213293 -0.13134369254 0.095426782966 0.84790861607 
-0.19252476096 0.13987742364 0.81473690271 -0.24845425785 0.1805125922 0.76957023144 -0.29760658741 0.21622383595 0.71364080906 
-0.33864095807 0.24603705108 0.64847409725 -0.37043809891 0.26913902164 0.57584768534 -0.39213064313 0.2848995924 0.4977427423 
-0.40312689543 0.29288882017 0.41628968716 -0.40312689543 0.29288882017 0.33371031284 -0.39213064313 0.2848995924 0.2522572577 
-0.37043809891 0.26913902164 0.17415228486 -0.33864095807 0.24603705108 0.10152591765 -0.29760658741 0.21622383595 0.036359213293 
-0.15440377593 0.050168827176 0.84790861607 -0.2263264358 0.073537915945 0.81473690271 -0.29207551479 0.0949010849 0.76957023144 
-0.34985750914 0.11367559433 0.71364080906 -0.39809632301 0.12934933603 0.64847409725 -0.43547609448 0.14149476588 0.57584768534 
-0.4609772265 0.14978057146 0.4977427423 -0.47390407324 0.15398077667 0.41628968716 -0.47390407324 0.15398077667 0.33371031284 
-0.4609772265 0.14978057146 0.2522572577 -0.43547609448 0.14149476588 0.17415228486 -0.39809632301 0.12934933603 0.10152591765 
-0.34985750914 0.11367559433 0.036359213293 -0.16234973073 1.988210824e-17 0.84790861607 -0.23797369003 2.9143372925e-17 0.81473690271 
-0.30710634589 3.76096823e-17 0.76957023144 -0.36786195636 4.5050095809e-17 0.71364080906 -0.41858324409 5.1261663035e-17 0.64847409725 
-0.45788666606 5.6074944302e-17 0.57584768534 -0.48470014334 5.9358646193e-17 0.4977427423 -0.49829223752 6.1023198283e-17 0.41628968716 
-0.49829223752 6.1023198283e-17 0.33371031284 -0.48470014334 5.9358646193e-17 0.2522572577 -0.45788666606 5.6074944302e-17 0.17415228486 
-0.41858324409 5.1261663035e-17 0.10152591765 -0.36786195636 4.5050095809e-17 0.036359213293 -0.15440377593 -0.050168827176 0.84790861607 
-0.2263264358 -0.073537915945 0.81473690271 -0.29207551479 -0.0949010849 0.76957023144 -0.34985750914 -0.11367559433 0.71364080906 
-0.39809632301 -0.12934933603 0.64847409725 -0.43547609448 -0.14149476588 0.57584768534 -0.4609772265 -0.14978057146 0.4977427423 
-0.47390407324 -0.15398077667 0.41628968716 -0.47390407324 -0.15398077667 0.33371031284 -0.4609772265 -0.14978057146 0.2522572577 
-0.43547609448 -0.14149476588 0.17415228486 -0.39809632301 -0.12934933603 0.10152591765 -0.34985750914 -0.11367559433 0.036359213293 
-0.13134369254 -0.095426782966 0.84790861607 -0.19252476096 -0.13987742364 0.81473690271 -0.24845425785 -0.1805125922 0.76957023144 
-0.29760658741 -0.21622383595 0.71364080906 -0.33864095807 -0.24603705108 0.64847409725 -0.37043809891 -0.26913902164 0.57584768534 
-0.39213064313 -0.2848995924 0.4977427423 -0.40312689543 -0.29288882017 0.41628968716 -0.40312689543 -0.29288882017 0.33371031284 
-0.39213064313 -0.2848995924 0.2522572577 -0.37043809891 -0.26913902164 0.17415228486 -0.33864095807 -0.24603705108 0.10152591765 
-0.29760658741 -0.21622383595 0.036359213293 -0.095426782966 -0.13134369254 0.84790861607 -0.13987742364 -0.19252476096 0.81473690271 
-0.1805125922 -0.24845425785 0.76957023144 -0.21622383595 -0.29760658741 0.71364080906 -0.24603705108 -0.33864095807 0.64847409725 
-0.26913902164 -0.37043809891 0.57584768534 -0.2848995924 -0.39213064313 0.4977427423 -0.29288882017 -0.40312689543 0.41628968716 
-0.29288882017 -0.40312689543 0.33371031284 -0.2848995924 -0.39213064313 0.2522572577 -0.26913902164 -0.37043809891 0.17415228486 
-0.24603705108 -0.33864095807 0.10152591765 -0.21622383595 -0.29760658741 0.036359213293 -0.050168827176 -0.15440377593 0.84790861607 
-0.073537915945 -0.2263264358 0.81473690271 -0.0949010849 -0.29207551479 0.76957023144 -0.11367559433 -0.34985750914 0.71364080906 
-0.12934933603 -0.39809632301 0.64847409725 -0.14149476588 -0.43547609448 0.57584768534 -0.14978057146 -0.4609772265 0.4977427423 
-0.15398077667 -0.47390407324 0.41628968716 -0.15398077667 -0.47390407324 0.33371031284 -0.14978057146 -0.4609772265 0.2522572577 
-0.14149476588 -0.43547609448 0.17415228486 -0.12934933603 -0.39809632301 0.10152591765 -0.11367559433 -0.34985750914 0.036359213293 
-2.9823163188e-17 -0.16234973073 0.84790861607 -4.3715059388e-17 -0.23797369003 0.81473690271 -5.6414525104e-17 -0.30710634589 0.76957023144 
-6.7575143714e-17 -0.36786195636 0.71364080906 -7.6892492899e-17 -0.41858324409 0.64847409725 -8.4112416453e-17 -0.45788666606 0.57584768534 
-8.9037972598e-17 -0.48470014334 0.4977427423 -9.1534800733e-17 -0.49829223752 0.41628968716 -9.1534800733e-17 -0.49829223752 0.33371031284 
-8.9037972598e-17 -0.48470014334 0.2522572577 -8.4112416453e-17 -0.45788666606 0.17415228486 -7.6892492899e-17 -0.41858324409 0.10152591765 
-6.7575143714e-17 -0.36786195636 0.036359213293 0.050168827176 -0.15440377593 0.84790861607 0.073537915945 -0.2263264358 0.81473690271 
0.0949010849 -0.29207551479 0.76957023144 0.11367559433 -0.34985750914 0.71364080906 0.12934933603 -0.39809632301 0.64847409725 
0.14149476588 -0.43547609448 0.57584768534 0.14978057146 -0.4609772265 0.4977427423 0.15398077667 -0.47390407324 0.41628968716 
0.15398077667 -0.47390407324 0.33371031284 0.14978057146 -0.4609772265 0.2522572577 0.14149476588 -0.43547609448 0.17415228486 
0.12934933603 -0.39809632301 0.10152591765 0.11367559433 -0.34985750914 0.036359213293 0.095426782966 -0.13134369254 0.84790861607 
0.13987742364 -0.19252476096 0.81473690271 0.1805125922 -0.24845425785 0.76957023144 0.21622383595 -0.29760658741 0.71364080906 
0.24603705108 -0.33864095807 0.64847409725 0.26913902164 -0.37043809891 0.57584768534 0.2848995924 -0.39213064313 0.4977427423 
0.29288882017 -0.40312689543 0.41628968716 0.29288882017 -0.40312689543 0.33371031284 0.2848995924 -0.39213064313 0.2522572577 
0.26913902164 -0.37043809891 0.17415228486 0.24603705108 -0.33864095807 0.10152591765 0.21622383595 -0.29760658741 0.036359213293 
0.13134369254 -0.095426782966 0.84790861607 0.19252476096 -0.13987742364 0.81473690271 0.24845425785 -0.1805125922 0.76957023144 
0.29760658741 -0.21622383595 0.71364080906 0.33864095807 -0.24603705108 0.64847409725 0.37043809891 -0.26913902164 0.57584768534 
0.39213064313 -0.2848995924 0.4977427423 0.40312689543 -0.29288882017 0.41628968716 0.40312689543 -0.29288882017 0.33371031284 
0.39213064313 -0.2848995924 0.2522572577 0.37043809891 -0.26913902164 0.17415228486 0.33864095807 -0.24603705108 0.10152591765 
0.29760658741 -0.21622383595 0.036359213293 0.15440377593 -0.050168827176 0.84790861607 0.2263264358 -0.073537915945 0.81473690271 
0.29207551479 -0.0949010849 0.76957023144 0.34985750914 -0.11367559433 0.71364080906 0.39809632301 -0.12934933603 0.64847409725 
0.43547609448 -0.14149476588 0.57584768534 0.4609772265 -0.14978057146 0.4977427423 0.47390407324 -0.15398077667 0.41628968716 
0.47390407324 -0.15398077667 0.33371031284 0.4609772265 -0.14978057146 0.2522572577 0.43547609448 -0.14149476588 0.17415228486 
0.39809632301 -0.12934933603 0.10152591765 0.34985750914 -0.11367559433 0.036359213293 0.32836531327 8.6818331113e-19 -2.8171179001e-17 
0.3220653893 0.03977618435 -4.9487070846e-09 0.31859390657 0.06169429886 -4.9487070599e-09 0.31229398553 0.10147046475 2.5407916269e-17 
0.29401088933 0.13735306108 5.1429095066e-09 0.28393623162 0.15712568324 5.1429095066e-09 0.2656531266 0.19300829687 6.9388939039e-18 
0.23717652592 0.22148489755 0 0.22148489755 0.23717652592 0 0.19300829687 0.2656531266 0 
0.15712568324 0.28393623162 -5.1429094891e-09 0.13735306108 0.29401088933 -5.1429095118e-09 0.10147046475 0.31229398553 -2.5197041461e-17 
0.06169429886 0.31859390657 4.9487070318e-09 0.03977618435 0.3220653893 4.948707057e-09 1.8882986284e-17 0.32836531327 2.3493044647e-24 
-0.03977618435 0.3220653893 -4.9487070858e-09 -0.06169429886 0.31859390657 -4.948707057e-09 -0.10147046475 0.31229398553 -4.4034066554e-18 
-0.13735306108 0.29401088933 5.1429095118e-09 -0.15712568324 0.28393623162 5.1429095066e-09 -0.19300829687 0.2656531266 2.0768586122e-18 
-0.22148489755 0.23717652592 0 -0.23717652592 0.22148489755 0 -0.2656531266 0.19300829687 0 
-0.28393623162 0.15712568324 -5.1429094753e-09 -0.29401088933 0.13735306108 -5.1429095118e-09 -0.31229398553 0.10147046475 -2.5407916269e-17 
-0.31859390657 0.06169429886 4.9487070561e-09 -0.3220653893 0.03977618435 4.9487070561e-09 -0.32836531327 3.8887509036e-17 4.8381435661e-24 
-0.3220653893 -0.03977618435 -4.9487070852e-09 -0.31859390657 -0.06169429886 -4.9487070599e-09 -0.31229398553 -0.10147046475 -2.9741361642e-18 
-0.29401088933 -0.13735306108 5.1429095066e-09 -0.28393623162 -0.15712568324 5.1429095066e-09 -0.2656531266 -0.19300829687 6.9388939039e-18 
-0.23717652592 -0.22148489755 0 -0.22148489755 -0.23717652592 0 -0.19300829687 -0.2656531266 0 
-0.15712568324 -0.28393623162 -5.1429094753e-09 -0.13735306108 -0.29401088933 -5.1429094753e-09 -0.10147046475 -0.31229398553 -2.540791296e-17 
-0.06169429886 -0.31859390657 4.9487070561e-09 -0.03977618435 -0.3220653893 4.9487070565e-09 -5.8492876492e-17 -0.32836531327 -8.5284299912e-18 
0.03977618435 -0.3220653893 -4.9487070556e-09 0.06169429886 -0.31859390657 -4.9487070591e-09 0.10147046475 -0.31229398553 -1.5448640187e-18 
0.13735306108 -0.29401088933 5.1429095066e-09 0.15712568324 -0.28393623162 5.1429094891e-09 0.19300829687 -0.2656531266 -0 
0.22148489755 -0.23717652592 -0 0.23717652592 -0.22148489755 -0 0.2656531266 -0.19300829687 -2.0768619209e-18 
0.28393623162 -0.15712568324 -5.1429094753e-09 0.29401088933 -0.13735306108 -5.1429095118e-09 0.31229398553 -0.10147046475 2.9741345099e-18 
0.31859390657 -0.06169429886 4.9487070561e-09 0.3220653893 -0.03977618435 4.9487070565e-09 0.082297295332 0 -0.86818063259 
0 0 -0.875 0.078269377351 0.025431262329 -0.86818063259 0.066579908133 0.048373136669 -0.86818063259 
0.048373136669 0.066579908133 -0.86818063259 0.025431262329 0.078269377351 -0.86818063259 5.0392558428e-18 0.082297295332 -0.86818063259 
-0.025431262329 0.078269377351 -0.86818063259 -0.048373136669 0.066579908133 -0.86818063259 -0.066579908133 0.048373136669 -0.86818063259 
-0.078269377351 0.025431262329 -0.86818063259 -0.082297295332 1.0078511686e-17 -0.86818063259 -0.078269377351 -0.025431262329 -0.86818063259 
-0.066579908133 -0.048373136669 -0.86818063259 -0.048373136669 -0.066579908133 -0.86818063259 -0.025431262329 -0.078269377351 -0.86818063259 
-1.5117767942e-17 -0.082297295332 -0.86818063259 0.025431262329 -0.078269377351 -0.86818063259 0.048373136669 -0.066579908133 -0.86818063259 
0.066579908133 -0.048373136669 -0.86818063259 0.078269377351 -0.025431262329 -0.86818063259 0.36786195636 0 -0.036359213293 
0.41858324409 0 -0.10152591765 0.39809632301 0.12934933603 -0.10152591765 0.34985750914 0.11367559433 -0.036359213293 
0.45788666606 0 -0.17415228486 0.43547609448 0.14149476588 -0.17415228486 0.48470014334 0 -0.2522572577 
0.4609772265 0.14978057146 -0.2522572577 0.49829223752 0 -0.33371031284 0.47390407324 0.15398077667 -0.33371031284 
0.49829223752 0 -0.41628968716 0.47390407324 0.15398077667 -0.41628968716 0.48470014334 0 -0.4977427423 
0.4609772265 0.14978057146 -0.4977427423 0.45788666606 0 -0.57584768534 0.43547609448 0.14149476588 -0.57584768534 
0.41858324409 0 -0.64847409725 0.39809632301 0.12934933603 -0.64847409725 0.36786195636 0 -0.71364080906 
0.34985750914 0.11367559433 -0.71364080906 0.30710634589 0 -0.76957023144 0.29207551479 0.0949010849 -0.76957023144 
0.23797369003 0 -0.81473690271 0.2263264358 0.073537915945 -0.81473690271 0.16234973073 0 -0.84790861607 
0.15440377593 0.050168827176 -0.84790861607 0.33864095807 0.24603705108 -0.10152591765 0.29760658741 0.21622383595 -0.036359213293 
0.37043809891 0.26913902164 -0.17415228486 0.39213064313 0.2848995924 -0.2522572577 0.40312689543 0.29288882017 -0.33371031284 
0.40312689543 0.29288882017 -0.41628968716 0.39213064313 0.2848995924 -0.4977427423 0.37043809891 0.26913902164 -0.57584768534 
0.33864095807 0.24603705108 -0.64847409725 0.29760658741 0.21622383595 -0.71364080906 0.24845425785 0.1805125922 -0.76957023144 
0.19252476096 0.13987742364 -0.81473690271 0.13134369254 0.095426782966 -0.84790861607 0.24603705108 0.33864095807 -0.10152591765 
0.21622383595 0.29760658741 -0.036359213293 0.26913902164 0.37043809891 -0.17415228486 0.2848995924 0.39213064313 -0.2522572577 
0.29288882017 0.40312689543 -0.33371031284 0.29288882017 0.40312689543 -0.41628968716 0.2848995924 0.39213064313 -0.4977427423 
0.26913902164 0.37043809891 -0.57584768534 0.24603705108 0.33864095807 -0.64847409725 0.21622383595 0.29760658741 -0.71364080906 
0.1805125922 0.24845425785 -0.76957023144 0.13987742364 0.19252476096 -0.81473690271 0.095426782966 0.13134369254 -0.84790861607 
0.12934933603 0.39809632301 -0.10152591765 0.11367559433 0.34985750914 -0.036359213293 0.14149476588 0.43547609448 -0.17415228486 
0.14978057146 0.4609772265 -0.2522572577 0.15398077667 0.47390407324 -0.33371031284 0.15398077667 0.47390407324 -0.41628968716 
0.14978057146 0.4609772265 -0.4977427423 0.14149476588 0.43547609448 -0.57584768534 0.12934933603 0.39809632301 -0.64847409725 
0.11367559433 0.34985750914 -0.71364080906 0.0949010849 0.29207551479 -0.76957023144 0.073537915945 0.2263264358 -0.81473690271 
0.050168827176 0.15440377593 -0.84790861607 2.5630831518e-17 0.41858324409 -0.10152591765 2.2525047905e-17 0.36786195636 -0.036359213293 
2.8037472151e-17 0.45788666606 -0.17415228486 2.9679323096e-17 0.48470014334 -0.2522572577 3.0511599142e-17 0.49829223752 -0.33371031284 
3.0511599142e-17 0.49829223752 -0.41628968716 2.9679323096e-17 0.48470014334 -0.4977427423 2.8037472151e-17 0.45788666606 -0.57584768534 
2.5630831518e-17 0.41858324409 -0.64847409725 2.2525047905e-17 0.36786195636 -0.71364080906 1.880484115e-17 0.30710634589 -0.76957023144 
1.4571686463e-17 0.23797369003 -0.81473690271 9.9410541201e-18 0.16234973073 -0.84790861607 -0.12934933603 0.39809632301 -0.10152591765 
-0.11367559433 0.34985750914 -0.036359213293 -0.14149476588 0.43547609448 -0.17415228486 -0.14978057146 0.4609772265 -0.2522572577 
-0.15398077667 0.47390407324 -0.33371031284 -0.15398077667 0.47390407324 -0.41628968716 -0.14978057146 0.4609772265 -0.4977427423 
-0.14149476588 0.43547609448 -0.57584768534 -0.12934933603 0.39809632301 -0.64847409725 -0.11367559433 0.34985750914 -0.71364080906 
-0.0949010849 0.29207551479 -0.76957023144 -0.073537915945 0.2263264358 -0.81473690271 -0.050168827176 0.15440377593 -0.84790861607 
-0.24603705108 0.33864095807 -0.10152591765 -0.21622383595 0.29760658741 -0.036359213293 -0.26913902164 0.37043809891 -0.17415228486 
-0.2848995924 0.39213064313 -0.2522572577 -0.29288882017 0.40312689543 -0.33371031284 -0.29288882017 0.40312689543 -0.41628968716 
-0.2848995924 0.39213064313 -0.4977427423 -0.26913902164 0.37043809891 -0.57584768534 -0.24603705108 0.33864095807 -0.64847409725 
-0.21622383595 0.29760658741 -0.71364080906 -0.1805125922 0.24845425785 -0.76957023144 -0.13987742364 0.19252476096 -0.81473690271 
-0.095426782966 0.13134369254 -0.84790861607 -0.33864095807 0.24603705108 -0.10152591765 -0.29760658741 0.21622383595 -0.036359213293 
-0.37043809891 0.26913902164 -0.17415228486 -0.39213064313 0.2848995924 -0.2522572577 -0.40312689543 0.29288882017 -0.33371031284 
-0.40312689543 0.29288882017 -0.41628968716 -0.39213064313 0.2848995924 -0.4977427423 -0.37043809891 0.26913902164 -0.57584768534 
-0.33864095807 0.24603705108 -0.64847409725 -0.29760658741 0.21622383595 -0.71364080906 -0.24845425785 0.1805125922 -0.76957023144 
-0.19252476096 0.13987742364 -0.81473690271 -0.13134369254 0.095426782966 -0.84790861607 -0.39809632301 0.12934933603 -0.10152591765 
-0.34985750914 0.11367559433 -0.036359213293 -0.43547609448 0.14149476588 -0.17415228486 -0.4609772265 0.14978057146 -0.2522572577 
-0.47390407324 0.15398077667 -0.33371031284 -0.47390407324 0.15398077667 -0.41628968716 -0.4609772265 0.14978057146 -0.4977427423 
-0.43547609448 0.14149476588 -0.57584768534 -0.39809632301 0.12934933603 -0.64847409725 -0.34985750914 0.11367559433 -0.71364080906 
-0.29207551479 0.0949010849 -0.76957023144 -0.2263264358 0.073537915945 -0.81473690271 -0.15440377593 0.050168827176 -0.84790861607 
-0.41858324409 5.1261663035e-17 -0.10152591765 -0.36786195636 4.5050095809e-17 -0.036359213293 -0.45788666606 5.6074944302e-17 -0.17415228486 
-0.48470014334 5.9358646193e-17 -0.2522572577 -0.49829223752 6.1023198283e-17 -0.33371031284 -0.49829223752 6.1023198283e-17 -0.41628968716 
-0.48470014334 5.9358646193e-17 -0.4977427423 -0.45788666606 5.6074944302e-17 -0.57584768534 -0.41858324409 5.1261663035e-17 -0.64847409725 
-0.36786195636 4.5050095809e-17 -0.71364080906 -0.30710634589 3.76096823e-17 -0.76957023144 -0.23797369003 2.9143372925e-17 -0.81473690271 
-0.16234973073 1.988210824e-17 -0.84790861607 -0.39809632301 -0.12934933603 -0.10152591765 -0.34985750914 -0.11367559433 -0.036359213293 
-0.43547609448 -0.14149476588 -0.17415228486 -0.4609772265 -0.14978057146 -0.2522572577 -0.47390407324 -0.15398077667 -0.33371031284 
-0.47390407324 -0.15398077667 -0.41628968716 -0.4609772265 -0.14978057146 -0.4977427423 -0.43547609448 -0.14149476588 -0.57584768534 
-0.39809632301 -0.12934933603 -0.64847409725 -0.34985750914 -0.11367559433 -0.71364080906 -0.29207551479 -0.0949010849 -0.76957023144 
-0.2263264358 -0.073537915945 -0.81473690271 -0.15440377593 -0.050168827176 -0.84790861607 -0.33864095807 -0.24603705108 -0.10152591765 
-0.29760658741 -0.21622383595 -0.036359213293 -0.37043809891 -0.26913902164 -0.17415228486 -0.39213064313 -0.2848995924 -0.2522572577 
-0.40312689543 -0.29288882017 -0.33371031284 -0.40312689543 -0.29288882017 -0.41628968716 -0.39213064313 -0.2848995924 -0.4977427423 
-0.37043809891 -0.26913902164 -0.57584768534 -0.33864095807 -0.24603705108 -0.64847409725 -0.29760658741 -0.21622383595 -0.71364080906 
-0.24845425785 -0.1805125922 -0.76957023144 -0.19252476096 -0.13987742364 -0.81473690271 -0.13134369254 -0.095426782966 -0.84790861607 
-0.24603705108 -0.33864095807 -0.10152591765 -0.21622383595 -0.29760658741 -0.036359213293 -0.26913902164 -0.37043809891 -0.17415228486 
-0.2848995924 -0.39213064313 -0.2522572577 -0.29288882017 -0.40312689543 -0.33371031284 -0.29288882017 -0.40312689543 -0.41628968716 
-0.2848995924 -0.39213064313 -0.4977427423 -0.26913902164 -0.37043809891 -0.57584768534 -0.24603705108 -0.33864095807 -0.64847409725 
-0.21622383595 -0.29760658741 -0.71364080906 -0.1805125922 -0.24845425785 -0.76957023144 -0.13987742364 -0.19252476096 -0.81473690271 
-0.095426782966 -0.13134369254 -0.84790861607 -0.12934933603 -0.39809632301 -0.10152591765 -0.11367559433 -0.34985750914 -0.036359213293 
-0.14149476588 -0.43547609448 -0.17415228486 -0.14978057146 -0.4609772265 -0.2522572577 -0.15398077667 -0.47390407324 -0.33371031284 
-0.15398077667 -0.47390407324 -0.41628968716 -0.14978057146 -0.4609772265 -0.4977427423 -0.14149476588 -0.43547609448 -0.57584768534 
-0.12934933603 -0.39809632301 -0.64847409725 -0.11367559433 -0.34985750914 -0.71364080906 -0.0949010849 -0.29207551479 -0.76957023144 
-0.073537915945 -0.2263264358 -0.81473690271 -0.050168827176 -0.15440377593 -0.84790861607 -7.6892492899e-17 -0.41858324409 -0.10152591765 
-6.7575143714e-17 -0.36786195636 -0.036359213293 -8.4112416453e-17 -0.45788666606 -0.17415228486 -8.9037972598e-17 -0.48470014334 -0.2522572577 
-9.1534800733e-17 -0.49829223752 -0.33371031284 -9.1534800733e-17 -0.49829223752 -0.41628968716 -8.9037972598e-17 -0.48470014334 -0.4977427423 
-8.4112416453e-17 -0.45788666606 -0.57584768534 -7.6892492899e-17 -0.41858324409 -0.64847409725 -6.7575143714e-17 -0.36786195636 -0.71364080906 
-5.6414525104e-17 -0.30710634589 -0.76957023144 -4.3715059388e-17 -0.23797369003 -0.81473690271 -2.9823163188e-17 -0.16234973073 -0.84790861607 
0.12934933603 -0.39809632301 -0.10152591765 0.11367559433 -0.34985750914 -0.036359213293 0.14149476588 -0.43547609448 -0.17415228486 
0.14978057146 -0.4609772265 -0.2522572577 0.15398077667 -0.47390407324 -0.33371031284 0.15398077667 -0.47390407324 -0.41628968716 
0.14978057146 -0.4609772265 -0.4977427423 0.14149476588 -0.43547609448 -0.57584768534 0.12934933603 -0.39809632301 -0.64847409725 
0.11367559433 -0.34985750914 -0.71364080906 0.0949010849 -0.29207551479 -0.76957023144 0.073537915945 -0.2263264358 -0.81473690271 
0.050168827176 -0.15440377593 -0.84790861607 0.24603705108 -0.33864095807 -0.10152591765 0.21622383595 -0.29760658741 -0.036359213293 
0.26913902164 -0.37043809891 -0.17415228486 0.2848995924 -0.39213064313 -0.2522572577 0.29288882017 -0.40312689543 -0.33371031284 
0.29288882017 -0.40312689543 -0.41628968716 0.2848995924 -0.39213064313 -0.4977427423 0.26913902164 -0.37043809891 -0.57584768534 
0.24603705108 -0.33864095807 -0.64847409725 0.21622383595 -0.29760658741 -0.71364080906 0.1805125922 -0.24845425785 -0.76957023144 
0.13987742364 -0.19252476096 -0.81473690271 0.095426782966 -0.13134369254 -0.84790861607 0.33864095807 -0.24603705108 -0.10152591765 
0.29760658741 -0.21622383595 -0.036359213293 0.37043809891 -0.26913902164 -0.17415228486 0.39213064313 -0.2848995924 -0.2522572577 
0.40312689543 -0.29288882017 -0.33371031284 0.40312689543 -0.29288882017 -0.41628968716 0.39213064313 -0.2848995924 -0.4977427423 
0.37043809891 -0.26913902164 -0.57584768534 0.33864095807 -0.24603705108 -0.64847409725 0.29760658741 -0.21622383595 -0.71364080906 
0.24845425785 -0.1805125922 -0.76957023144 0.19252476096 -0.13987742364 -0.81473690271 0.13134369254 -0.095426782966 -0.84790861607 
0.39809632301 -0.12934933603 -0.10152591765 0.34985750914 -0.11367559433 -0.036359213293 0.43547609448 -0.14149476588 -0.17415228486 
0.4609772265 -0.14978057146 -0.2522572577 0.47390407324 -0.15398077667 -0.33371031284 0.47390407324 -0.15398077667 -0.41628968716 
0.4609772265 -0.14978057146 -0.4977427423 0.43547609448 -0.14149476588 -0.57584768534 0.39809632301 -0.12934933603 -0.64847409725 
0.34985750914 -0.11367559433 -0.71364080906 0.29207551479 -0.0949010849 -0.76957023144 0.2263264358 -0.073537915945 -0.81473690271 
0.15440377593 -0.050168827176 -0.84790861607 
POLYGONS 1161 3560
OFFSETS vtktypeint64
0 3 6 9 12 15 18 21 24 
27 30 33 36 39 42 45 48 51 
54 57 60 63 66 69 72 75 78 
81 84 87 90 93 96 99 102 105 
108 111 114 117 120 123 126 129 132 
135 138 141 144 147 150 153 156 159 
162 165 168 171 174 177 180 183 186 
189 192 195 198 201 204 207 210 213 
216 219 222 225 228 231 234 237 240 
243 246 249 252 255 258 261 264 267 
270 273 276 279 282 285 288 291 294 
297 300 303 306 309 312 315 318 321 
324 327 330 333 336 339 342 345 348 
351 354 357 360 363 366 369 372 375 
378 381 384 387 390 393 396 399 402 
405 408 411 414 417 420 423 426 429 
432 435 438 441 444 447 450 453 456 
459 462 465 468 471 474 477 480 483 
486 489 492 495 498 501 504 507 510 
513 516 519 522 525 528 531 534 537 
540 543 546 549 552 555 558 561 564 
567 570 573 576 579 582 585 588 591 
594 597 600 603 606 609 612 615 618 
621 624 627 630 633 636 639 642 645 
648 651 654 657 660 663 666 669 672 
675 678 681 684 687 690 693 696 699 
702 705 708 711 714 717 720 723 726 
729 732 735 738 741 744 747 750 753 
756 759 762 765 768 771 774 777 780 
783 786 789 792 795 798 801 804 807 
810 813 816 819 822 825 828 831 834 
837 840 843 846 849 852 855 858 861 
864 867 870 873 876 879 882 885 888 
891 894 897 900 903 906 909 912 915 
918 921 924 927 930 933 936 939 942 
945 948 951 954 957 960 963 966 969 
972 975 978 981 984 987 990 993 996 
999 1002 1005 1008 1011 1014 1017 1020 1023 
1026 1029 1032 1035 1038 1041 1044 1047 1050 
1053 1056 1059 1062 1065 1068 1071 1074 1077 
1080 1083 1086 1089 1092 1095 1098 1101 1104 
1107 1110 1113 1116 1119 1122 1125 1128 1131 
1134 1137 1140 1143 1146 1149 1152 1155 1158 
1161 1164 1167 1170 1173 1176 1179 1182 1185 
1188 1191 1194 1197 1200 1203 1206 1209 1212 
1215 1218 1221 1224 1227 1230 1233 1236 1239 
1242 1245 1248 1251 1254 1257 1260 1263 1266 
1269 1272 1275 1278 1281 1284 1287 1290 1293 
1296 1299 1302 1305 1308 1311 1314 1317 1320 
1323 1326 1329 1332 1335 1338 1341 1344 1347 
1350 1353 1356 1359 1362 1365 1368 1371 1374 
1377 1380 1383 1386 1389 1392 1395 1398 1401 
1404 1407 1410 1413 1416 1419 1422 1425 1428 
1431 1434 1437 1440 1443 1446 1449 1452 1455 
1458 1461 1464 1467 1470 1473 1476 1479 1482 
1485 1488 1491 1494 1497 1500 1503 1506 1509 
1512 1515 1518 1521 1524 1527 1530 1533 1536 
1539 1542 1545 1548 1551 1554 1557 1560 1563 
1566 1569 1572 1575 1578 1581 1584 1587 1590 
1593 1596 1599 1602 1605 1608 1611 1614 1617 
1620 1624 1628 1632 1636 1640 1644 1648 1652 
1656 1660 1664 1668 1672 1676 1680 1684 1688 
1692 1696 1700 1704 1708 1712 1716 1720 1724 
1728 1732 1736 1740 1744 1748 1752 1756 1760 
1764 1768 1772 1776 1780 1783 1786 1789 1792 
1795 1798 1801 1804 1807 1810 1813 1816 1819 
1822 1825 1828 1831 1834 1837 1840 1843 1846 
1849 1852 1855 1858 1861 1864 1867 1870 1873 
1876 1879 1882 1885 1888 1891 1894 1897 1900 
1903 1906 1909 1912 1915 1918 1921 1924 1927 
1930 1933 1936 1939 1942 1945 1948 1951 1954 
1957 1960 1963 1966 1969 1972 1975 1978 1981 
1984 1987 1990 1993 1996 1999 2002 2005 2008 
2011 2014 2017 2020 2023 2026 2029 2032 2035 
2038 2041 2044 2047 2050 2053 2056 2059 2062 
2065 2068 2071 2074 2077 2080 2083 2086 2089 
2092 2095 2098 2101 2104 2107 2110 2113 2116 
2119 2122 2125 2128 2131 2134 2137 2140 2143 
2146 2149 2152 2155 2158 2161 2164 2167 2170 
2173 2176 2179 2182 2185 2188 2191 2194 2197 
2200 2203 2206 2209 2212 2215 2218 2221 2224 
2227 2230 2233 2236 2239 2242 2245 2248 2251 
2254 2257 2260 2263 2266 2269 2272 2275 2278 
2281 2284 2287 2290 2293 2296 2299 2302 2305 
2308 2311 2314 2317 2320 2323 2326 2329 2332 
2335 2338 2341 2344 2347 2350 2353 2356 2359 
2362 2365 2368 2371 2374 2377 2380 2383 2386 
2389 2392 2395 2398 2401 2404 2407 2410 2413 
2416 2419 2422 2425 2428 2431 2434 2437 2440 
2443 2446 2449 2452 2455 2458 2461 2464 2467 
2470 2473 2476 2479 2482 2485 2488 2491 2494 
2497 2500 2503 2506 2509 2512 2515 2518 2521 
2524 2527 2530 2533 2536 2539 2542 2545 2548 
2551 2554 2557 2560 2563 2566 2569 2572 2575 
2578 2581 2584 2587 2590 2593 2596 2599 2602 
2605 2608 2611 2614 2617 2620 2623 2626 2629 
2632 2635 2638 2641 2644 2647 2650 2653 2656 
2659 2662 2665 2668 2671 2674 2677 2680 2683 
2686 2689 2692 2695 2698 2701 2704 2707 2710 
2713 2716 2719 2722 2725 2728 2731 2734 2737 
2740 2743 2746 2749 2752 2755 2758 2761 2764 
2767 2770 2773 2776 2779 2782 2785 2788 2791 
2794 2797 2800 2803 2806 2809 2812 2815 2818 
2821 2824 2827 2830 2833 2836 2839 2842 2845 
2848 2851 2854 2857 2860 2863 2866 2869 2872 
2875 2878 2881 2884 2887 2890 2893 2896 2899 
2902 2905 2908 2911 2914 2917 2920 2923 2926 
2929 2932 2935 2938 2941 2944 2947 2950 2953 
2956 2959 2962 2965 2968 2971 2974 2977 2980 
2983 2986 2989 2992 2995 2998 3001 3004 3007 
3010 3013 3016 3019 3022 3025 3028 3031 3034 
3037 3040 3043 3046 3049 3052 3055 3058 3061 
3064 3067 3070 3073 3076 3079 3082 3085 3088 
3091 3094 3097 3100 3103 3106 3109 3112 3115 
3118 3121 3124 3127 3130 3133 3136 3139 3142 
3145 3148 3151 3154 3157 3160 3163 3166 3169 
3172 3175 3178 3181 3184 3187 3190 3193 3196 
3199 3202 3205 3208 3211 3214 3217 3220 3223 
3226 3229 3232 3235 3238 3241 3244 3247 3250 
3253 3256 3259 3262 3265 3268 3271 3274 3277 
3280 3283 3286 3289 3292 3295 3298 3301 3304 
3307 3310 3313 3316 3319 3322 3325 3328 3331 
3334 3337 3340 3343 3346 3349 3352 3355 3358 
3361 3364 3367 3370 3373 3376 3379 3382 3385 
3388 3391 3394 3397 3400 3404 3408 3412 3416 
3420 3424 3428 3432 3436 3440 3444 3448 3452 
3456 3460 3464 3468 3472 3476 3480 3484 3488 
3492 3496 3500 3504 3508 3512 3516 3520 3524 
3528 3532 3536 3540 3544 3548 3552 3556 3560 

CONNECTIVITY vtktypeint64
0 1 2 1 3 2 3 4 2 
4 5 2 5 6 2 6 7 2 
7 8 2 8 9 2 9 10 2 
10 11 2 11 12 2 12 13 2 
13 14 2 14 15 2 15 16 2 
16 17 2 17 18 2 18 19 2 
19 20 2 20 0 2 0 21 22 
0 22 1 21 23 24 21 24 22 
23 25 26 23 26 24 25 27 28 
25 28 26 27 29 30 27 30 28 
29 31 32 29 32 30 31 33 34 
31 34 32 33 35 36 33 36 34 
35 37 38 35 38 36 37 39 40 
37 40 38 39 41 42 39 42 40 
41 43 44 41 44 42 43 45 46 
43 46 44 1 22 47 1 47 3 
22 24 48 22 48 47 24 26 49 
24 49 48 26 28 50 26 50 49 
28 30 51 28 51 50 30 32 52 
30 52 51 32 34 53 32 53 52 
34 36 54 34 54 53 36 38 55 
36 55 54 38 40 56 38 56 55 
40 42 57 40 57 56 42 44 58 
42 58 57 44 46 59 44 59 58 
3 47 60 3 60 4 47 48 61 
47 61 60 48 49 62 48 62 61 
49 50 63 49 63 62 50 51 64 
50 64 63 51 52 65 51 65 64 
52 53 66 52 66 65 53 54 67 
53 67 66 54 55 68 54 68 67 
55 56 69 55 69 68 56 57 70 
56 70 69 57 58 71 57 71 70 
58 59 72 58 72 71 4 60 73 
4 73 5 60 61 74 60 74 73 
61 62 75 61 75 74 62 63 76 
62 76 75 63 64 77 63 77 76 
64 65 78 64 78 77 65 66 79 
65 79 78 66 67 80 66 80 79 
67 68 81 67 81 80 68 69 82 
68 82 81 69 70 83 69 83 82 
70 71 84 70 84 83 71 72 85 
71 85 84 5 73 86 5 86 6 
73 74 87 73 87 86 74 75 88 
74 88 87 75 76 89 75 89 88 
76 77 90 76 90 89 77 78 91 
77 91 90 78 79 92 78 92 91 
79 80 93 79 93 92 80 81 94 
80 94 93 81 82 95 81 95 94 
82 83 96 82 96 95 83 84 97 
83 97 96 84 85 98 84 98 97 
6 86 99 6 99 7 86 87 100 
86 100 99 87 88 101 87 101 100 
88 89 102 88 102 101 89 90 103 
89 103 102 90 91 104 90 104 103 
91 92 105 91 105 104 92 93 106 
92 106 105 93 94 107 93 107 106 
94 95 108 94 108 107 95 96 109 
95 109 108 96 97 110 96 110 109 
97 98 111 97 111 110 7 99 112 
7 112 8 99 100 113 99 113 112 
100 101 114 100 114 113 101 102 115 
101 115 114 102 103 116 102 116 115 
103 104 117 103 117 116 104 105 118 
104 118 117 105 106 119 105 119 118 
106 107 120 106 120 119 107 108 121 
107 121 120 108 109 122 108 122 121 
109 110 123 109 123 122 110 111 124 
110 124 123 8 112 125 8 125 9 
112 113 126 112 126 125 113 114 127 
113 127 126 114 115 128 114 128 127 
115 116 129 115 129 128 116 117 130 
116 130 129 117 118 131 117 131 130 
118 119 132 118 132 131 119 120 133 
119 133 132 120 121 134 120 134 133 
121 122 135 121 135 134 122 123 136 
122 136 135 123 124 137 123 137 136 
9 125 138 9 138 10 125 126 139 
125 139 138 126 127 140 126 140 139 
127 128 141 127 141 140 128 129 142 
128 142 141 129 130 143 129 143 142 
130 131 144 130 144 143 131 132 145 
131 145 144 132 133 146 132 146 145 
133 134 147 133 147 146 134 135 148 
134 148 147 135 136 149 135 149 148 
136 137 150 136 150 149 10 138 151 
10 151 11 138 139 152 138 152 151 
139 140 153 139 153 152 140 141 154 
140 154 153 141 142 155 141 155 154 
142 143 156 142 156 155 143 144 157 
143 157 156 144 145 158 144 158 157 
145 146 159 145 159 158 146 147 160 
146 160 159 147 148 161 147 161 160 
148 149 162 148 162 161 149 150 163 
149 163 162 11 151 164 11 164 12 
151 152 165 151 165 164 152 153 166 
152 166 165 153 154 167 153 167 166 
154 155 168 154 168 167 155 156 169 
155 169 168 156 157 170 156 170 169 
157 158 171 157 171 170 158 159 172 
158 172 171 159 160 173 159 173 172 
160 161 174 160 174 173 161 162 175 
161 175 174 162 163 176 162 176 175 
12 164 177 12 177 13 164 165 178 
164 178 177 165 166 179 165 179 178 
166 167 180 166 180 179 167 168 181 
167 181 180 168 169 182 168 182 181 
169 170 183 169 183 182 170 171 184 
170 184 183 171 172 185 171 185 184 
172 173 186 172 186 185 173 174 187 
173 187 186 174 175 188 174 188 187 
175 176 189 175 189 188 13 177 190 
13 190 14 177 178 191 177 191 190 
178 179 192 178 192 191 179 180 193 
179 193 192 180 181 194 180 194 193 
181 182 195 181 195 194 182 183 196 
182 196 195 183 184 197 183 197 196 
184 185 198 184 198 197 185 186 199 
185 199 198 186 187 200 186 200 199 
187 188 201 187 201 200 188 189 202 
188 202 201 14 190 203 14 203 15 
190 191 204 190 204 203 191 192 205 
191 205 204 192 193 206 192 206 205 
193 194 207 193 207 206 194 195 208 
194 208 207 195 196 209 195 209 208 
196 197 210 196 210 209 197 198 211 
197 211 210 198 199 212 198 212 211 
199 200 213 199 213 212 200 201 214 
200 214 213 201 202 215 201 215 214 
15 203 216 15 216 16 203 204 217 
203 217 216 204 205 218 204 218 217 
205 206 219 205 219 218 206 207 220 
206 220 219 207 208 221 207 221 220 
208 209 222 208 222 221 209 210 223 
209 223 222 210 211 224 210 224 223 
211 212 225 211 225 224 212 213 226 
212 226 225 213 214 227 213 227 226 
214 215 228 214 228 227 16 216 229 
16 229 17 216 217 230 216 230 229 
217 218 231 217 231 230 218 219 232 
218 232 231 219 220 233 219 233 232 
220 221 234 220 234 233 221 222 235 
221 235 234 222 223 236 222 236 235 
223 224 237 223 237 236 224 225 238 
224 238 237 225 226 239 225 239 238 
226 227 240 226 240 239 227 228 241 
227 241 240 17 229 242 17 242 18 
229 230 243 229 243 242 230 231 244 
230 244 243 231 232 245 231 245 244 
232 233 246 232 246 245 233 234 247 
233 247 246 234 235 248 234 248 247 
235 236 249 235 249 248 236 237 250 
236 250 249 237 238 251 237 251 250 
238 239 252 238 252 251 239 240 253 
239 253 252 240 241 254 240 254 253 
18 242 255 18 255 19 242 243 256 
242 256 255 243 244 257 243 257 256 
244 245 258 244 258 257 245 246 259 
245 259 258 246 247 260 246 260 259 
247 248 261 247 261 260 248 249 262 
248 262 261 249 250 263 249 263 262 
250 251 264 250 264 263 251 252 265 
251 265 264 252 253 266 252 266 265 
253 254 267 253 267 266 19 255 268 
19 268 20 255 256 269 255 269 268 
256 257 270 256 270 269 257 258 271 
257 271 270 258 259 272 258 272 271 
259 260 273 259 273 272 260 261 274 
260 274 273 261 262 275 261 275 274 
262 263 276 262 276 275 263 264 277 
263 277 276 264 265 278 264 278 277 
265 266 279 265 279 278 266 267 280 
266 280 279 20 268 21 20 21 0 
268 269 23 268 23 21 269 270 25 
269 25 23 270 271 27 270 27 25 
271 272 29 271 29 27 272 273 31 
272 31 29 273 274 33 273 33 31 
274 275 35 274 35 33 275 276 37 
275 37 35 276 277 39 276 39 37 
277 278 41 277 41 39 278 279 43 
278 43 41 279 280 45 279 45 43 
45 281 282 283 45 283 284 46 46 
284 285 286 46 286 287 59 59 287 
288 289 59 289 290 72 72 290 291 
292 72 292 293 85 85 293 294 295 
85 295 296 98 98 296 297 298 98 
298 299 111 111 299 300 301 111 301 
302 124 124 302 303 304 124 304 305 
137 137 305 306 307 137 307 308 150 
150 308 309 310 150 310 311 163 163 
311 312 313 163 313 314 176 176 314 
315 316 176 316 317 189 189 317 318 
319 189 319 320 202 202 320 321 322 
202 322 323 215 215 323 324 325 215 
325 326 228 228 326 327 328 228 328 
329 241 241 329 330 331 241 331 332 
254 254 332 333 334 254 334 335 267 
267 335 336 337 267 337 338 280 280 
338 339 340 280 340 281 45 341 342 
343 343 342 344 344 342 345 345 342 
346 346 342 347 347 342 348 348 342 
349 349 342 350 350 342 351 351 342 
352 352 342 353 353 342 354 354 342 
355 355 342 356 356 342 357 357 342 
358 358 342 359 359 342 360 360 342 
361 361 342 341 362 363 364 362 364 
365 363 366 367 363 367 364 366 368 
369 366 369 367 368 370 371 368 371 
369 370 372 373 370 373 371 372 374 
375 372 375 373 374 376 377 374 377 
375 376 378 379 376 379 377 378 380 
381 378 381 379 380 382 383 380 383 
381 382 384 385 382 385 383 384 386 
387 384 387 385 386 341 343 386 343 
387 365 364 388 365 388 389 364 367 
390 364 390 388 367 369 391 367 391 
390 369 371 392 369 392 391 371 373 
393 371 393 392 373 375 394 373 394 
393 375 377 395 375 395 394 377 379 
396 377 396 395 379 381 397 379 397 
396 381 383 398 381 398 397 383 385 
399 383 399 398 385 387 400 385 400 
399 387 343 344 387 344 400 389 388 
401 389 401 402 388 390 403 388 403 
401 390 391 404 390 404 403 391 392 
405 391 405 404 392 393 406 392 406 
405 393 394 407 393 407 406 394 395 
408 394 408 407 395 396 409 395 409 
408 396 397 410 396 410 409 397 398 
411 397 411 410 398 399 412 398 412 
411 399 400 413 399 413 412 400 344 
345 400 345 413 402 401 414 402 414 
415 401 403 416 401 416 414 403 404 
417 403 417 416 404 405 418 404 418 
417 405 406 419 405 419 418 406 407 
420 406 420 419 407 408 421 407 421 
420 408 409 422 408 422 421 409 410 
423 409 423 422 410 411 424 410 424 
423 411 412 425 411 425 424 412 413 
426 412 426 425 413 345 346 413 346 
426 415 414 427 415 427 428 414 416 
429 414 429 427 416 417 430 416 430 
429 417 418 431 417 431 430 418 419 
432 418 432 431 419 420 433 419 433 
432 420 421 434 420 434 433 421 422 
435 421 435 434 422 423 436 422 436 
435 423 424 437 423 437 436 424 425 
438 424 438 437 425 426 439 425 439 
438 426 346 347 426 347 439 428 427 
440 428 440 441 427 429 442 427 442 
440 429 430 443 429 443 442 430 431 
444 430 444 443 431 432 445 431 445 
444 432 433 446 432 446 445 433 434 
447 433 447 446 434 435 448 434 448 
447 435 436 449 435 449 448 436 437 
450 436 450 449 437 438 451 437 451 
450 438 439 452 438 452 451 439 347 
348 439 348 452 441 440 453 441 453 
454 440 442 455 440 455 453 442 443 
456 442 456 455 443 444 457 443 457 
456 444 445 458 444 458 457 445 446 
459 445 459 458 446 447 460 446 460 
459 447 448 461 447 461 460 448 449 
462 448 462 461 449 450 463 449 463 
462 450 451 464 450 464 463 451 452 
465 451 465 464 452 348 349 452 349 
465 454 453 466 454 466 467 453 455 
468 453 468 466 455 456 469 455 469 
468 456 457 470 456 470 469 457 458 
471 457 471 470 458 459 472 458 472 
471 459 460 473 459 473 472 460 461 
474 460 474 473 461 462 475 461 475 
474 462 463 476 462 476 475 463 464 
477 463 477 476 464 465 478 464 478 
477 465 349 350 465 350 478 467 466 
479 467 479 480 466 468 481 466 481 
479 468 469 482 468 482 481 469 470 
483 469 483 482 470 471 484 470 484 
483 471 472 485 471 485 484 472 473 
486 472 486 485 473 474 487 473 487 
486 474 475 488 474 488 487 475 476 
489 475 489 488 476 477 490 476 490 
489 477 478 491 477 491 490 478 350 
351 478 351 491 480 479 492 480 492 
493 479 481 494 479 494 492 481 482 
495 481 495 494 482 483 496 482 496 
495 483 484 497 483 497 496 484 485 
498 484 498 497 485 486 499 485 499 
498 486 487 500 486 500 499 487 488 
501 487 501 500 488 489 502 488 502 
501 489 490 503 489 503 502 490 491 
504 490 504 503 491 351 352 491 352 
504 493 492 505 493 505 506 492 494 
507 492 507 505 494 495 508 494 508 
507 495 496 509 495 509 508 496 497 
510 496 510 509 497 498 511 497 511 
510 498 499 512 498 512 511 499 500 
513 499 513 512 500 501 514 500 514 
513 501 502 515 501 515 514 502 503 
516 502 516 515 503 504 517 503 517 
516 504 352 353 504 353 517 506 505 
518 506 518 519 505 507 520 505 520 
518 507 508 521 507 521 520 508 509 
522 508 522 521 509 510 523 509 523 
522 510 511 524 510 524 523 511 512 
525 511 525 524 512 513 526 512 526 
525 513 514 527 513 527 526 514 515 
528 514 528 527 515 516 529 515 529 
528 516 517 530 516 530 529 517 353 
354 517 354 530 519 518 531 519 531 
532 518 520 533 518 533 531 520 521 
534 520 534 533 521 522 535 521 535 
534 522 523 536 522 536 535 523 524 
537 523 537 536 524 525 538 524 538 
537 525 526 539 525 539 538 526 527 
540 526 540 539 527 528 541 527 541 
540 528 529 542 528 542 541 529 530 
543 529 543 542 530 354 355 530 355 
543 532 531 544 532 544 545 531 533 
546 531 546 544 533 534 547 533 547 
546 534 535 548 534 548 547 535 536 
549 535 549 548 536 537 550 536 550 
549 537 538 551 537 551 550 538 539 
552 538 552 551 539 540 553 539 553 
552 540 541 554 540 554 553 541 542 
555 541 555 554 542 543 556 542 556 
555 543 355 356 543 356 556 545 544 
557 545 557 558 544 546 559 544 559 
557 546 547 560 546 560 559 547 548 
561 547 561 560 548 549 562 548 562 
561 549 550 563 549 563 562 550 551 
564 550 564 563 551 552 565 551 565 
564 552 553 566 552 566 565 553 554 
567 553 567 566 554 555 568 554 568 
567 555 556 569 555 569 568 556 356 
357 556 357 569 558 557 570 558 570 
571 557 559 572 557 572 570 559 560 
573 559 573 572 560 561 574 560 574 
573 561 562 575 561 575 574 562 563 
576 562 576 575 563 564 577 563 577 
576 564 565 578 564 578 577 565 566 
579 565 579 578 566 567 580 566 580 
579 567 568 581 567 581 580 568 569 
582 568 582 581 569 357 358 569 358 
582 571 570 583 571 583 584 570 572 
585 570 585 583 572 573 586 572 586 
585 573 574 587 573 587 586 574 575 
588 574 588 587 575 576 589 575 589 
588 576 577 590 576 590 589 577 578 
591 577 591 590 578 579 592 578 592 
591 579 580 593 579 593 592 580 581 
594 580 594 593 581 582 595 581 595 
594 582 358 359 582 359 595 584 583 
596 584 596 597 583 585 598 583 598 
596 585 586 599 585 599 598 586 587 
600 586 600 599 587 588 601 587 601 
600 588 589 602 588 602 601 589 590 
603 589 603 602 590 591 604 590 604 
603 591 592 605 591 605 604 592 593 
606 592 606 605 593 594 607 593 607 
606 594 595 608 594 608 607 595 359 
360 595 360 608 597 596 609 597 609 
610 596 598 611 596 611 609 598 599 
612 598 612 611 599 600 613 599 613 
612 600 601 614 600 614 613 601 602 
615 601 615 614 602 603 616 602 616 
615 603 604 617 603 617 616 604 605 
618 604 618 617 605 606 619 605 619 
618 606 607 620 606 620 619 607 608 
621 607 621 620 608 360 361 608 361 
621 610 609 363 610 363 362 609 611 
366 609 366 363 611 612 368 611 368 
366 612 613 370 612 370 368 613 614 
372 613 372 370 614 615 374 614 374 
372 615 616 376 615 376 374 616 617 
378 616 378 376 617 618 380 617 380 
378 618 619 382 618 382 380 619 620 
384 619 384 382 620 621 386 620 386 
384 621 361 341 621 341 386 362 365 
282 281 365 284 283 282 365 389 285 
284 389 287 286 285 389 402 288 287 
402 290 289 288 402 415 291 290 415 
293 292 291 415 428 294 293 428 296 
295 294 428 441 297 296 441 299 298 
297 441 454 300 299 454 302 301 300 
454 467 303 302 467 305 304 303 467 
480 306 305 480 308 307 306 480 493 
309 308 493 311 310 309 493 506 312 
311 506 314 313 312 506 519 315 314 
519 317 316 315 519 532 318 317 532 
320 319 318 532 545 321 320 545 323 
322 321 545 558 324 323 558 326 325 
324 558 571 327 326 571 329 328 327 
571 584 330 329 584 332 331 330 584 
597 333 332 597 335 334 333 597 610 
336 335 610 338 337 336 610 362 339 
338 362 281 340 339 


================================================
FILE: testing/data/branched3.vtk
================================================
# vtk DataFile Version 5.1
vtk output
ASCII
DATASET POLYDATA
POINTS 882 double
0.36786195636 0 0.33864077926 0.41858324409 0 0.27347406745 0.39809632301 0.12934933603 0.27347406745 
0.34985750914 0.11367559433 0.33864077926 0.45788666606 0 0.20084771514 0.43547609448 0.14149476588 0.20084771514 
0.48470014334 0 0.1227427423 0.4609772265 0.14978057146 0.1227427423 0.49829223752 0 0.041289672256 
0.47390407324 0.15398077667 0.041289672256 0.49829223752 0 -0.041289672256 0.47390407324 0.15398077667 -0.041289672256 
0.48470014334 0 -0.1227427423 0.4609772265 0.14978057146 -0.1227427423 0.45788666606 0 -0.20084771514 
0.43547609448 0.14149476588 -0.20084771514 0.41858324409 0 -0.27347406745 0.39809632301 0.12934933603 -0.27347406745 
0.36786195636 0 -0.33864077926 0.34985750914 0.11367559433 -0.33864077926 0.33864095807 0.24603705108 0.27347406745 
0.29760658741 0.21622383595 0.33864077926 0.37043809891 0.26913902164 0.20084771514 0.39213064313 0.2848995924 0.1227427423 
0.40312689543 0.29288882017 0.041289672256 0.40312689543 0.29288882017 -0.041289672256 0.39213064313 0.2848995924 -0.1227427423 
0.37043809891 0.26913902164 -0.20084771514 0.33864095807 0.24603705108 -0.27347406745 0.29760658741 0.21622383595 -0.33864077926 
0.24603705108 0.33864095807 0.27347406745 0.21622383595 0.29760658741 0.33864077926 0.26913902164 0.37043809891 0.20084771514 
0.2848995924 0.39213064313 0.1227427423 0.29288882017 0.40312689543 0.041289672256 0.29288882017 0.40312689543 -0.041289672256 
0.2848995924 0.39213064313 -0.1227427423 0.26913902164 0.37043809891 -0.20084771514 0.24603705108 0.33864095807 -0.27347406745 
0.21622383595 0.29760658741 -0.33864077926 0.12934933603 0.39809632301 0.27347406745 0.11367559433 0.34985750914 0.33864077926 
0.14149476588 0.43547609448 0.20084771514 0.14978057146 0.4609772265 0.1227427423 0.15398077667 0.47390407324 0.041289672256 
0.15398077667 0.47390407324 -0.041289672256 0.14978057146 0.4609772265 -0.1227427423 0.14149476588 0.43547609448 -0.20084771514 
0.12934933603 0.39809632301 -0.27347406745 0.11367559433 0.34985750914 -0.33864077926 2.5630831518e-17 0.41858324409 0.27347406745 
2.2525047905e-17 0.36786195636 0.33864077926 2.8037472151e-17 0.45788666606 0.20084771514 2.9679323096e-17 0.48470014334 0.1227427423 
3.0511599142e-17 0.49829223752 0.041289672256 3.0511599142e-17 0.49829223752 -0.041289672256 2.9679323096e-17 0.48470014334 -0.1227427423 
2.8037472151e-17 0.45788666606 -0.20084771514 2.5630831518e-17 0.41858324409 -0.27347406745 2.2525047905e-17 0.36786195636 -0.33864077926 
-0.12934933603 0.39809632301 0.27347406745 -0.11367559433 0.34985750914 0.33864077926 -0.14149476588 0.43547609448 0.20084771514 
-0.14978057146 0.4609772265 0.1227427423 -0.15398077667 0.47390407324 0.041289672256 -0.15398077667 0.47390407324 -0.041289672256 
-0.14978057146 0.4609772265 -0.1227427423 -0.14149476588 0.43547609448 -0.20084771514 -0.12934933603 0.39809632301 -0.27347406745 
-0.11367559433 0.34985750914 -0.33864077926 -0.24603705108 0.33864095807 0.27347406745 -0.21622383595 0.29760658741 0.33864077926 
-0.26913902164 0.37043809891 0.20084771514 -0.2848995924 0.39213064313 0.1227427423 -0.29288882017 0.40312689543 0.041289672256 
-0.29288882017 0.40312689543 -0.041289672256 -0.2848995924 0.39213064313 -0.1227427423 -0.26913902164 0.37043809891 -0.20084771514 
-0.24603705108 0.33864095807 -0.27347406745 -0.21622383595 0.29760658741 -0.33864077926 -0.33864095807 0.24603705108 0.27347406745 
-0.29760658741 0.21622383595 0.33864077926 -0.37043809891 0.26913902164 0.20084771514 -0.39213064313 0.2848995924 0.1227427423 
-0.40312689543 0.29288882017 0.041289672256 -0.40312689543 0.29288882017 -0.041289672256 -0.39213064313 0.2848995924 -0.1227427423 
-0.37043809891 0.26913902164 -0.20084771514 -0.33864095807 0.24603705108 -0.27347406745 -0.29760658741 0.21622383595 -0.33864077926 
-0.39809632301 0.12934933603 0.27347406745 -0.34985750914 0.11367559433 0.33864077926 -0.43547609448 0.14149476588 0.20084771514 
-0.4609772265 0.14978057146 0.1227427423 -0.47390407324 0.15398077667 0.041289672256 -0.47390407324 0.15398077667 -0.041289672256 
-0.4609772265 0.14978057146 -0.1227427423 -0.43547609448 0.14149476588 -0.20084771514 -0.39809632301 0.12934933603 -0.27347406745 
-0.34985750914 0.11367559433 -0.33864077926 -0.41858324409 5.1261663035e-17 0.27347406745 -0.36786195636 4.5050095809e-17 0.33864077926 
-0.45788666606 5.6074944302e-17 0.20084771514 -0.48470014334 5.9358646193e-17 0.1227427423 -0.49829223752 6.1023198283e-17 0.041289672256 
-0.49829223752 6.1023198283e-17 -0.041289672256 -0.48470014334 5.9358646193e-17 -0.1227427423 -0.45788666606 5.6074944302e-17 -0.20084771514 
-0.41858324409 5.1261663035e-17 -0.27347406745 -0.36786195636 4.5050095809e-17 -0.33864077926 -0.39809632301 -0.12934933603 0.27347406745 
-0.34985750914 -0.11367559433 0.33864077926 -0.43547609448 -0.14149476588 0.20084771514 -0.4609772265 -0.14978057146 0.1227427423 
-0.47390407324 -0.15398077667 0.041289672256 -0.47390407324 -0.15398077667 -0.041289672256 -0.4609772265 -0.14978057146 -0.1227427423 
-0.43547609448 -0.14149476588 -0.20084771514 -0.39809632301 -0.12934933603 -0.27347406745 -0.34985750914 -0.11367559433 -0.33864077926 
-0.33864095807 -0.24603705108 0.27347406745 -0.29760658741 -0.21622383595 0.33864077926 -0.37043809891 -0.26913902164 0.20084771514 
-0.39213064313 -0.2848995924 0.1227427423 -0.40312689543 -0.29288882017 0.041289672256 -0.40312689543 -0.29288882017 -0.041289672256 
-0.39213064313 -0.2848995924 -0.1227427423 -0.37043809891 -0.26913902164 -0.20084771514 -0.33864095807 -0.24603705108 -0.27347406745 
-0.29760658741 -0.21622383595 -0.33864077926 -0.24603705108 -0.33864095807 0.27347406745 -0.21622383595 -0.29760658741 0.33864077926 
-0.26913902164 -0.37043809891 0.20084771514 -0.2848995924 -0.39213064313 0.1227427423 -0.29288882017 -0.40312689543 0.041289672256 
-0.29288882017 -0.40312689543 -0.041289672256 -0.2848995924 -0.39213064313 -0.1227427423 -0.26913902164 -0.37043809891 -0.20084771514 
-0.24603705108 -0.33864095807 -0.27347406745 -0.21622383595 -0.29760658741 -0.33864077926 -0.12934933603 -0.39809632301 0.27347406745 
-0.11367559433 -0.34985750914 0.33864077926 -0.14149476588 -0.43547609448 0.20084771514 -0.14978057146 -0.4609772265 0.1227427423 
-0.15398077667 -0.47390407324 0.041289672256 -0.15398077667 -0.47390407324 -0.041289672256 -0.14978057146 -0.4609772265 -0.1227427423 
-0.14149476588 -0.43547609448 -0.20084771514 -0.12934933603 -0.39809632301 -0.27347406745 -0.11367559433 -0.34985750914 -0.33864077926 
-7.6892492899e-17 -0.41858324409 0.27347406745 -6.7575143714e-17 -0.36786195636 0.33864077926 -8.4112416453e-17 -0.45788666606 0.20084771514 
-8.9037972598e-17 -0.48470014334 0.1227427423 -9.1534800733e-17 -0.49829223752 0.041289672256 -9.1534800733e-17 -0.49829223752 -0.041289672256 
-8.9037972598e-17 -0.48470014334 -0.1227427423 -8.4112416453e-17 -0.45788666606 -0.20084771514 -7.6892492899e-17 -0.41858324409 -0.27347406745 
-6.7575143714e-17 -0.36786195636 -0.33864077926 0.12934933603 -0.39809632301 0.27347406745 0.11367559433 -0.34985750914 0.33864077926 
0.14149476588 -0.43547609448 0.20084771514 0.14978057146 -0.4609772265 0.1227427423 0.15398077667 -0.47390407324 0.041289672256 
0.15398077667 -0.47390407324 -0.041289672256 0.14978057146 -0.4609772265 -0.1227427423 0.14149476588 -0.43547609448 -0.20084771514 
0.12934933603 -0.39809632301 -0.27347406745 0.11367559433 -0.34985750914 -0.33864077926 0.24603705108 -0.33864095807 0.27347406745 
0.21622383595 -0.29760658741 0.33864077926 0.26913902164 -0.37043809891 0.20084771514 0.2848995924 -0.39213064313 0.1227427423 
0.29288882017 -0.40312689543 0.041289672256 0.29288882017 -0.40312689543 -0.041289672256 0.2848995924 -0.39213064313 -0.1227427423 
0.26913902164 -0.37043809891 -0.20084771514 0.24603705108 -0.33864095807 -0.27347406745 0.21622383595 -0.29760658741 -0.33864077926 
0.33864095807 -0.24603705108 0.27347406745 0.29760658741 -0.21622383595 0.33864077926 0.37043809891 -0.26913902164 0.20084771514 
0.39213064313 -0.2848995924 0.1227427423 0.40312689543 -0.29288882017 0.041289672256 0.40312689543 -0.29288882017 -0.041289672256 
0.39213064313 -0.2848995924 -0.1227427423 0.37043809891 -0.26913902164 -0.20084771514 0.33864095807 -0.24603705108 -0.27347406745 
0.29760658741 -0.21622383595 -0.33864077926 0.39809632301 -0.12934933603 0.27347406745 0.34985750914 -0.11367559433 0.33864077926 
0.43547609448 -0.14149476588 0.20084771514 0.4609772265 -0.14978057146 0.1227427423 0.47390407324 -0.15398077667 0.041289672256 
0.47390407324 -0.15398077667 -0.041289672256 0.4609772265 -0.14978057146 -0.1227427423 0.43547609448 -0.14149476588 -0.20084771514 
0.39809632301 -0.12934933603 -0.27347406745 0.34985750914 -0.11367559433 -0.33864077926 0.3283653157 -6.2943287406e-18 -0.375 
0.32206539101 0.039776188896 -0.37500000495 0.3185939096 0.061694295065 -0.37500000495 0.31229398784 0.10147046551 -0.375 
0.29401088955 0.13735306593 -0.37499999486 0.28393623568 0.15712568057 -0.37499999486 0.26565312857 0.1930082983 -0.375 
0.23717652463 0.22148490223 -0.375 0.22148490223 0.23717652463 -0.375 0.1930082983 0.26565312857 -0.375 
0.15712568057 0.28393623568 -0.37500000514 0.13735306593 0.29401088955 -0.37500000514 0.10147046551 0.31229398784 -0.375 
0.061694295065 0.3185939096 -0.37499999505 0.039776188896 0.32206539101 -0.37499999505 2.5828452418e-17 0.3283653157 -0.375 
-0.039776188896 0.32206539101 -0.37500000495 -0.061694295065 0.3185939096 -0.37500000495 -0.10147046551 0.31229398784 -0.375 
-0.13735306593 0.29401088955 -0.37499999486 -0.15712568057 0.28393623568 -0.37499999486 -0.1930082983 0.26565312857 -0.375 
-0.22148490223 0.23717652463 -0.375 -0.23717652463 0.22148490223 -0.375 -0.26565312857 0.1930082983 -0.375 
-0.28393623568 0.15712568057 -0.37500000514 -0.29401088955 0.13735306593 -0.37500000514 -0.31229398784 0.10147046551 -0.375 
-0.3185939096 0.061694295065 -0.37499999505 -0.32206539101 0.039776188896 -0.37499999505 -0.3283653157 4.1448352812e-17 -0.375 
-0.32206539101 -0.039776188896 -0.37500000495 -0.3185939096 -0.061694295065 -0.37500000495 -0.31229398784 -0.10147046551 -0.375 
-0.29401088955 -0.13735306593 -0.37499999486 -0.28393623568 -0.15712568057 -0.37499999486 -0.26565312857 -0.1930082983 -0.375 
-0.23717652463 -0.22148490223 -0.375 -0.22148490223 -0.23717652463 -0.375 -0.1930082983 -0.26565312857 -0.375 
-0.15712568057 -0.28393623568 -0.37500000514 -0.13735306593 -0.29401088955 -0.37500000514 -0.10147046551 -0.31229398784 -0.375 
-0.061694295065 -0.3185939096 -0.37499999505 -0.039776188896 -0.32206539101 -0.37499999505 -6.1641014888e-17 -0.3283653157 -0.375 
0.039776188896 -0.32206539101 -0.37500000495 0.061694295065 -0.3185939096 -0.37500000495 0.10147046551 -0.31229398784 -0.375 
0.13735306593 -0.29401088955 -0.37499999486 0.15712568057 -0.28393623568 -0.37499999486 0.1930082983 -0.26565312857 -0.375 
0.22148490223 -0.23717652463 -0.375 0.23717652463 -0.22148490223 -0.375 0.26565312857 -0.1930082983 -0.375 
0.28393623568 -0.15712568057 -0.37500000514 0.29401088955 -0.13735306593 -0.37500000514 0.31229398784 -0.10147046551 -0.375 
0.3185939096 -0.061694295065 -0.37499999505 0.32206539101 -0.039776188896 -0.37499999505 0.082297295332 0 -1.2431806326 
0 0 -1.25 0.078269377351 0.025431262329 -1.2431806326 0.066579908133 0.048373136669 -1.2431806326 
0.048373136669 0.066579908133 -1.2431806326 0.025431262329 0.078269377351 -1.2431806326 5.0392558428e-18 0.082297295332 -1.2431806326 
-0.025431262329 0.078269377351 -1.2431806326 -0.048373136669 0.066579908133 -1.2431806326 -0.066579908133 0.048373136669 -1.2431806326 
-0.078269377351 0.025431262329 -1.2431806326 -0.082297295332 1.0078511686e-17 -1.2431806326 -0.078269377351 -0.025431262329 -1.2431806326 
-0.066579908133 -0.048373136669 -1.2431806326 -0.048373136669 -0.066579908133 -1.2431806326 -0.025431262329 -0.078269377351 -1.2431806326 
-1.5117767942e-17 -0.082297295332 -1.2431806326 0.025431262329 -0.078269377351 -1.2431806326 0.048373136669 -0.066579908133 -1.2431806326 
0.066579908133 -0.048373136669 -1.2431806326 0.078269377351 -0.025431262329 -1.2431806326 0.36786195636 0 -0.41135922074 
0.41858324409 0 -0.47652593255 0.39809632301 0.12934933603 -0.47652593255 0.34985750914 0.11367559433 -0.41135922074 
0.45788666606 0 -0.54915231466 0.43547609448 0.14149476588 -0.54915231466 0.48470014334 0 -0.6272572279 
0.4609772265 0.14978057146 -0.6272572279 0.49829223752 0 -0.70871031284 0.47390407324 0.15398077667 -0.70871031284 
0.49829223752 0 -0.79128968716 0.47390407324 0.15398077667 -0.79128968716 0.48470014334 0 -0.8727427721 
0.4609772265 0.14978057146 -0.8727427721 0.45788666606 0 -0.95084768534 0.43547609448 0.14149476588 -0.95084768534 
0.41858324409 0 -1.0234740973 0.39809632301 0.12934933603 -1.0234740973 0.36786195636 0 -1.0886408091 
0.34985750914 0.11367559433 -1.0886408091 0.30710634589 0 -1.1445702314 0.29207551479 0.0949010849 -1.1445702314 
0.23797369003 0 -1.1897368431 0.2263264358 0.073537915945 -1.1897368431 0.16234973073 0 -1.2229086161 
0.15440377593 0.050168827176 -1.2229086161 0.33864095807 0.24603705108 -0.47652593255 0.29760658741 0.21622383595 -0.41135922074 
0.37043809891 0.26913902164 -0.54915231466 0.39213064313 0.2848995924 -0.6272572279 0.40312689543 0.29288882017 -0.70871031284 
0.40312689543 0.29288882017 -0.79128968716 0.39213064313 0.2848995924 -0.8727427721 0.37043809891 0.26913902164 -0.95084768534 
0.33864095807 0.24603705108 -1.0234740973 0.29760658741 0.21622383595 -1.0886408091 0.24845425785 0.1805125922 -1.1445702314 
0.19252476096 0.13987742364 -1.1897368431 0.13134369254 0.095426782966 -1.2229086161 0.24603705108 0.33864095807 -0.47652593255 
0.21622383595 0.29760658741 -0.41135922074 0.26913902164 0.37043809891 -0.54915231466 0.2848995924 0.39213064313 -0.6272572279 
0.29288882017 0.40312689543 -0.70871031284 0.29288882017 0.40312689543 -0.79128968716 0.2848995924 0.39213064313 -0.8727427721 
0.26913902164 0.37043809891 -0.95084768534 0.24603705108 0.33864095807 -1.0234740973 0.21622383595 0.29760658741 -1.0886408091 
0.1805125922 0.24845425785 -1.1445702314 0.13987742364 0.19252476096 -1.1897368431 0.095426782966 0.13134369254 -1.2229086161 
0.12934933603 0.39809632301 -0.47652593255 0.11367559433 0.34985750914 -0.41135922074 0.14149476588 0.43547609448 -0.54915231466 
0.14978057146 0.4609772265 -0.6272572279 0.15398077667 0.47390407324 -0.70871031284 0.15398077667 0.47390407324 -0.79128968716 
0.14978057146 0.4609772265 -0.8727427721 0.14149476588 0.43547609448 -0.95084768534 0.12934933603 0.39809632301 -1.0234740973 
0.11367559433 0.34985750914 -1.0886408091 0.0949010849 0.29207551479 -1.1445702314 0.073537915945 0.2263264358 -1.1897368431 
0.050168827176 0.15440377593 -1.2229086161 2.5630831518e-17 0.41858324409 -0.47652593255 2.2525047905e-17 0.36786195636 -0.41135922074 
2.8037472151e-17 0.45788666606 -0.54915231466 2.9679323096e-17 0.48470014334 -0.6272572279 3.0511599142e-17 0.49829223752 -0.70871031284 
3.0511599142e-17 0.49829223752 -0.79128968716 2.9679323096e-17 0.48470014334 -0.8727427721 2.8037472151e-17 0.45788666606 -0.95084768534 
2.5630831518e-17 0.41858324409 -1.0234740973 2.2525047905e-17 0.36786195636 -1.0886408091 1.880484115e-17 0.30710634589 -1.1445702314 
1.4571686463e-17 0.23797369003 -1.1897368431 9.9410541201e-18 0.16234973073 -1.2229086161 -0.12934933603 0.39809632301 -0.47652593255 
-0.11367559433 0.34985750914 -0.41135922074 -0.14149476588 0.43547609448 -0.54915231466 -0.14978057146 0.4609772265 -0.6272572279 
-0.15398077667 0.47390407324 -0.70871031284 -0.15398077667 0.47390407324 -0.79128968716 -0.14978057146 0.4609772265 -0.8727427721 
-0.14149476588 0.43547609448 -0.95084768534 -0.12934933603 0.39809632301 -1.0234740973 -0.11367559433 0.34985750914 -1.0886408091 
-0.0949010849 0.29207551479 -1.1445702314 -0.073537915945 0.2263264358 -1.1897368431 -0.050168827176 0.15440377593 -1.2229086161 
-0.24603705108 0.33864095807 -0.47652593255 -0.21622383595 0.29760658741 -0.41135922074 -0.26913902164 0.37043809891 -0.54915231466 
-0.2848995924 0.39213064313 -0.6272572279 -0.29288882017 0.40312689543 -0.70871031284 -0.29288882017 0.40312689543 -0.79128968716 
-0.2848995924 0.39213064313 -0.8727427721 -0.26913902164 0.37043809891 -0.95084768534 -0.24603705108 0.33864095807 -1.0234740973 
-0.21622383595 0.29760658741 -1.0886408091 -0.1805125922 0.24845425785 -1.1445702314 -0.13987742364 0.19252476096 -1.1897368431 
-0.095426782966 0.13134369254 -1.2229086161 -0.33864095807 0.24603705108 -0.47652593255 -0.29760658741 0.21622383595 -0.41135922074 
-0.37043809891 0.26913902164 -0.54915231466 -0.39213064313 0.2848995924 -0.6272572279 -0.40312689543 0.29288882017 -0.70871031284 
-0.40312689543 0.29288882017 -0.79128968716 -0.39213064313 0.2848995924 -0.8727427721 -0.37043809891 0.26913902164 -0.95084768534 
-0.33864095807 0.24603705108 -1.0234740973 -0.29760658741 0.21622383595 -1.0886408091 -0.24845425785 0.1805125922 -1.1445702314 
-0.19252476096 0.13987742364 -1.1897368431 -0.13134369254 0.095426782966 -1.2229086161 -0.39809632301 0.12934933603 -0.47652593255 
-0.34985750914 0.11367559433 -0.41135922074 -0.43547609448 0.14149476588 -0.54915231466 -0.4609772265 0.14978057146 -0.6272572279 
-0.47390407324 0.15398077667 -0.70871031284 -0.47390407324 0.15398077667 -0.79128968716 -0.4609772265 0.14978057146 -0.8727427721 
-0.43547609448 0.14149476588 -0.95084768534 -0.39809632301 0.12934933603 -1.0234740973 -0.34985750914 0.11367559433 -1.0886408091 
-0.29207551479 0.0949010849 -1.1445702314 -0.2263264358 0.073537915945 -1.1897368431 -0.15440377593 0.050168827176 -1.2229086161 
-0.41858324409 5.1261663035e-17 -0.47652593255 -0.36786195636 4.5050095809e-17 -0.41135922074 -0.45788666606 5.6074944302e-17 -0.54915231466 
-0.48470014334 5.9358646193e-17 -0.6272572279 -0.49829223752 6.1023198283e-17 -0.70871031284 -0.49829223752 6.1023198283e-17 -0.79128968716 
-0.48470014334 5.9358646193e-17 -0.8727427721 -0.45788666606 5.6074944302e-17 -0.95084768534 -0.41858324409 5.1261663035e-17 -1.0234740973 
-0.36786195636 4.5050095809e-17 -1.0886408091 -0.30710634589 3.76096823e-17 -1.1445702314 -0.23797369003 2.9143372925e-17 -1.1897368431 
-0.16234973073 1.988210824e-17 -1.2229086161 -0.39809632301 -0.12934933603 -0.47652593255 -0.34985750914 -0.11367559433 -0.41135922074 
-0.43547609448 -0.14149476588 -0.54915231466 -0.4609772265 -0.14978057146 -0.6272572279 -0.47390407324 -0.15398077667 -0.70871031284 
-0.47390407324 -0.15398077667 -0.79128968716 -0.4609772265 -0.14978057146 -0.8727427721 -0.43547609448 -0.14149476588 -0.95084768534 
-0.39809632301 -0.12934933603 -1.0234740973 -0.34985750914 -0.11367559433 -1.0886408091 -0.29207551479 -0.0949010849 -1.1445702314 
-0.2263264358 -0.073537915945 -1.1897368431 -0.15440377593 -0.050168827176 -1.2229086161 -0.33864095807 -0.24603705108 -0.47652593255 
-0.29760658741 -0.21622383595 -0.41135922074 -0.37043809891 -0.26913902164 -0.54915231466 -0.39213064313 -0.2848995924 -0.6272572279 
-0.40312689543 -0.29288882017 -0.70871031284 -0.40312689543 -0.29288882017 -0.79128968716 -0.39213064313 -0.2848995924 -0.8727427721 
-0.37043809891 -0.26913902164 -0.95084768534 -0.33864095807 -0.24603705108 -1.0234740973 -0.29760658741 -0.21622383595 -1.0886408091 
-0.24845425785 -0.1805125922 -1.1445702314 -0.19252476096 -0.13987742364 -1.1897368431 -0.13134369254 -0.095426782966 -1.2229086161 
-0.24603705108 -0.33864095807 -0.47652593255 -0.21622383595 -0.29760658741 -0.41135922074 -0.26913902164 -0.37043809891 -0.54915231466 
-0.2848995924 -0.39213064313 -0.6272572279 -0.29288882017 -0.40312689543 -0.70871031284 -0.29288882017 -0.40312689543 -0.79128968716 
-0.2848995924 -0.39213064313 -0.8727427721 -0.26913902164 -0.37043809891 -0.95084768534 -0.24603705108 -0.33864095807 -1.0234740973 
-0.21622383595 -0.29760658741 -1.0886408091 -0.1805125922 -0.24845425785 -1.1445702314 -0.13987742364 -0.19252476096 -1.1897368431 
-0.095426782966 -0.13134369254 -1.2229086161 -0.12934933603 -0.39809632301 -0.47652593255 -0.11367559433 -0.34985750914 -0.41135922074 
-0.14149476588 -0.43547609448 -0.54915231466 -0.14978057146 -0.4609772265 -0.6272572279 -0.15398077667 -0.47390407324 -0.70871031284 
-0.15398077667 -0.47390407324 -0.79128968716 -0.14978057146 -0.4609772265 -0.8727427721 -0.14149476588 -0.43547609448 -0.95084768534 
-0.12934933603 -0.39809632301 -1.0234740973 -0.11367559433 -0.34985750914 -1.0886408091 -0.0949010849 -0.29207551479 -1.1445702314 
-0.073537915945 -0.2263264358 -1.1897368431 -0.050168827176 -0.15440377593 -1.2229086161 -7.6892492899e-17 -0.41858324409 -0.47652593255 
-6.7575143714e-17 -0.36786195636 -0.41135922074 -8.4112416453e-17 -0.45788666606 -0.54915231466 -8.9037972598e-17 -0.48470014334 -0.6272572279 
-9.1534800733e-17 -0.49829223752 -0.70871031284 -9.1534800733e-17 -0.49829223752 -0.79128968716 -8.9037972598e-17 -0.48470014334 -0.8727427721 
-8.4112416453e-17 -0.45788666606 -0.95084768534 -7.6892492899e-17 -0.41858324409 -1.0234740973 -6.7575143714e-17 -0.36786195636 -1.0886408091 
-5.6414525104e-17 -0.30710634589 -1.1445702314 -4.3715059388e-17 -0.23797369003 -1.1897368431 -2.9823163188e-17 -0.16234973073 -1.2229086161 
0.12934933603 -0.39809632301 -0.47652593255 0.11367559433 -0.34985750914 -0.41135922074 0.14149476588 -0.43547609448 -0.54915231466 
0.14978057146 -0.4609772265 -0.6272572279 0.15398077667 -0.47390407324 -0.70871031284 0.15398077667 -0.47390407324 -0.79128968716 
0.14978057146 -0.4609772265 -0.8727427721 0.14149476588 -0.43547609448 -0.95084768534 0.12934933603 -0.39809632301 -1.0234740973 
0.11367559433 -0.34985750914 -1.0886408091 0.0949010849 -0.29207551479 -1.1445702314 0.073537915945 -0.2263264358 -1.1897368431 
0.050168827176 -0.15440377593 -1.2229086161 0.24603705108 -0.33864095807 -0.47652593255 0.21622383595 -0.29760658741 -0.41135922074 
0.26913902164 -0.37043809891 -0.54915231466 0.2848995924 -0.39213064313 -0.6272572279 0.29288882017 -0.40312689543 -0.70871031284 
0.29288882017 -0.40312689543 -0.79128968716 0.2848995924 -0.39213064313 -0.8727427721 0.26913902164 -0.37043809891 -0.95084768534 
0.24603705108 -0.33864095807 -1.0234740973 0.21622383595 -0.29760658741 -1.0886408091 0.1805125922 -0.24845425785 -1.1445702314 
0.13987742364 -0.19252476096 -1.1897368431 0.095426782966 -0.13134369254 -1.2229086161 0.33864095807 -0.24603705108 -0.47652593255 
0.29760658741 -0.21622383595 -0.41135922074 0.37043809891 -0.26913902164 -0.54915231466 0.39213064313 -0.2848995924 -0.6272572279 
0.40312689543 -0.29288882017 -0.70871031284 0.40312689543 -0.29288882017 -0.79128968716 0.39213064313 -0.2848995924 -0.8727427721 
0.37043809891 -0.26913902164 -0.95084768534 0.33864095807 -0.24603705108 -1.0234740973 0.29760658741 -0.21622383595 -1.0886408091 
0.24845425785 -0.1805125922 -1.1445702314 0.19252476096 -0.13987742364 -1.1897368431 0.13134369254 -0.095426782966 -1.2229086161 
0.39809632301 -0.12934933603 -0.47652593255 0.34985750914 -0.11367559433 -0.41135922074 0.43547609448 -0.14149476588 -0.54915231466 
0.4609772265 -0.14978057146 -0.6272572279 0.47390407324 -0.15398077667 -0.70871031284 0.47390407324 -0.15398077667 -0.79128968716 
0.4609772265 -0.14978057146 -0.8727427721 0.43547609448 -0.14149476588 -0.95084768534 0.39809632301 -0.12934933603 -1.0234740973 
0.34985750914 -0.11367559433 -1.0886408091 0.29207551479 -0.0949010849 -1.1445702314 0.2263264358 -0.073537915945 -1.1897368431 
0.15440377593 -0.050168827176 -1.2229086161 0.32206539101 0.039776188896 0.37499999505 0.3283653157 6.2943287406e-18 0.375 
0.31229398784 0.10147046551 0.375 0.3185939096 0.061694295065 0.37499999505 0.29401088955 0.13735306593 0.37500000514 
0.26565312857 0.1930082983 0.375 0.28393623568 0.15712568057 0.37500000514 0.23717652463 0.22148490223 0.375 
0.1930082983 0.26565312857 0.375 0.22148490223 0.23717652463 0.375 0.15712568057 0.28393623568 0.37499999486 
0.10147046551 0.31229398784 0.375 0.13735306593 0.29401088955 0.37499999486 0.061694295065 0.3185939096 0.37500000495 
1.779775782e-17 0.3283653157 0.375 0.039776188896 0.32206539101 0.37500000495 -0.039776188896 0.32206539101 0.37499999505 
-0.10147046551 0.31229398784 0.375 -0.061694295065 0.3185939096 0.37499999505 -0.13735306593 0.29401088955 0.37500000514 
-0.1930082983 0.26565312857 0.375 -0.15712568057 0.28393623568 0.37500000514 -0.22148490223 0.23717652463 0.375 
-0.26565312857 0.1930082983 0.375 -0.23717652463 0.22148490223 0.375 -0.28393623568 0.15712568057 0.37499999486 
-0.31229398784 0.10147046551 0.375 -0.29401088955 0.13735306593 0.37499999486 -0.3185939096 0.061694295065 0.37500000495 
-0.3283653157 3.7802275371e-17 0.375 -0.32206539101 0.039776188896 0.37500000495 -0.32206539101 -0.039776188896 0.37499999505 
-0.31229398784 -0.10147046551 0.375 -0.3185939096 -0.061694295065 0.37499999505 -0.29401088955 -0.13735306593 0.37500000514 
-0.26565312857 -0.1930082983 0.375 -0.28393623568 -0.15712568057 0.37500000514 -0.23717652463 -0.22148490223 0.375 
-0.1930082983 -0.26565312857 0.375 -0.22148490223 -0.23717652463 0.375 -0.15712568057 -0.28393623568 0.37499999486 
-0.10147046551 -0.31229398784 0.375 -0.13735306593 -0.29401088955 0.37499999486 -0.061694295065 -0.3185939096 0.37500000495 
-5.7734190001e-17 -0.3283653157 0.375 -0.039776188896 -0.32206539101 0.37500000495 0.039776188896 -0.32206539101 0.37499999505 
0.10147046551 -0.31229398784 0.375 0.061694295065 -0.3185939096 0.37499999505 0.13735306593 -0.29401088955 0.37500000514 
0.1930082983 -0.26565312857 0.375 0.15712568057 -0.28393623568 0.37500000514 0.22148490223 -0.23717652463 0.375 
0.26565312857 -0.1930082983 0.375 0.23717652463 -0.22148490223 0.375 0.28393623568 -0.15712568057 0.37499999486 
0.31229398784 -0.10147046551 0.375 0.29401088955 -0.13735306593 0.37499999486 0.3185939096 -0.061694295065 0.37500000495 
0.32206539101 -0.039776188896 0.37500000495 0.082297295332 0 1.2431806326 0.078269377351 0.025431262329 1.2431806326 
0 0 1.25 0.066579908133 0.048373136669 1.2431806326 0.048373136669 0.066579908133 1.2431806326 
0.025431262329 0.078269377351 1.2431806326 5.0392558428e-18 0.082297295332 1.2431806326 -0.025431262329 0.078269377351 1.2431806326 
-0.048373136669 0.066579908133 1.2431806326 -0.066579908133 0.048373136669 1.2431806326 -0.078269377351 0.025431262329 1.2431806326 
-0.082297295332 1.0078511686e-17 1.2431806326 -0.078269377351 -0.025431262329 1.2431806326 -0.066579908133 -0.048373136669 1.2431806326 
-0.048373136669 -0.066579908133 1.2431806326 -0.025431262329 -0.078269377351 1.2431806326 -1.5117767942e-17 -0.082297295332 1.2431806326 
0.025431262329 -0.078269377351 1.2431806326 0.048373136669 -0.066579908133 1.2431806326 0.066579908133 -0.048373136669 1.2431806326 
0.078269377351 -0.025431262329 1.2431806326 0.16234973073 0 1.2229086161 0.15440377593 0.050168827176 1.2229086161 
0.23797369003 0 1.1897368431 0.2263264358 0.073537915945 1.1897368431 0.30710634589 0 1.1445702314 
0.29207551479 0.0949010849 1.1445702314 0.36786195636 0 1.0886408091 0.34985750914 0.11367559433 1.0886408091 
0.41858324409 0 1.0234740973 0.39809632301 0.12934933603 1.0234740973 0.45788666606 0 0.95084768534 
0.43547609448 0.14149476588 0.95084768534 0.48470014334 0 0.8727427721 0.4609772265 0.14978057146 0.8727427721 
0.49829223752 0 0.79128968716 0.47390407324 0.15398077667 0.79128968716 0.49829223752 0 0.70871031284 
0.47390407324 0.15398077667 0.70871031284 0.48470014334 0 0.6272572279 0.4609772265 0.14978057146 0.6272572279 
0.45788666606 0 0.54915231466 0.43547609448 0.14149476588 0.54915231466 0.41858324409 0 0.47652593255 
0.39809632301 0.12934933603 0.47652593255 0.36786195636 0 0.41135922074 0.34985750914 0.11367559433 0.41135922074 
0.13134369254 0.0954
Download .txt
gitextract_hxnqu4v5/

├── .github/
│   ├── FUNDING.yml
│   └── workflows/
│       └── cmake.yml
├── CITATION.cff
├── CMakeLists.txt
├── Contact.cxx
├── Contact.h
├── LICENSE
├── Merger.cxx
├── Merger.h
├── Optimize.cxx
├── Optimize.h
├── README.md
├── Utilities.cxx
├── Utilities.h
├── module/
│   ├── CMakeLists.txt
│   └── vtk.module
├── paraview/
│   ├── CMakeLists.txt
│   ├── module/
│   │   ├── CMakeLists.txt
│   │   ├── vtk.module
│   │   └── vtkPolyDataBooleanFilter.xml
│   └── paraview.plugin
├── run_some_tests.sh
├── testing/
│   ├── data/
│   │   ├── branched.vtk
│   │   ├── branched3.vtk
│   │   ├── branched4.vtk
│   │   ├── branched6.vtk
│   │   ├── cross.vtk
│   │   ├── merger.vtk
│   │   └── non-manifold.vtk
│   ├── generate_frieze.py
│   ├── pytest.ini
│   ├── test_congruence.cxx
│   ├── test_filter.py
│   ├── test_merger.cxx
│   └── test_python.py
├── vtkPolyDataBooleanFilter.cxx
└── vtkPolyDataBooleanFilter.h
Download .txt
SYMBOL INDEX (135 symbols across 13 files)

FILE: Contact.cxx
  function Clean (line 33) | vtkSmartPointer<vtkPolyData> Clean (vtkPolyData *pd) {
  type Cmp (line 383) | struct Cmp {

FILE: Contact.h
  type class (line 26) | enum class
  type class (line 31) | enum class
  function PointSrc (line 37) | enum class PointSrc {
  type std (line 103) | typedef std::vector<InterPt> InterPtsType;
  type std (line 104) | typedef std::vector<std::tuple<InterPt, InterPt>> OverlapsType;
  type std (line 106) | typedef std::set<Pair> NonManifoldEdgesType;
  type std (line 108) | typedef std::vector<std::pair<vtkIdType, vtkIdType>> PairsType;
  function class (line 112) | class Contact {

FILE: Merger.h
  type std (line 22) | typedef std::vector<std::size_t> GroupType;
  type std (line 24) | typedef std::map<vtkIdType, std::size_t> SourcesType;
  function class (line 26) | class Conn {
  type ConnCmp (line 48) | struct ConnCmp {
  type std (line 54) | typedef std::vector<Conn> ConnsType;
  type std (line 55) | typedef std::map<std::size_t, ConnsType> PolyConnsType;
  type std (line 57) | typedef std::set<Conn, ConnCmp> ConnsType2;
  type Cmp (line 90) | struct Cmp {
  type std (line 98) | typedef std::set<Prio, Cmp> PriosType;
  type std (line 100) | typedef std::map<std::size_t, Prio> PolyPriosType;
  function class (line 102) | class Merger {

FILE: Optimize.cxx
  type Cmp (line 262) | struct Cmp {
  function IdsType (line 378) | IdsType PreventEqualCaptPoints::TriangulateCell (vtkPolyData *pd, vtkIdT...

FILE: Optimize.h
  type std (line 22) | typedef std::map<Pair, Points> Edges;
  function class (line 24) | class SnapPoint {
  function class (line 46) | class SnapEdge {
  function class (line 70) | class PreventEqualCaptPoints {

FILE: Utilities.cxx
  function ComputeNormal (line 30) | double ComputeNormal (vtkPoints *pts, double *n, vtkIdType num, const vt...
  function FindPoints (line 52) | void FindPoints (vtkKdTreePointLocator *pl, const double *pt, vtkIdList ...
  function WriteVTK (line 78) | void WriteVTK (const char *name, vtkPolyData *pd) {
  function GetAngle (line 89) | double GetAngle (const double *vA, const double *vB, const double *n) {
  function Transform (line 128) | void Transform (const double *in, double *out, const Base &base) {
  function ComputeNormal (line 146) | double ComputeNormal (const Poly &poly, double *n) {
  function PointInPoly (line 169) | bool PointInPoly (const Poly &poly, const Point3d &p) {
  function WritePolys (line 199) | void WritePolys (const char *name, const PolysType &polys) {
  function GetPolys (line 221) | void GetPolys (const ReferencedPointsType &pts, const IndexedPolysType &...
  function GetPoly (line 233) | void GetPoly (vtkPoints *pts, vtkIdType num, const vtkIdType *poly, Poly...
  function FlattenPoly (line 245) | void FlattenPoly (const Poly &poly, Poly &out, const Base &base) {
  function FlattenPoly2 (line 261) | void FlattenPoly2 (const Poly &poly, Poly &out, const Base2 &base) {
  function GetEdgeProj (line 275) | std::shared_ptr<Proj> GetEdgeProj (const Poly &poly, const Point3d &p) {
  function ProjOnLine (line 300) | void ProjOnLine (const Point3d &a, const Point3d &b, const Point3d &p, d...
  function ProjOnLine (line 317) | void ProjOnLine (vtkPolyData *pd, const Pair &line, const Point3d &p, st...
  function CreatePolyData (line 331) | vtkSmartPointer<vtkPolyData> CreatePolyData (const PolysType &polys) {
  function GetTriangleQuality (line 354) | double GetTriangleQuality (const Poly &poly) {

FILE: Utilities.h
  function class (line 48) | class Point3d {
  function operator (line 65) | bool operator== (const Point3d &other) const {
  function GetVec (line 78) | static double GetVec (const Point3d &a, const Point3d &b, double *v) {
  function GetDist (line 86) | static double GetDist (const Point3d &a, const Point3d &b) {
  type std (line 96) | typedef std::vector<vtkIdType> IdsType;
  type std (line 97) | typedef std::set<vtkIdType> _IdsType;
  function class (line 99) | class Pair {
  function operator (line 107) | bool operator== (const Pair &other) const {
  function vtkIdType (line 110) | vtkIdType operator& (const Pair &other) const {
  function class (line 122) | class Base {
  function class (line 132) | class Base2 {
  type std (line 184) | typedef std::vector<Point3d> Poly, Points;
  type std (line 185) | typedef std::vector<Poly> PolysType;
  type std (line 194) | typedef std::deque<vtkIdType> IndexedPoly;
  type std (line 195) | typedef std::vector<IndexedPoly> IndexedPolysType;
  type std (line 197) | typedef std::map<vtkIdType, std::reference_wrapper<const Point3d>> Refer...
  function class (line 205) | class Proj {

FILE: testing/generate_frieze.py
  function extrude (line 42) | def extrude(pts, h, z=0):
  class Frieze (line 69) | class Frieze:
    method __init__ (line 70) | def __init__ (self, usr_cfg):
    method draw_bricks (line 88) | def draw_bricks(self, seq, end_seq):
    method draw_zz_bricks (line 176) | def draw_zz_bricks(self):
    method draw_spacer (line 207) | def draw_spacer(self):
    method export (line 220) | def export(self, name):

FILE: testing/test_congruence.cxx
  function main (line 19) | int main() {

FILE: testing/test_filter.py
  function check_result (line 42) | def check_result(bf, expected_regs=None):
  function write_result (line 258) | def write_result(bf, d):
  function extrude_polygon (line 265) | def extrude_polygon(poly, z):
  function create_polydata (line 289) | def create_polydata(pts, polys):
  function test_simple (line 311) | def test_simple(tmp_path):
  function test_simple_2 (line 330) | def test_simple_2(tmp_path):
  function test_simple_3 (line 349) | def test_simple_3(tmp_path):
  function test_simple_4 (line 367) | def test_simple_4(tmp_path):
  function test_same (line 419) | def test_same(tmp_path):
  function test_intersecting_strips (line 433) | def test_intersecting_strips():
  function test_intersecting_strips_2 (line 470) | def test_intersecting_strips_2():
  function test_touch (line 495) | def test_touch(tmp_path):
  function test_merger (line 513) | def test_merger(tmp_path):
  function test_merger_2 (line 529) | def test_merger_2(tmp_path):
  function test_quads (line 545) | def test_quads(tmp_path):
  function test_triangle_strips (line 564) | def test_triangle_strips(tmp_path):
  function test_special (line 598) | def test_special(tmp_path):
  function test_non_manifolds (line 632) | def test_non_manifolds():
  function test_branched (line 650) | def test_branched(tmp_path):
  function test_branched_2 (line 666) | def test_branched_2(tmp_path):
  function test_branched_3 (line 683) | def test_branched_3():
  function test_branched_4 (line 698) | def test_branched_4(tmp_path):
  function test_branched_5 (line 714) | def test_branched_5(tmp_path):
  function test_branched_6 (line 748) | def test_branched_6(tmp_path):
  function test_bad_shaped (line 764) | def test_bad_shaped(tmp_path):
  function test_self_intersecting_polys (line 805) | def test_self_intersecting_polys():
  function test_equal_capt_pts (line 854) | def test_equal_capt_pts(tmp_path):
  function test_equal_capt_pts_2 (line 879) | def test_equal_capt_pts_2(tmp_path):
  function test_equal_capt_pts_3 (line 902) | def test_equal_capt_pts_3(tmp_path):
  function test_transform_matrix (line 944) | def test_transform_matrix(tmp_path):

FILE: testing/test_merger.cxx
  function Poly (line 26) | Poly CreateRegularPoly (double r, vtkIdType step, double x, double y, do...
  function Test (line 52) | bool Test (vtkPolyData *pd, PolysType &polys, vtkIdType numCells, [[mayb...
  function main (line 147) | int main() {

FILE: vtkPolyDataBooleanFilter.cxx
  function ComputeNormal (line 1023) | void ComputeNormal (const StripPtsType &pts, const _RefsType &poly, doub...
  function CleanPoly (line 1052) | void CleanPoly (vtkPolyData *pd, IdsType &poly) {
  type Cmp (line 1912) | struct Cmp {
  type Cmp (line 2129) | struct Cmp {
  type Congr (line 2485) | enum class Congr {
  class PolyAtEdge (line 2491) | class PolyAtEdge {
    method PolyAtEdge (line 2495) | PolyAtEdge (vtkPolyData *_pd, vtkIdType _polyId, vtkIdType _ptIdA, vtk...
    method Congr (line 2528) | Congr IsCongruent (const PolyAtEdge &p) const {
  class PolyPair (line 2550) | class PolyPair {
    method PolyPair (line 2552) | PolyPair (PolyAtEdge _pA, PolyAtEdge _pB) : pA(_pA), pB(_pB) {}
    method GetLoc (line 2556) | void GetLoc (PolyAtEdge &pT, vtkIdType mode) {
  function GetEdgePolys (line 2620) | std::shared_ptr<PolyPair> GetEdgePolys (vtkPolyData *pd, vtkIdList *ptsA...
  function vtkMatrix4x4 (line 3127) | vtkMatrix4x4* vtkPolyDataBooleanFilter::GetMatrix (int i) {
  function vtkMTimeType (line 3135) | vtkMTimeType vtkPolyDataBooleanFilter::GetMTime () {

FILE: vtkPolyDataBooleanFilter.h
  type OperMode (line 38) | enum OperMode {
  type class (line 46) | enum class
  function std (line 60) | inline std::underlying_type_t<Capt> operator& (Capt lhs, Capt rhs) {
  type class (line 65) | enum class
  function Loc (line 71) | enum class Loc {
  type std (line 131) | typedef std::map<vtkIdType, StripPt> StripPtsType;
  type std (line 132) | typedef std::deque<StripPtR> StripType;
  type std (line 133) | typedef std::vector<StripType> StripsType;
  type std (line 135) | typedef std::vector<std::reference_wrapper<StripType>> _StripsType;
  function class (line 137) | class PStrips {
  type std (line 162) | typedef std::map<vtkIdType, PStrips> PolyStripsType;
  type std (line 164) | typedef std::vector<std::reference_wrapper<StripPtR>> RefsType;
  type std (line 165) | typedef std::vector<std::reference_wrapper<const StripPtR>> ConstRefsType;
  function SetOperModeToNone (line 213) | void SetOperModeToNone () { OperMode = OPER_NONE; Modified(); }
  function SetOperModeToUnion (line 214) | void SetOperModeToUnion () { OperMode = OPER_UNION; Modified(); }
  function SetOperModeToIntersection (line 215) | void SetOperModeToIntersection () { OperMode = OPER_INTERSECTION; Modifi...
  function SetOperModeToDifference (line 216) | void SetOperModeToDifference () { OperMode = OPER_DIFFERENCE; Modified(); }
  function SetOperModeToDifference2 (line 217) | void SetOperModeToDifference2 () { OperMode = OPER_DIFFERENCE2; Modified...
  function PrintSelf (line 230) | void PrintSelf (ostream&, vtkIndent) override {}
Condensed preview — 37 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (602K chars).
[
  {
    "path": ".github/FUNDING.yml",
    "chars": 15,
    "preview": "ko_fi: zippy84\n"
  },
  {
    "path": ".github/workflows/cmake.yml",
    "chars": 1849,
    "preview": "name: CMake\n\non:\n  push:\n    branches:\n      - '*'\n\njobs:\n  Build:\n    runs-on: ${{matrix.os}}\n    strategy:\n      matri"
  },
  {
    "path": "CITATION.cff",
    "chars": 319,
    "preview": "cff-version: 1.2.0\ntitle: vtkbool\nmessage: 'If you use this software, please cite it as below.'\ntype: software\nauthors:\n"
  },
  {
    "path": "CMakeLists.txt",
    "chars": 4624,
    "preview": "# Copyright 2012-2025 Ronald Römer\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use"
  },
  {
    "path": "Contact.cxx",
    "chars": 28690,
    "preview": "/*\nCopyright 2012-2025 Ronald Römer\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use thi"
  },
  {
    "path": "Contact.h",
    "chars": 3932,
    "preview": "/*\nCopyright 2012-2025 Ronald Römer\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use thi"
  },
  {
    "path": "LICENSE",
    "chars": 10175,
    "preview": "\n                                 Apache License\n                           Version 2.0, January 2004\n                  "
  },
  {
    "path": "Merger.cxx",
    "chars": 24424,
    "preview": "/*\nCopyright 2012-2025 Ronald Römer\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use thi"
  },
  {
    "path": "Merger.h",
    "chars": 3501,
    "preview": "/*\nCopyright 2012-2025 Ronald Römer\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use thi"
  },
  {
    "path": "Optimize.cxx",
    "chars": 13854,
    "preview": "/*\nCopyright 2012-2025 Ronald Römer\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use thi"
  },
  {
    "path": "Optimize.h",
    "chars": 2399,
    "preview": "/*\nCopyright 2012-2025 Ronald Römer\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use thi"
  },
  {
    "path": "README.md",
    "chars": 9538,
    "preview": "# vtkbool [![CMake](https://github.com/zippy84/vtkbool/actions/workflows/cmake.yml/badge.svg)](https://github.com/zippy8"
  },
  {
    "path": "Utilities.cxx",
    "chars": 8851,
    "preview": "/*\nCopyright 2012-2025 Ronald Römer\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use thi"
  },
  {
    "path": "Utilities.h",
    "chars": 6617,
    "preview": "/*\nCopyright 2012-2025 Ronald Römer\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use thi"
  },
  {
    "path": "module/CMakeLists.txt",
    "chars": 250,
    "preview": "set(srcs\n    ../Utilities.cxx\n    ../Optimize.cxx\n    ../Contact.cxx\n    ../Merger.cxx\n    ../vtkPolyDataBooleanFilter.c"
  },
  {
    "path": "module/vtk.module",
    "chars": 243,
    "preview": "NAME\n  vtkbool\nDEPENDS\n  VTK::CommonCore\n  VTK::CommonExecutionModel\n  VTK::FiltersPoints\n  VTK::IOLegacy\n  VTK::Filters"
  },
  {
    "path": "paraview/CMakeLists.txt",
    "chars": 155,
    "preview": "paraview_add_plugin(PolyDataBooleanFilter\n  VERSION \"${vtkbool_VERSION}\"\n  MODULES vtkbool\n  MODULE_FILES \"${CMAKE_CURRE"
  },
  {
    "path": "paraview/module/CMakeLists.txt",
    "chars": 360,
    "preview": "set(srcs\n    ../../Utilities.cxx\n    ../../Optimize.cxx\n    ../../Contact.cxx\n    ../../Merger.cxx\n    ../../vtkPolyData"
  },
  {
    "path": "paraview/module/vtk.module",
    "chars": 165,
    "preview": "NAME\n  vtkbool\nDEPENDS\n  VTK::CommonCore\n  VTK::CommonExecutionModel\n  VTK::FiltersPoints\n  VTK::IOLegacy\n  VTK::Filters"
  },
  {
    "path": "paraview/module/vtkPolyDataBooleanFilter.xml",
    "chars": 2071,
    "preview": "<ServerManagerConfiguration>\n    <ProxyGroup name=\"filters\">\n        <SourceProxy name=\"PolyDataBooleanFilter\" class=\"vt"
  },
  {
    "path": "paraview/paraview.plugin",
    "chars": 161,
    "preview": "NAME\n  PolyDataBooleanFilter\nREQUIRES_MODULES\n  VTK::CommonCore\n  VTK::FiltersCore\nDESCRIPTION\n  This module contains a "
  },
  {
    "path": "run_some_tests.sh",
    "chars": 450,
    "preview": "#!/bin/bash\nexport PYTHONPATH=/home/zippy/vtkbool/build/lib/python3.13/site-packages/vtkbool:$PYTHONPATH\npushd testing\n\n"
  },
  {
    "path": "testing/data/branched.vtk",
    "chars": 46722,
    "preview": "# vtk DataFile Version 5.1\nvtk output\nASCII\nDATASET POLYDATA\nPOINTS 622 double\n0.082297295332 0 0.86818063259 0.07826937"
  },
  {
    "path": "testing/data/branched3.vtk",
    "chars": 65344,
    "preview": "# vtk DataFile Version 5.1\nvtk output\nASCII\nDATASET POLYDATA\nPOINTS 882 double\n0.36786195636 0 0.33864077926 0.418583244"
  },
  {
    "path": "testing/data/branched4.vtk",
    "chars": 105616,
    "preview": "# vtk DataFile Version 5.1\nvtk output\nASCII\nDATASET POLYDATA\nPOINTS 1346 double\n0.082297295332 0 -0.86818063259 0 0 -0.8"
  },
  {
    "path": "testing/data/branched6.vtk",
    "chars": 92359,
    "preview": "# vtk DataFile Version 5.1\nvtk output\nASCII\nDATASET POLYDATA\nPOINTS 1232 double\n0.082297295332 -0.36765038967 0.86818063"
  },
  {
    "path": "testing/data/cross.vtk",
    "chars": 2748,
    "preview": "# vtk DataFile Version 5.1\nvtk output\nASCII\nDATASET POLYDATA\nPOINTS 70 double\n0.5 1 0 0.43301269412 1 -0.25 0.25 1 -0.43"
  },
  {
    "path": "testing/data/merger.vtk",
    "chars": 726,
    "preview": "# vtk DataFile Version 5.1\nvtk output\nASCII\nDATASET POLYDATA\nPOINTS 24 double\n-0.4 0 0.5 -0.4 0 -0.5 0 -0.4 0.5 \n0 -0.4 "
  },
  {
    "path": "testing/data/non-manifold.vtk",
    "chars": 2346,
    "preview": "# vtk DataFile Version 5.1\nvtk output\nASCII\nDATASET POLYDATA\nPOINTS 58 double\n-1 -0.5 -1 -1 0.5 -1 1 0.5 -1 \n1 -0.5 -1 -"
  },
  {
    "path": "testing/generate_frieze.py",
    "chars": 14106,
    "preview": "#!/usr/bin/env python\n# *-* coding: UTF-8 *-*\n\n# Copyright 2012-2025 Ronald Römer\n#\n# Licensed under the Apache License,"
  },
  {
    "path": "testing/pytest.ini",
    "chars": 69,
    "preview": "[pytest]\nlog_cli = True\naddopts = --basetemp=out\nxfail_strict = True\n"
  },
  {
    "path": "testing/test_congruence.cxx",
    "chars": 1153,
    "preview": "/*\nCopyright 2012-2025 Ronald Römer\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use thi"
  },
  {
    "path": "testing/test_filter.py",
    "chars": 25421,
    "preview": "#!/usr/bin/env python\n# *-* coding: UTF-8 *-*\n\n# Copyright 2012-2025 Ronald Römer\n#\n# Licensed under the Apache License,"
  },
  {
    "path": "testing/test_merger.cxx",
    "chars": 4775,
    "preview": "/*\nCopyright 2012-2025 Ronald Römer\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use thi"
  },
  {
    "path": "testing/test_python.py",
    "chars": 1068,
    "preview": "#!/usr/bin/env python\n# *-* coding: UTF-8 *-*\n\n# Copyright 2012-2025 Ronald Römer\n#\n# Licensed under the Apache License,"
  },
  {
    "path": "vtkPolyDataBooleanFilter.cxx",
    "chars": 93495,
    "preview": "/*\nCopyright 2012-2025 Ronald Römer\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use thi"
  },
  {
    "path": "vtkPolyDataBooleanFilter.h",
    "chars": 6480,
    "preview": "/*\nCopyright 2012-2025 Ronald Römer\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use thi"
  }
]

About this extraction

This page contains the full source code of the zippy84/vtkbool GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 37 files (571.4 KB), approximately 234.5k tokens, and a symbol index with 135 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!