├── .gitignore ├── LICENSE ├── README.md ├── data └── points-1k.json ├── demo.png └── src ├── main ├── cpp │ ├── concaveman.cpp │ └── concaveman.h └── python │ ├── concaveman.py │ └── demo.py └── test └── cpp └── test_concaveman.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | # Python 35 | __pycache__/ 36 | *.pyc 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2019, sadaszewski 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # concaveman-cpp 2 | C++ port of mapbox's JS [concaveman](https://github.com/mapbox/concaveman), with a Python wrapper 3 | 4 | Please take a look at my [blog](https://adared.ch/concaveman-cpp-a-very-fast-2d-concave-hull-maybe-even-faster-with-c-and-python/) for a longer introduction :) 5 | 6 | ![Demo Screenshot](demo.png) 7 | -------------------------------------------------------------------------------- /data/points-1k.json: -------------------------------------------------------------------------------- 1 | [[-122.06851,37.394206],[-122.06849,37.393812],[-122.06848,37.393392],[-122.06849,37.393059],[-122.06851,37.392685],[-122.06855,37.392237],[-122.06868,37.390828],[-122.06882,37.389118],[-122.06885,37.388747],[-122.06885,37.388346],[-122.06884,37.387894],[-122.06881,37.387364],[-122.0688,37.385968],[-122.06881,37.38581],[-122.06886,37.385644],[-122.06892,37.38552],[-122.06901,37.385392],[-122.06927,37.38509],[-122.06941,37.384941],[-122.06953,37.3848],[-122.06845,37.384527],[-122.06962,37.384659],[-122.06967,37.384519],[-122.06984,37.383871],[-122.06838,37.383681],[-122.06991,37.383713],[-122.07004,37.383547],[-122.07018,37.383415],[-122.07048,37.383211],[-122.07094,37.382826],[-122.07112,37.382676],[-122.07126,37.382534],[-122.07148,37.382317],[-122.07168,37.382082],[-122.07207,37.381618],[-122.07189,37.381524],[-122.07134,37.382215],[-122.07285,37.380693],[-122.07114,37.382419],[-122.07302,37.380475],[-122.07063,37.38086],[-122.07313,37.380351],[-122.07089,37.382632],[-122.07304,37.380309],[-122.07296,37.38027],[-122.07036,37.381122],[-122.07064,37.382803],[-122.07331,37.380096],[-122.07284,37.3804],[-122.07292,37.380435],[-122.07268,37.38015],[-122.07349,37.380178],[-122.07112,37.380373],[-122.07042,37.382931],[-122.07345,37.379885],[-122.07352,37.380324],[-122.07242,37.380042],[-122.07286,37.37994],[-122.07364,37.379969],[-122.0725,37.379929],[-122.0678,37.378525],[-122.07372,37.380403],[-122.0734,37.380484],[-122.07009,37.383117],[-122.07258,37.379928],[-122.07267,37.379855],[-122.07373,37.379837],[-122.07367,37.379587],[-122.0737,37.380497],[-122.07145,37.380051],[-122.07312,37.380047],[-122.07246,37.379796],[-122.07301,37.379727],[-122.07363,37.380598],[-122.07386,37.380225],[-122.0731,37.379774],[-122.07202,37.379869],[-122.0716,37.380105],[-122.07168,37.380692],[-122.07392,37.379709],[-122.07228,37.37972],[-122.07389,37.379696],[-122.07283,37.379645],[-122.07381,37.37942],[-122.06777,37.378219],[-122.07323,37.379729],[-122.07171,37.380098],[-122.07385,37.379649],[-122.07164,37.379862],[-122.06976,37.383287],[-122.07441,37.381131],[-122.07403,37.379766],[-122.07331,37.379771],[-122.0738,37.379574],[-122.0721,37.379647],[-122.07397,37.379845],[-122.0719,37.380184],[-122.06936,37.381891],[-122.07428,37.381305],[-122.07276,37.379417],[-122.07162,37.379699],[-122.07191,37.379567],[-122.07196,37.380377],[-122.07384,37.380011],[-122.07206,37.380278],[-122.07475,37.381288],[-122.072,37.380332],[-122.06942,37.383489],[-122.0738,37.380026],[-122.07134,37.381081],[-122.07258,37.379338],[-122.07447,37.381401],[-122.07377,37.38007],[-122.07295,37.379501],[-122.07408,37.381206],[-122.07176,37.379504],[-122.07446,37.379979],[-122.07415,37.379012],[-122.07251,37.37931],[-122.07379,37.380118],[-122.06787,37.377728],[-122.07299,37.379552],[-122.06772,37.377703],[-122.06921,37.383496],[-122.07384,37.380143],[-122.07453,37.380011],[-122.07309,37.379472],[-122.07223,37.380125],[-122.0739,37.380173],[-122.07239,37.37925],[-122.069,37.382165],[-122.07156,37.379416],[-122.06797,37.377592],[-122.074,37.380216],[-122.07436,37.37912],[-122.07321,37.379644],[-122.06902,37.383572],[-122.07232,37.380532],[-122.07401,37.38026],[-122.0733,37.379427],[-122.07466,37.379845],[-122.06904,37.383673],[-122.07427,37.380224],[-122.07232,37.380589],[-122.07171,37.381265],[-122.07335,37.379417],[-122.07426,37.380232],[-122.07221,37.379163],[-122.07398,37.380306],[-122.0748,37.380141],[-122.07137,37.379336],[-122.06812,37.377494],[-122.07427,37.379251],[-122.06773,37.377362],[-122.07447,37.37897],[-122.0742,37.380323],[-122.06876,37.382346],[-122.07474,37.380219],[-122.07391,37.380395],[-122.07094,37.379408],[-122.07206,37.379092],[-122.07352,37.379484],[-122.07478,37.379683],[-122.06878,37.383679],[-122.07389,37.380437],[-122.07434,37.381768],[-122.07537,37.381605],[-122.07335,37.379131],[-122.06831,37.377451],[-122.06881,37.383805],[-122.07418,37.379373],[-122.07418,37.380434],[-122.07388,37.380493],[-122.07456,37.378849],[-122.07347,37.379187],[-122.06858,37.382404],[-122.07367,37.379485],[-122.07508,37.380278],[-122.07395,37.380536],[-122.07488,37.379555],[-122.07187,37.379],[-122.07419,37.38054],[-122.06849,37.377464],[-122.07466,37.378714],[-122.06851,37.383802],[-122.07412,37.38062],[-122.07498,37.380416],[-122.07527,37.380371],[-122.07498,37.379426],[-122.07056,37.379243],[-122.06777,37.376982],[-122.07169,37.378911],[-122.06866,37.377528],[-122.07448,37.380555],[-122.07491,37.380465],[-122.07533,37.380404],[-122.07499,37.380502],[-122.07487,37.380445],[-122.07471,37.378329],[-122.07504,37.380523],[-122.07482,37.380423],[-122.07532,37.380143],[-122.07508,37.380542],[-122.074,37.380778],[-122.07477,37.378258],[-122.07387,37.381526],[-122.06877,37.377622],[-122.0751,37.379278],[-122.07152,37.378834],[-122.0707,37.379084],[-122.07149,37.378825],[-122.07481,37.380614],[-122.07489,37.380653],[-122.07072,37.379048],[-122.07476,37.380594],[-122.07483,37.378182],[-122.07547,37.380228],[-122.07142,37.37883],[-122.07493,37.380674],[-122.07074,37.379028],[-122.07141,37.378794],[-122.07472,37.38057],[-122.06883,37.377749],[-122.07138,37.378859],[-122.07519,37.379164],[-122.07552,37.380187],[-122.07494,37.380738],[-122.07497,37.378339],[-122.07522,37.379127],[-122.07139,37.379004],[-122.07568,37.380584],[-122.07487,37.380767],[-122.07128,37.378729],[-122.07468,37.378094],[-122.07088,37.378939],[-122.07504,37.380784],[-122.06885,37.377886],[-122.07612,37.381995],[-122.07529,37.379037],[-122.07097,37.379156],[-122.07118,37.378683],[-122.07461,37.378179],[-122.07378,37.38243],[-122.07516,37.378429],[-122.07572,37.380061],[-122.07114,37.378659],[-122.06789,37.384126],[-122.06995,37.378991],[-122.07457,37.378225],[-122.0711,37.378668],[-122.07477,37.38092],[-122.07524,37.378423],[-122.0688,37.378018],[-122.07583,37.380726],[-122.07581,37.380029],[-122.07507,37.377893],[-122.07107,37.3787],[-122.07539,37.378906],[-122.0752,37.378368],[-122.07469,37.380882],[-122.07489,37.380975],[-122.0759,37.38001],[-122.07516,37.377938],[-122.07113,37.379188],[-122.0677,37.384207],[-122.07598,37.380809],[-122.06869,37.378142],[-122.07107,37.379197],[-122.07483,37.381051],[-122.07511,37.378581],[-122.07521,37.377869],[-122.07507,37.3783],[-122.07536,37.378262],[-122.0755,37.378769],[-122.07604,37.380051],[-122.07568,37.380917],[-122.0741,37.382588],[-122.07346,37.382272],[-122.0753,37.378011],[-122.07587,37.382323],[-122.07528,37.377834],[-122.06754,37.384224],[-122.07422,37.377859],[-122.07457,37.381054],[-122.07476,37.381144],[-122.07555,37.378708],[-122.06961,37.378851],[-122.07614,37.3801],[-122.06852,37.378223],[-122.07537,37.377848],[-122.07501,37.378711],[-122.0749,37.378215],[-122.07563,37.378608],[-122.07436,37.38073],[-122.07583,37.381001],[-122.07548,37.378102],[-122.07328,37.382181],[-122.07568,37.378579],[-122.06734,37.38419],[-122.07489,37.378651],[-122.06835,37.378252],[-122.07547,37.37772],[-122.07426,37.382364],[-122.07559,37.377962],[-122.06933,37.378736],[-122.07577,37.378467],[-122.07424,37.380895],[-122.06719,37.384105],[-122.07476,37.378582],[-122.07395,37.377603],[-122.07557,37.377586],[-122.07541,37.381263],[-122.07583,37.378389],[-122.0755,37.377364],[-122.06802,37.378237],[-122.06709,37.383981],[-122.0759,37.378309],[-122.07591,37.378287],[-122.07568,37.37745],[-122.0761,37.378796],[-122.07561,37.38137],[-122.07448,37.38207],[-122.06706,37.383837],[-122.06796,37.375554],[-122.07601,37.378168],[-122.07363,37.377395],[-122.0754,37.382916],[-122.07602,37.378915],[-122.0741,37.377943],[-122.07605,37.378112],[-122.07551,37.38151],[-122.06708,37.3837],[-122.0688,37.378536],[-122.07657,37.379366],[-122.07693,37.381252],[-122.0761,37.378042],[-122.06683,37.384847],[-122.07351,37.377306],[-122.07613,37.378014],[-122.07593,37.37903],[-122.07424,37.37801],[-122.07745,37.382638],[-122.06877,37.378434],[-122.07643,37.378961],[-122.0734,37.377315],[-122.06717,37.383572],[-122.07666,37.379247],[-122.07337,37.377342],[-122.06877,37.378355],[-122.07755,37.382684],[-122.0753,37.381405],[-122.07622,37.377894],[-122.07522,37.383151],[-122.07671,37.379188],[-122.0751,37.382773],[-122.07439,37.378094],[-122.07239,37.381735],[-122.07715,37.380607],[-122.068,37.375153],[-122.07654,37.378814],[-122.07592,37.376816],[-122.07678,37.379142],[-122.06733,37.383478],[-122.07769,37.382752],[-122.06883,37.378219],[-122.07682,37.379108],[-122.07727,37.380647],[-122.07453,37.378148],[-122.07632,37.377762],[-122.076,37.376863],[-122.06726,37.377952],[-122.07741,37.382885],[-122.07606,37.37689],[-122.06642,37.385054],[-122.07665,37.378666],[-122.07779,37.38263],[-122.06753,37.38344],[-122.07784,37.382833],[-122.07691,37.378993],[-122.07737,37.382948],[-122.07671,37.378697],[-122.0759,37.376999],[-122.06893,37.378031],[-122.07642,37.377617],[-122.07725,37.38283],[-122.07738,37.383021],[-122.07783,37.382508],[-122.07536,37.382466],[-122.07256,37.38344],[-122.07765,37.382554],[-122.06801,37.374825],[-122.06771,37.383457],[-122.07701,37.378852],[-122.07748,37.382987],[-122.07681,37.378566],[-122.07651,37.377497],[-122.0758,37.377138],[-122.07704,37.378811],[-122.06896,37.377865],[-122.06688,37.377808],[-122.07727,37.38316],[-122.06608,37.385202],[-122.06786,37.383525],[-122.07785,37.382344],[-122.07621,37.376405],[-122.07761,37.383127],[-122.07779,37.380657],[-122.0776,37.382854],[-122.07704,37.378685],[-122.07711,37.38309],[-122.07662,37.37736],[-122.07626,37.376335],[-122.07632,37.376524],[-122.06893,37.377711],[-122.07632,37.376373],[-122.07636,37.376513],[-122.06797,37.383632],[-122.07629,37.376297],[-122.07668,37.377294],[-122.07779,37.380505],[-122.07251,37.376914],[-122.06799,37.374476],[-122.07646,37.376377],[-122.07666,37.377189],[-122.07636,37.376193],[-122.06659,37.377622],[-122.07721,37.382341],[-122.06804,37.383756],[-122.06885,37.377558],[-122.07705,37.378253],[-122.07771,37.380917],[-122.06571,37.385332],[-122.07709,37.378139],[-122.07705,37.378194],[-122.0665,37.377519],[-122.06807,37.383871],[-122.07618,37.376173],[-122.07857,37.383194],[-122.07622,37.383627],[-122.07714,37.378081],[-122.07647,37.376055],[-122.06874,37.377415],[-122.0757,37.377268],[-122.0761,37.376131],[-122.07675,37.376687],[-122.07675,37.376513],[-122.07772,37.381126],[-122.07326,37.383788],[-122.07724,37.378024],[-122.06644,37.3774],[-122.07254,37.378247],[-122.07181,37.383086],[-122.07684,37.376562],[-122.06793,37.374066],[-122.07273,37.376527],[-122.06644,37.377276],[-122.07883,37.383321],[-122.07694,37.376776],[-122.06531,37.385464],[-122.07694,37.376616],[-122.07691,37.37648],[-122.07748,37.378022],[-122.07741,37.381121],[-122.06649,37.377148],[-122.07854,37.380491],[-122.07706,37.376855],[-122.07785,37.381563],[-122.07672,37.383866],[-122.07779,37.379833],[-122.0757,37.375931],[-122.07286,37.378471],[-122.07773,37.381533],[-122.07716,37.376773],[-122.06658,37.377033],[-122.07798,37.38163],[-122.06833,37.377042],[-122.07355,37.383413],[-122.07662,37.376324],[-122.07721,37.376977],[-122.07922,37.383447],[-122.07723,37.377002],[-122.06491,37.385566],[-122.07133,37.382833],[-122.06784,37.373667],[-122.0667,37.376965],[-122.07731,37.376903],[-122.07722,37.381405],[-122.06822,37.376922],[-122.07651,37.376196],[-122.07645,37.376231],[-122.07741,37.38151],[-122.06685,37.376927],[-122.06472,37.385617],[-122.06825,37.384634],[-122.07748,37.377496],[-122.07403,37.384184],[-122.06812,37.376786],[-122.07182,37.377852],[-122.07746,37.377045],[-122.06702,37.376927],[-122.07773,37.381959],[-122.0653,37.377212],[-122.06446,37.38567],[-122.07746,37.377346],[-122.06805,37.376641],[-122.06772,37.3733],[-122.07849,37.379829],[-122.07785,37.380935],[-122.07757,37.377195],[-122.06719,37.376978],[-122.07508,37.37562],[-122.07779,37.379186],[-122.07769,37.38214],[-122.06425,37.385569],[-122.06801,37.376522],[-122.07861,37.382005],[-122.06731,37.377063],[-122.07152,37.378083],[-122.06503,37.377112],[-122.07577,37.381638],[-122.07766,37.382273],[-122.06799,37.376377],[-122.07448,37.384422],[-122.06739,37.37717],[-122.07773,37.382325],[-122.07763,37.382328],[-122.07767,37.377475],[-122.07758,37.382361],[-122.06397,37.385727],[-122.07872,37.381798],[-122.07754,37.382384],[-122.06757,37.372938],[-122.068,37.376253],[-122.0639,37.385595],[-122.07769,37.382439],[-122.07749,37.382388],[-122.06743,37.377319],[-122.07444,37.38365],[-122.06835,37.385352],[-122.08021,37.383921],[-122.07509,37.375161],[-122.07772,37.377677],[-122.08024,37.383912],[-122.07748,37.374768],[-122.07877,37.381624],[-122.07502,37.375086],[-122.08028,37.384036],[-122.08031,37.383852],[-122.07591,37.384906],[-122.07586,37.384988],[-122.06352,37.385768],[-122.06738,37.372597],[-122.07864,37.379166],[-122.07932,37.382338],[-122.07779,37.377957],[-122.07579,37.385073],[-122.07616,37.381103],[-122.06333,37.38562],[-122.0742,37.375179],[-122.07575,37.38515],[-122.07591,37.385129],[-122.07568,37.385016],[-122.0777,37.374419],[-122.06319,37.385603],[-122.08042,37.383484],[-122.08071,37.384254],[-122.07775,37.374345],[-122.0787,37.381153],[-122.0631,37.38579],[-122.06306,37.385548],[-122.06398,37.376724],[-122.07786,37.377652],[-122.0743,37.376523],[-122.07474,37.378017],[-122.07429,37.375063],[-122.06294,37.385463],[-122.06805,37.375443],[-122.06755,37.378082],[-122.07955,37.381956],[-122.07783,37.374115],[-122.0629,37.385352],[-122.06372,37.376623],[-122.06844,37.386233],[-122.0628,37.385301],[-122.07787,37.377366],[-122.07801,37.374156],[-122.07944,37.379166],[-122.06288,37.385249],[-122.07545,37.385618],[-122.07788,37.373944],[-122.07394,37.376422],[-122.07963,37.381744],[-122.06805,37.375196],[-122.07806,37.374134],[-122.07427,37.376865],[-122.06356,37.376563],[-122.07948,37.379091],[-122.07789,37.377264],[-122.0735,37.374833],[-122.07938,37.379255],[-122.07655,37.385448],[-122.0629,37.385143],[-122.06757,37.378431],[-122.0627,37.385164],[-122.07774,37.377216],[-122.07793,37.377202],[-122.06342,37.37651],[-122.08037,37.384796],[-122.06294,37.385052],[-122.08058,37.382851],[-122.07533,37.385814],[-122.06263,37.385092],[-122.08034,37.384844],[-122.06747,37.378542],[-122.07785,37.377107],[-122.07785,37.377078],[-122.07689,37.385611],[-122.07938,37.379511],[-122.0779,37.373584],[-122.06345,37.376704],[-122.06303,37.384893],[-122.06741,37.378674],[-122.07497,37.38542],[-122.06338,37.376793],[-122.07754,37.376909],[-122.07791,37.37346],[-122.06732,37.378773],[-122.07947,37.378632],[-122.08149,37.384004],[-122.07782,37.376814],[-122.08176,37.384793],[-122.07822,37.373587],[-122.07774,37.373461],[-122.08024,37.379169],[-122.06718,37.378854],[-122.06332,37.384733],[-122.07739,37.376752],[-122.06215,37.385347],[-122.08037,37.379169],[-122.07865,37.377202],[-122.07773,37.373581],[-122.06171,37.385838],[-122.07758,37.376834],[-122.07757,37.373461],[-122.06391,37.376882],[-122.06701,37.378892],[-122.08031,37.379085],[-122.06354,37.384733],[-122.07756,37.373541],[-122.08044,37.379277],[-122.08054,37.37917],[-122.06845,37.387145],[-122.07756,37.373575],[-122.07744,37.376779],[-122.08047,37.37931],[-122.07773,37.373738],[-122.08077,37.382169],[-122.07495,37.38643],[-122.06403,37.375798],[-122.06684,37.378888],[-122.07859,37.373591],[-122.06364,37.384762],[-122.06231,37.385547],[-122.08125,37.384324],[-122.06362,37.384588],[-122.07995,37.385478],[-122.0775,37.37365],[-122.06186,37.3855],[-122.07867,37.373591],[-122.06369,37.384556],[-122.07758,37.385972],[-122.08223,37.38502],[-122.07822,37.373977],[-122.0618,37.385528],[-122.06137,37.385859],[-122.08116,37.384282],[-122.06389,37.375755],[-122.06668,37.378841],[-122.08053,37.37948],[-122.06173,37.385549],[-122.07815,37.374032],[-122.07244,37.374299],[-122.06188,37.385578],[-122.0808,37.381977],[-122.06387,37.384874],[-122.0777,37.373978],[-122.06385,37.384484],[-122.06374,37.375718],[-122.06161,37.385568],[-122.08138,37.385352],[-122.06654,37.378734],[-122.06367,37.375714],[-122.06362,37.37571],[-122.07703,37.37354],[-122.0743,37.373848],[-122.08083,37.38422],[-122.06401,37.384408],[-122.06101,37.385906],[-122.07698,37.373528],[-122.06143,37.385568],[-122.07946,37.377842],[-122.06212,37.376025],[-122.07697,37.373492],[-122.06209,37.376011],[-122.06646,37.378598],[-122.07745,37.374043],[-122.07945,37.377202],[-122.0803,37.378537],[-122.08236,37.384432],[-122.06346,37.375694],[-122.07697,37.373433],[-122.06413,37.384349],[-122.07699,37.373409],[-122.07789,37.382678],[-122.07903,37.384205],[-122.07703,37.373393],[-122.0776,37.374302],[-122.0641,37.385167],[-122.06646,37.378457],[-122.06332,37.375666],[-122.06327,37.375649],[-122.07196,37.374057],[-122.06418,37.38513],[-122.06069,37.385961],[-122.0742,37.386136],[-122.0644,37.3752],[-122.0782,37.386276],[-122.07944,37.373604],[-122.07457,37.387043],[-122.06396,37.38524],[-122.06651,37.378329],[-122.07957,37.386105],[-122.0822,37.383083],[-122.07816,37.386345],[-122.06423,37.385333],[-122.063,37.37795],[-122.06437,37.375098],[-122.07468,37.387065],[-122.06309,37.37558],[-122.08039,37.383999],[-122.07178,37.373985],[-122.08298,37.385393],[-122.07944,37.373475],[-122.08213,37.384753],[-122.08035,37.383982],[-122.08034,37.383982],[-122.08034,37.383983],[-122.06349,37.376397],[-122.06864,37.388026],[-122.08033,37.383985],[-122.08032,37.383989],[-122.08031,37.383994],[-122.07707,37.386511],[-122.06661,37.378214],[-122.06426,37.375149],[-122.07689,37.374058],[-122.07165,37.373985],[-122.0823,37.382926],[-122.06038,37.386034],[-122.07749,37.3734],[-122.0646,37.384115],[-122.06292,37.375511],[-122.07438,37.385877],[-122.07852,37.386425],[-122.07754,37.373413],[-122.07219,37.373773],[-122.06074,37.385411],[-122.06393,37.384844],[-122.07191,37.375431],[-122.08099,37.385986],[-122.07152,37.374012],[-122.06316,37.376176],[-122.06677,37.378137],[-122.07737,37.37472],[-122.08018,37.377202],[-122.06066,37.385391],[-122.08192,37.379175],[-122.08234,37.382776],[-122.06845,37.388258],[-122.08273,37.383897],[-122.07945,37.376574],[-122.06391,37.384749],[-122.07678,37.374235],[-122.07944,37.373185],[-122.06275,37.375412],[-122.06058,37.385373],[-122.08028,37.377202],[-122.08193,37.379239],[-122.07182,37.375319],[-122.06694,37.378112],[-122.06333,37.376239],[-122.08029,37.377828],[-122.06051,37.385358],[-122.06449,37.385679],[-122.07136,37.374127],[-122.0654,37.377428],[-122.06255,37.375335],[-122.06067,37.385614],[-122.07932,37.373184],[-122.0636,37.384843],[-122.08029,37.377289],[-122.07178,37.375177],[-122.08029,37.377098],[-122.06389,37.384618],[-122.08042,37.377199],[-122.06,37.386136],[-122.06258,37.375291],[-122.06043,37.385342],[-122.08216,37.379173],[-122.06865,37.388427],[-122.06494,37.383975],[-122.07863,37.384831],[-122.06353,37.384821],[-122.06374,37.384621],[-122.07927,37.37314],[-122.07351,37.385862],[-122.0802,37.373616],[-122.0649,37.383939],[-122.06347,37.384823],[-122.0628,37.376046],[-122.07944,37.374233],[-122.06394,37.384514],[-122.06844,37.388543],[-122.07923,37.386685],[-122.08032,37.373618],[-122.0628,37.378552],[-122.07944,37.372884],[-122.06043,37.385596],[-122.07796,37.371625],[-122.07412,37.387685],[-122.06728,37.378114],[-122.07287,37.384532],[-122.07867,37.373468],[-122.06019,37.385313],[-122.06782,37.380648],[-122.08264,37.385973],[-122.07918,37.386758],[-122.06298,37.376111],[-122.07786,37.375004],[-122.07796,37.371564],[-122.06327,37.384917],[-122.06011,37.385321],[-122.07909,37.386711],[-122.08372,37.385773],[-122.06574,37.377609],[-122.08032,37.373482],[-122.07914,37.38683],[-122.05965,37.386264],[-122.0767,37.387117],[-122.07928,37.386805],[-122.06474,37.383728],[-122.07831,37.384675],[-122.06032,37.385374],[-122.06004,37.385351],[-122.07366,37.385604],[-122.08327,37.384136],[-122.06072,37.384929],[-122.06865,37.38876],[-122.06034,37.385758],[-122.06479,37.38603],[-122.0778,37.371563],[-122.0802,37.37348],[-122.06278,37.374952],[-122.07784,37.374856],[-122.074,37.38786],[-122.06841,37.388808],[-122.06316,37.385022],[-122.06038,37.385431],[-122.08385,37.385839],[-122.06587,37.377703],[-122.05993,37.385403],[-122.06193,37.377693],[-122.06075,37.384835],[-122.06245,37.375916],[-122.0806,37.386617],[-122.06042,37.385494],[-122.08382,37.385901],[-122.06482,37.383547],[-122.06489,37.386151],[-122.06022,37.385884],[-122.07254,37.38432],[-122.08399,37.385906],[-122.06556,37.369595],[-122.08029,37.376552],[-122.06311,37.385162],[-122.07776,37.374714],[-122.07927,37.37263],[-122.08114,37.377183],[-122.06597,37.377826],[-122.06075,37.384693],[-122.08398,37.385688],[-122.06485,37.383468],[-122.06262,37.375979],[-122.05932,37.386405],[-122.07089,37.374678],[-122.07389,37.388069],[-122.08359,37.384238],[-122.08093,37.373625],[-122.06311,37.385272],[-122.07946,37.372406],[-122.0832,37.383161],[-122.08127,37.377188],[-122.0728,37.385582],[-122.06006,37.385987],[-122.06863,37.389096],[-122.06837,37.389102],[-122.08404,37.385598],[-122.08388,37.385629],[-122.07825,37.385448],[-122.0778,37.371894],[-122.06603,37.377963],[-122.06068,37.384821],[-122.07927,37.372445],[-122.07748,37.387429],[-122.06303,37.374563],[-122.06316,37.385354],[-122.08381,37.385595],[-122.07947,37.372305],[-122.06506,37.383503],[-122.06506,37.386361],[-122.07797,37.371049],[-122.08031,37.374238],[-122.07945,37.374965],[-122.07941,37.372256],[-122.06064,37.384917],[-122.08204,37.380184],[-122.0596,37.385719],[-122.06329,37.385397],[-122.06516,37.386308],[-122.07927,37.37232],[-122.06804,37.378395],[-122.07961,37.373474],[-122.06517,37.383455],[-122.06022,37.375309],[-122.0822,37.381746],[-122.07218,37.384134],[-122.07059,37.374548],[-122.05985,37.386093],[-122.05902,37.386554],[-122.06491,37.386427],[-122.07932,37.372264],[-122.07377,37.388339],[-122.07945,37.375516],[-122.08225,37.386607],[-122.0834,37.386341],[-122.07209,37.384119],[-122.06376,37.378764],[-122.0842,37.385357],[-122.06528,37.383373],[-122.06006,37.375304],[-122.07295,37.385295],[-122.07201,37.384158],[-122.0678,37.381445],[-122.08354,37.386406],[-122.08118,37.37672],[-122.06529,37.386576],[-122.06114,37.377515],[-122.07639,37.38776],[-122.07036,37.374488],[-122.06198,37.374649],[-122.06535,37.38327],[-122.05935,37.385904],[-122.05989,37.37526],[-122.06836,37.37851],[-122.06107,37.377502],[-122.06459,37.386535],[-122.08023,37.387268],[-122.06054,37.385264],[-122.07773,37.372369],[-122.06101,37.377505],[-122.0726,37.386266],[-122.05867,37.386727],[-122.06097,37.377523],[-122.06489,37.385998],[-122.06097,37.384061],[-122.05945,37.386268],[-122.06095,37.377551],[-122.0655,37.386715],[-122.07183,37.384371],[-122.06382,37.385353],[-122.06618,37.378512],[-122.07012,37.374465],[-122.08204,37.377217],[-122.06463,37.386656],[-122.07042,37.375253],[-122.07207,37.385295],[-122.06554,37.386634],[-122.06455,37.386353],[-122.08128,37.376392],[-122.07172,37.384416],[-122.06643,37.383318],[-122.06644,37.375907],[-122.08441,37.384634],[-122.06544,37.386823],[-122.06565,37.386783],[-122.05958,37.375148],[-122.07626,37.376075],[-122.06463,37.386333],[-122.07649,37.371562],[-122.08214,37.3812]] -------------------------------------------------------------------------------- /demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sadaszewski/concaveman-cpp/97d47b0e31cca9926b487d6b1694af9bf5cf91fa/demo.png -------------------------------------------------------------------------------- /src/main/cpp/concaveman.cpp: -------------------------------------------------------------------------------- 1 | #if 0 2 | g++ -std=c++11 -shared concaveman.cpp -o libconcaveman.so 3 | exit 0 4 | #endif 5 | 6 | // 7 | // Author: Stanislaw Adaszewski, 2019 8 | // 9 | 10 | #include "concaveman.h" 11 | 12 | extern "C" { 13 | void pyconcaveman2d(double *points_c, size_t num_points, 14 | int *hull_points_c, size_t num_hull_points, 15 | double concavity, double lengthThreshold, 16 | double **concave_points_c, size_t *num_concave_points, 17 | void(**p_free)(void*)); 18 | } 19 | 20 | void pyconcaveman2d(double *points_c, size_t num_points, 21 | int *hull_points_c, size_t num_hull_points, 22 | double concavity, double lengthThreshold, 23 | double **p_concave_points_c, 24 | size_t *p_num_concave_points, 25 | void(**p_free)(void*)) { 26 | 27 | std::cout << "pyconcaveman2d(), concavity: " << concavity << 28 | " lengthThreshold: " << lengthThreshold << std::endl; 29 | 30 | typedef double T; 31 | typedef std::array point_type; 32 | 33 | std::vector points(num_points); 34 | for (auto i = 0; i < num_points; i++) { 35 | points[i] = { points_c[i << 1], points_c[(i << 1) + 1] }; 36 | } 37 | 38 | std::cout << "points:" << std::endl; 39 | for (auto &p : points) 40 | std::cout << p[0] << " " << p[1] << std::endl; 41 | 42 | std::vector hull(num_hull_points); 43 | for (auto i = 0; i < num_hull_points; i++) { 44 | hull[i] = hull_points_c[i]; 45 | } 46 | 47 | std::cout << "hull:" << std::endl; 48 | for (auto &i : hull) 49 | std::cout << i << std::endl; 50 | 51 | auto concave_points = concaveman(points, hull, concavity, lengthThreshold); 52 | 53 | std::cout << "concave_points:" << std::endl; 54 | for (auto &p : concave_points) 55 | std::cout << p[0] << " " << p[1] << std::endl; 56 | 57 | double *concave_points_c = *p_concave_points_c = (double*) malloc(sizeof(double) * 2 * concave_points.size()); 58 | for (auto i = 0; i < concave_points.size(); i++) { 59 | concave_points_c[i << 1] = concave_points[i][0]; 60 | concave_points_c[(i << 1) + 1] = concave_points[i][1]; 61 | } 62 | 63 | *p_num_concave_points = concave_points.size(); 64 | *p_free = free; 65 | } 66 | -------------------------------------------------------------------------------- /src/main/cpp/concaveman.h: -------------------------------------------------------------------------------- 1 | // 2 | // Author: Stanislaw Adaszewski, 2019 3 | // C++ port from https://github.com/mapbox/concaveman (js) 4 | // 5 | // Comments from js repo added by wheeled 6 | // 7 | 8 | #pragma once 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | //#define DEBUG // uncomment to dump debug info to screen 25 | //#define DEBUG_2 // uncomment to dump second-level debug info to screen 26 | 27 | template 28 | std::unique_ptr make_unique(Args&&... args) { 29 | return std::unique_ptr(new T(std::forward(args)...)); 30 | } 31 | 32 | 33 | template class compare_first { 34 | public: 35 | bool operator()(const T &a, const T &b) { 36 | return (std::get<0>(a) < std::get<0>(b)); 37 | } 38 | }; 39 | 40 | 41 | template T orient2d( 42 | const std::array &p1, 43 | const std::array &p2, 44 | const std::array &p3) { 45 | 46 | T res = (p2[1] - p1[1]) * (p3[0] - p2[0]) - 47 | (p2[0] - p1[0]) * (p3[1] - p2[1]); 48 | 49 | return res; 50 | } 51 | 52 | 53 | // check if the edges (p1,q1) and (p2,q2) intersect 54 | template bool intersects( 55 | const std::array &p1, 56 | const std::array &q1, 57 | const std::array &p2, 58 | const std::array &q2) { 59 | 60 | auto res = (p1[0] != q2[0] || p1[1] != q2[1]) && 61 | (q1[0] != p2[0] || q1[1] != p2[1]) && 62 | (orient2d(p1, q1, p2) > 0) != (orient2d(p1, q1, q2) > 0) && 63 | (orient2d(p2, q2, p1) > 0) != (orient2d(p2, q2, q1) > 0); 64 | 65 | return res; 66 | } 67 | 68 | 69 | // square distance between 2 points 70 | template T getSqDist( 71 | const std::array &p1, 72 | const std::array &p2) { 73 | 74 | auto dx = p1[0] - p2[0]; 75 | auto dy = p1[1] - p2[1]; 76 | return dx * dx + dy * dy; 77 | } 78 | 79 | 80 | // square distance from a point to a segment 81 | template T sqSegDist( 82 | const std::array &p, 83 | const std::array &p1, 84 | const std::array &p2) { 85 | 86 | auto x = p1[0]; 87 | auto y = p1[1]; 88 | auto dx = p2[0] - x; 89 | auto dy = p2[1] - y; 90 | 91 | if (dx != 0 || dy != 0) { 92 | auto t = ((p[0] - x) * dx + (p[1] - y) * dy) / (dx * dx + dy * dy); 93 | if (t > 1) { 94 | x = p2[0]; 95 | y = p2[1]; 96 | } else if (t > 0) { 97 | x += dx * t; 98 | y += dy * t; 99 | } 100 | } 101 | 102 | dx = p[0] - x; 103 | dy = p[1] - y; 104 | 105 | return dx * dx + dy * dy; 106 | } 107 | 108 | 109 | // segment to segment distance, ported from http://geomalgorithms.com/a07-_distance.html by Dan Sunday 110 | template T sqSegSegDist(T x0, T y0, 111 | T x1, T y1, 112 | T x2, T y2, 113 | T x3, T y3) { 114 | auto ux = x1 - x0; 115 | auto uy = y1 - y0; 116 | auto vx = x3 - x2; 117 | auto vy = y3 - y2; 118 | auto wx = x0 - x2; 119 | auto wy = y0 - y2; 120 | auto a = ux * ux + uy * uy; 121 | auto b = ux * vx + uy * vy; 122 | auto c = vx * vx + vy * vy; 123 | auto d = ux * wx + uy * wy; 124 | auto e = vx * wx + vy * wy; 125 | auto D = a * c - b * b; 126 | 127 | T sc, sN, tc, tN; 128 | auto sD = D; 129 | auto tD = D; 130 | 131 | if (D == 0) { 132 | sN = 0; 133 | sD = 1; 134 | tN = e; 135 | tD = c; 136 | } else { 137 | sN = b * e - c * d; 138 | tN = a * e - b * d; 139 | if (sN < 0) { 140 | sN = 0; 141 | tN = e; 142 | tD = c; 143 | } else if (sN > sD) { 144 | sN = sD; 145 | tN = e + b; 146 | tD = c; 147 | } 148 | } 149 | 150 | if (tN < 0) { 151 | tN = 0; 152 | if (-d < 0) sN = 0; 153 | else if (-d > a) sN = sD; 154 | else { 155 | sN = -d; 156 | sD = a; 157 | } 158 | } else if (tN > tD) { 159 | tN = tD; 160 | if (-d + b < 0) sN = 0; 161 | else if (-d + b > a) sN = sD; 162 | else { 163 | sN = -d + b; 164 | sD = a; 165 | } 166 | } 167 | 168 | sc = ((sN == 0) ? 0 : sN / sD); 169 | tc = ((tN == 0) ? 0 : tN / tD); 170 | 171 | auto cx = (1 - sc) * x0 + sc * x1; 172 | auto cy = (1 - sc) * y0 + sc * y1; 173 | auto cx2 = (1 - tc) * x2 + tc * x3; 174 | auto cy2 = (1 - tc) * y2 + tc * y3; 175 | auto dx = cx2 - cx; 176 | auto dy = cy2 - cy; 177 | 178 | return dx * dx + dy * dy; 179 | } 180 | 181 | 182 | template class rtree { 183 | public: 184 | typedef rtree type; 185 | typedef const type const_type; 186 | typedef type *type_ptr; 187 | typedef const type *type_const_ptr; 188 | typedef std::array bounds_type; 189 | typedef DATA data_type; 190 | 191 | rtree(): 192 | m_is_leaf(false), m_data() { 193 | for (auto i = 0; i < DIM; i++) { 194 | m_bounds[i] = std::numeric_limits::max(); 195 | m_bounds[i + DIM] = std::numeric_limits::min(); 196 | } 197 | } 198 | 199 | rtree(data_type data, const bounds_type &bounds): 200 | m_is_leaf(true), m_data(data), m_bounds(bounds) { 201 | for (auto i = 0; i < DIM; i++) 202 | if (bounds[i] > bounds[i + DIM]) 203 | throw std::runtime_error("Bounds minima have to be less than maxima"); 204 | } 205 | 206 | void insert(data_type data, const bounds_type &bounds) { 207 | if (m_is_leaf) 208 | throw std::runtime_error("Cannot insert into leaves"); 209 | 210 | m_bounds = updated_bounds(bounds); 211 | if (m_children.size() < MAX_CHILDREN) { 212 | auto r = make_unique(data, bounds); 213 | m_children.push_back(std::move(r)); 214 | return; 215 | } 216 | 217 | std::reference_wrapper best_child = *m_children.begin()->get(); 218 | auto best_volume = volume(best_child.get().updated_bounds(bounds)); 219 | for (auto it = ++m_children.begin(); it != m_children.end(); it++) { 220 | auto v = volume((*it)->updated_bounds(bounds)); 221 | if (v < best_volume) { 222 | best_volume = v; 223 | best_child = *it->get(); 224 | } 225 | } 226 | if (!best_child.get().is_leaf()) { 227 | best_child.get().insert(data, bounds); 228 | #ifdef DEBUG 229 | std::cout << "best_child: " << bounds[0] << " " << bounds[1] << std::endl; 230 | #endif 231 | return; 232 | } 233 | 234 | auto leaf = make_unique(best_child.get().data(), 235 | best_child.get().bounds()); 236 | best_child.get().m_is_leaf = false; 237 | best_child.get().m_data = data_type(); 238 | best_child.get().m_children.push_back(std::move(leaf)); 239 | best_child.get().insert(data, bounds); 240 | } 241 | 242 | void intersection(const bounds_type &bounds, 243 | std::vector> &res) const { 244 | if (!intersects(bounds)) 245 | return; 246 | if (m_is_leaf) { 247 | res.push_back(*this); 248 | return; 249 | } 250 | for (auto &ch : m_children) 251 | ch->intersection(bounds, res); 252 | } 253 | 254 | std::vector> intersection(const bounds_type& bounds) const { 255 | std::vector> res; 256 | intersection(bounds, res); 257 | return res; 258 | } 259 | 260 | bool intersects(const bounds_type &bounds) const { 261 | for (auto i = 0; i < DIM; i++) { 262 | if (m_bounds[i] > bounds[i + DIM]) 263 | return false; 264 | if (m_bounds[i + DIM] < bounds[i]) 265 | return false; 266 | } 267 | return true; 268 | } 269 | 270 | void erase(data_type data, const bounds_type &bounds) { 271 | if (m_is_leaf) 272 | throw std::runtime_error("Cannot erase from leaves"); 273 | 274 | if (!intersects(bounds)) 275 | return; 276 | 277 | for (auto it = m_children.begin(); it != m_children.end(); ) { 278 | if (!(*it)->m_is_leaf) { 279 | (*it)->erase(data, bounds); 280 | it++; 281 | } else if ((*it)->m_data == data && 282 | (*it)->m_bounds == bounds) { 283 | m_children.erase(it++); 284 | } else 285 | it++; 286 | } 287 | } 288 | 289 | void print(int level = 0) { 290 | // print the entire tree 291 | 292 | for (auto it = m_children.begin(); it != m_children.end(); ) { 293 | auto bounds = (*it)->m_bounds; 294 | std::string pad(level, '\t'); 295 | if ((*it)->m_is_leaf) { 296 | printf ("%s leaf %0.6f %0.6f \n", pad.c_str(), bounds[0], bounds[1]); 297 | } 298 | else { 299 | printf ("%s branch %0.6f %0.6f %0.6f %0.6f \n", pad.c_str(), bounds[0], bounds[1], bounds[2], bounds[3]); 300 | (*it)->print(level + 1); 301 | } 302 | it++; 303 | } 304 | } 305 | 306 | bounds_type updated_bounds(const bounds_type &child_bounds) const { 307 | bounds_type res; 308 | for (auto i = 0; i < DIM; i++) { 309 | res[i] = std::min(child_bounds[i], m_bounds[i]); 310 | res[i + DIM] = std::max(child_bounds[i + DIM], m_bounds[i + DIM]); 311 | } 312 | return res; 313 | } 314 | 315 | static T volume(const bounds_type &bounds) { 316 | T res = 1; 317 | for (auto i = 0; i < DIM; i++) { 318 | auto delta = bounds[i + DIM] - bounds[i]; 319 | res *= delta; 320 | } 321 | return res; 322 | } 323 | 324 | const bounds_type& bounds() const { 325 | return m_bounds; 326 | } 327 | 328 | bool is_leaf() const { 329 | return m_is_leaf; 330 | } 331 | 332 | data_type data() const { 333 | return m_data; 334 | } 335 | 336 | const std::list>& children() const { 337 | return m_children; 338 | } 339 | 340 | static std::string bounds_to_string(const bounds_type &bounds) { 341 | std::string res = "( "; 342 | for (auto i = 0; i < DIM * 2; i++) { 343 | if (i > 0) 344 | res += ", "; 345 | res += std::to_string(bounds[i]); 346 | } 347 | res += " )"; 348 | return res; 349 | } 350 | 351 | void to_string(std::string &res, int tab) const { 352 | std::string pad(tab, '\t'); 353 | 354 | if (m_is_leaf) { 355 | res += pad + "{ data: " + std::to_string(m_data) + 356 | ", bounds: " + bounds_to_string(m_bounds) + 357 | " }"; 358 | return; 359 | } 360 | 361 | res += pad + "{ bounds: " + bounds_to_string(m_bounds) + 362 | ", children: [\n"; 363 | auto i = 0; 364 | for (auto &ch : m_children) { 365 | if (i++ > 0) 366 | res += "\n"; 367 | ch->to_string(res, tab + 1); 368 | } 369 | res += "\n" + pad + "]}"; 370 | } 371 | 372 | std::string to_string() const { 373 | std::string res; 374 | to_string(res, 0); 375 | return res; 376 | } 377 | 378 | private: 379 | bool m_is_leaf; 380 | data_type m_data; 381 | std::list> m_children; 382 | bounds_type m_bounds; 383 | }; 384 | 385 | 386 | template struct Node { 387 | typedef Node type; 388 | typedef type *type_ptr; 389 | typedef std::array point_type; 390 | 391 | Node(): p(), 392 | minX(), minY(), maxX(), maxY() { 393 | 394 | } 395 | 396 | Node(const point_type &p): Node() { 397 | this->p = p; 398 | } 399 | 400 | point_type p; 401 | T minX; 402 | T minY; 403 | T maxX; 404 | T maxY; 405 | }; 406 | 407 | 408 | template class CircularList; 409 | 410 | 411 | template class CircularElement { 412 | public: 413 | typedef CircularElement type; 414 | typedef type *ptr_type; 415 | 416 | template CircularElement(Args&&... args): 417 | m_data(std::forward(args)...) { 418 | 419 | } 420 | 421 | T& data() { 422 | return m_data; 423 | } 424 | 425 | template CircularElement* insert(Args&&... args) { 426 | auto elem = new CircularElement(std::forward(args)...); 427 | elem->m_prev = this; 428 | elem->m_next = m_next; 429 | m_next->m_prev = elem; 430 | m_next = elem; 431 | return elem; 432 | } 433 | 434 | CircularElement* prev() { 435 | return m_prev; 436 | } 437 | 438 | CircularElement* next() { 439 | return m_next; 440 | } 441 | 442 | private: 443 | T m_data; 444 | CircularElement *m_prev; 445 | CircularElement *m_next; 446 | 447 | friend class CircularList; 448 | }; 449 | 450 | 451 | template class CircularList { 452 | public: 453 | typedef CircularElement element_type; 454 | 455 | CircularList(): m_last(nullptr) { 456 | 457 | } 458 | 459 | ~CircularList() { 460 | #ifdef DEBUG 461 | std::cout << "~CircularList()" << std::endl; 462 | #endif 463 | auto node = m_last; 464 | while (true) { 465 | #ifdef DEBUG 466 | // std::cout << (i++) << std::endl; 467 | #endif 468 | auto tmp = node; 469 | node = node->m_next; 470 | delete tmp; 471 | if (node == m_last) 472 | break; 473 | } 474 | } 475 | 476 | template CircularElement* insert(element_type *prev, Args&&... args) { 477 | auto elem = new CircularElement(std::forward(args)...); 478 | 479 | if (prev == nullptr && m_last != nullptr) 480 | throw std::runtime_error("Once the list is non-empty you must specify where to insert"); 481 | 482 | if (prev == nullptr) { 483 | elem->m_prev = elem->m_next = elem; 484 | } else { 485 | elem->m_prev = prev; 486 | elem->m_next = prev->m_next; 487 | prev->m_next->m_prev = elem; 488 | prev->m_next = elem; 489 | } 490 | 491 | m_last = elem; 492 | 493 | return elem; 494 | } 495 | 496 | 497 | private: 498 | element_type *m_last; 499 | }; 500 | 501 | 502 | // update the bounding box of a node's edge 503 | template void updateBBox(typename CircularElement::ptr_type elem) { 504 | auto &node(elem->data()); 505 | auto p1 = node.p; 506 | auto p2 = elem->next()->data().p; 507 | node.minX = std::min(p1[0], p2[0]); 508 | node.minY = std::min(p1[1], p2[1]); 509 | node.maxX = std::max(p1[0], p2[0]); 510 | node.maxY = std::max(p1[1], p2[1]); 511 | } 512 | 513 | 514 | #ifdef DEBUG_2 515 | template void snapshot( 516 | const std::array &a, 517 | const std::array &b, 518 | const std::array &c, 519 | const std::array &d, 520 | const double sqLen, 521 | const double maxSqLen, 522 | const std::array &trigger, 523 | const bool use_trigger) { 524 | 525 | if ( !use_trigger || trigger == b ) { 526 | if ( !use_trigger ) 527 | printf ("Snapshot untriggered\n"); 528 | else 529 | printf ("Snapshot trigger: %0.6f %0.6f \n", trigger[0], trigger[1]); 530 | printf ("... segment a, b: %0.6f %0.6f, %0.6f %0.6f \n", a[0], a[1], b[0], b[1]); 531 | printf ("... segment c, d: %0.6f %0.6f, %0.6f %0.6f \n", c[0], c[1], d[0], d[1]); 532 | printf ("... sqDist a-b, b-c, c-d: %e, %e, %e", getSqDist(a, b), getSqDist(b, c), getSqDist(c, d)); 533 | printf ("... sqLen, maxSqLen: %e, %e", sqLen, maxSqLen); 534 | } 535 | } 536 | #endif 537 | 538 | 539 | template std::vector> concaveman( 540 | const std::vector> &points, 541 | // start with a convex hull of the points 542 | const std::vector &hull, 543 | // a relative measure of concavity; higher value means simpler hull 544 | T concavity=2, 545 | // when a segment goes below this length threshold, it won't be drilled down further 546 | T lengthThreshold=0 547 | ) { 548 | 549 | typedef Node node_type; 550 | typedef std::array point_type; 551 | typedef CircularElement circ_elem_type; 552 | typedef CircularList circ_list_type; 553 | typedef circ_elem_type *circ_elem_ptr_type; 554 | 555 | #ifdef DEBUG 556 | std::cout << "concaveman()" << std::endl; 557 | #endif 558 | 559 | // exit if hull includes all points already 560 | if (hull.size() == points.size()) { 561 | std::vector res; 562 | for (auto &i : hull) res.push_back(points[i]); 563 | return res; 564 | } 565 | 566 | // index the points with an R-tree 567 | rtree tree; 568 | 569 | for (auto &p : points) 570 | tree.insert(p, { p[0], p[1], p[0], p[1] }); 571 | 572 | circ_list_type circList; 573 | circ_elem_ptr_type last = nullptr; 574 | 575 | std::list queue; 576 | 577 | // turn the convex hull into a linked list and populate the initial edge queue with the nodes 578 | for (auto &idx : hull) { 579 | auto &p = points[idx]; 580 | tree.erase(p, { p[0], p[1], p[0], p[1] }); 581 | last = circList.insert(last, p); 582 | queue.push_back(last); 583 | } 584 | 585 | #ifdef DEBUG_2 586 | tree.print(0); 587 | #endif 588 | 589 | // loops through the hull? why? 590 | #ifdef DEBUG 591 | std::cout << "Starting hull: "; 592 | #endif 593 | for (auto elem = last->next(); ; elem=elem->next()) { 594 | #ifdef DEBUG 595 | std::cout << elem->data().p[0] << " " << elem->data().p[1] << std::endl; 596 | #endif 597 | if (elem == last) 598 | break; 599 | } 600 | 601 | // index the segments with an R-tree (for intersection checks) 602 | rtree segTree; 603 | for (auto &elem : queue) { 604 | auto &node(elem->data()); 605 | updateBBox(elem); 606 | segTree.insert(elem, { node.minX, 607 | node.minY, node.maxX, node.maxY }); 608 | } 609 | 610 | auto sqConcavity = concavity * concavity; 611 | auto sqLenThreshold = lengthThreshold * lengthThreshold; 612 | 613 | // process edges one by one 614 | while (!queue.empty()) { 615 | auto elem = *queue.begin(); 616 | queue.pop_front(); 617 | 618 | auto a = elem->prev()->data().p; 619 | auto b = elem->data().p; 620 | auto c = elem->next()->data().p; 621 | auto d = elem->next()->next()->data().p; 622 | 623 | // skip the edge if it's already short enough 624 | auto sqLen = getSqDist(b, c); 625 | if (sqLen < sqLenThreshold) 626 | continue; 627 | 628 | auto maxSqLen = sqLen / sqConcavity; 629 | 630 | #ifdef DEBUG_2 631 | // dump key parameters either on every pass or when a certain point is 'b' 632 | point_type trigger = { 151.1373474787800, -33.7733192376544 }; 633 | snapshot(a, b, c, d, sqLen, maxSqLen, trigger, true); 634 | #endif 635 | 636 | // find the best connection point for the current edge to flex inward to 637 | bool ok; 638 | auto p = findCandidate(tree, a, b, c, d, maxSqLen, segTree, ok); 639 | 640 | // if we found a connection and it satisfies our concavity measure 641 | if (ok && std::min(getSqDist(p, b), getSqDist(p, c)) <= maxSqLen) { 642 | 643 | #ifdef DEBUG 644 | printf ("Modifying hull, p: %0.6f %0.6f \n" ,p[0], p[1]); 645 | #endif 646 | 647 | // connect the edge endpoints through this point and add 2 new edges to the queue 648 | queue.push_back(elem); 649 | queue.push_back(elem->insert(p)); 650 | 651 | // update point and segment indexes 652 | auto &node = elem->data(); 653 | auto &next = elem->next()->data(); 654 | 655 | tree.erase(p, { p[0], p[1], p[0], p[1] }); 656 | segTree.erase(elem, { node.minX, node.minY, node.maxX, node.maxY }); 657 | 658 | updateBBox(elem); 659 | updateBBox(elem->next()); 660 | 661 | segTree.insert(elem, { node.minX, node.minY, node.maxX, node.maxY }); 662 | segTree.insert(elem->next(), { next.minX, next.minY, next.maxX, next.maxY }); 663 | } 664 | #ifdef DEBUG 665 | else 666 | printf ("No point found along segment: %0.6f %0.6f, %0.6f %0.6f \n", b[0], b[1], c[0], c[1]); 667 | #endif 668 | } 669 | 670 | // convert the resulting hull linked list to an array of points 671 | std::vector concave; 672 | for (auto elem = last->next(); ; elem = elem->next()) { 673 | concave.push_back(elem->data().p); 674 | if (elem == last) 675 | break; 676 | } 677 | 678 | return concave; 679 | } 680 | 681 | 682 | template std::array findCandidate( 683 | const rtree> &tree, 684 | const std::array &a, 685 | const std::array &b, 686 | const std::array &c, 687 | const std::array &d, 688 | T maxDist, 689 | const rtree>::ptr_type> &segTree, 690 | bool &ok) { 691 | 692 | typedef std::array point_type; 693 | typedef CircularElement> circ_elem_type; 694 | typedef rtree> tree_type; 695 | typedef const tree_type const_tree_type; 696 | typedef std::reference_wrapper tree_ref_type; 697 | typedef std::tuple tuple_type; 698 | 699 | #ifdef DEBUG 700 | std::cout << "findCandidate(), maxDist: " << maxDist << std::endl; 701 | #endif 702 | 703 | ok = false; 704 | 705 | std::priority_queue, compare_first> queue; 706 | std::reference_wrapper node = tree; 707 | 708 | // search through the point R-tree with a depth-first search using a priority queue 709 | // in the order of distance to the edge (b, c) 710 | while (true) { 711 | for (auto &child : node.get().children()) { 712 | 713 | auto bounds = child->bounds(); 714 | point_type pt = { bounds[0], bounds[1] }; 715 | 716 | auto dist = child->is_leaf() ? sqSegDist(pt, b, c) : sqSegBoxDist(b, c, *child); 717 | if (dist > maxDist) 718 | continue; // skip the node if it's farther than we ever need 719 | 720 | queue.push(tuple_type(-dist, *child)); 721 | } 722 | 723 | while (!queue.empty() && std::get<1>(queue.top()).get().is_leaf()) { 724 | auto item = queue.top(); 725 | queue.pop(); 726 | 727 | auto bounds = std::get<1>(item).get().bounds(); 728 | point_type p = { bounds[0], bounds[1] }; 729 | 730 | // skip all points that are as close to adjacent edges (a,b) and (c,d), 731 | // and points that would introduce self-intersections when connected 732 | auto d0 = sqSegDist(p, a, b); 733 | auto d1 = sqSegDist(p, c, d); 734 | 735 | #ifdef DEBUG_2 736 | printf (" p: %0.6f %0.6f sqSegDist: %e, %e, %e \n", bounds[0], bounds[1], d0, std::get<0>(item), d1); 737 | #endif 738 | 739 | if (-std::get<0>(item) < d0 && -std::get<0>(item) < d1 && 740 | noIntersections(b, p, segTree) && 741 | noIntersections(c, p, segTree)) { 742 | 743 | ok = true; 744 | return std::get<1>(item).get().data(); 745 | } 746 | 747 | #ifdef DEBUG_2 748 | else { 749 | bool cond1 = -std::get<0>(item) < d0; 750 | bool cond2 = -std::get<0>(item) < d1; 751 | bool cond3 = noIntersections(b, p, segTree); 752 | bool cond4 = noIntersections(c, p, segTree); 753 | std::cout << "Not OK: " << cond1 << " " << cond2 << " " << cond3 << " " << cond4 << std::endl; 754 | } 755 | #endif 756 | } 757 | 758 | if (queue.empty()) 759 | break; 760 | 761 | node = std::get<1>(queue.top()); 762 | queue.pop(); 763 | } 764 | 765 | return point_type(); 766 | } 767 | 768 | 769 | // square distance from a segment bounding box to the given one 770 | template T sqSegBoxDist( 771 | const std::array &a, 772 | const std::array &b, 773 | const rtree &bbox) { 774 | 775 | if (inside(a, bbox) || inside(b, bbox)) 776 | return 0; 777 | 778 | auto &bounds = bbox.bounds(); 779 | auto minX = bounds[0]; 780 | auto minY = bounds[1]; 781 | auto maxX = bounds[2]; 782 | auto maxY = bounds[3]; 783 | 784 | auto d1 = sqSegSegDist(a[0], a[1], b[0], b[1], minX, minY, maxX, minY); 785 | if (d1 == 0) return 0; 786 | 787 | auto d2 = sqSegSegDist(a[0], a[1], b[0], b[1], minX, minY, minX, maxY); 788 | if (d2 == 0) return 0; 789 | 790 | auto d3 = sqSegSegDist(a[0], a[1], b[0], b[1], maxX, minY, maxX, maxY); 791 | if (d3 == 0) return 0; 792 | 793 | auto d4 = sqSegSegDist(a[0], a[1], b[0], b[1], minX, maxY, maxX, maxY); 794 | if (d4 == 0) return 0; 795 | 796 | return std::min(std::min(d1, d2), std::min(d3, d4)); 797 | } 798 | 799 | 800 | template bool inside( 801 | const std::array &a, 802 | const rtree &bbox) { 803 | 804 | auto &bounds = bbox.bounds(); 805 | 806 | auto minX = bounds[0]; 807 | auto minY = bounds[1]; 808 | auto maxX = bounds[2]; 809 | auto maxY = bounds[3]; 810 | 811 | auto res = (a[0] >= minX) && 812 | (a[0] <= maxX) && 813 | (a[1] >= minY) && 814 | (a[1] <= maxY); 815 | return res; 816 | } 817 | 818 | 819 | // check if the edge (a,b) doesn't intersect any other edges 820 | template bool noIntersections( 821 | const std::array &a, 822 | const std::array &b, 823 | const rtree>::ptr_type> &segTree) { 824 | 825 | auto minX = std::min(a[0], b[0]); 826 | auto minY = std::min(a[1], b[1]); 827 | auto maxX = std::max(a[0], b[0]); 828 | auto maxY = std::max(a[1], b[1]); 829 | 830 | auto isect = segTree.intersection({ minX, minY, maxX, maxY }); 831 | 832 | for (decltype(segTree) &ch : isect) { 833 | auto elem = ch.data(); 834 | 835 | if (intersects(elem->data().p, elem->next()->data().p, a, b)) 836 | return false; 837 | } 838 | 839 | return true; 840 | } 841 | -------------------------------------------------------------------------------- /src/main/python/concaveman.py: -------------------------------------------------------------------------------- 1 | import cffi 2 | import numpy as np 3 | 4 | 5 | _ffi = cffi.FFI() 6 | _ffi.cdef('void pyconcaveman2d(double *points_c, size_t num_points, int *hull_points_c, size_t num_hull_points, double concavity, double lengthThreshold, double **p_concave_points_c, size_t *p_num_concave_points, void (**p_free)(void*));') 7 | _lib = _ffi.dlopen('/Users/sadaszewski/Documents/workspace/concaveman-cpp/src/main/cpp/libconcaveman.so') 8 | 9 | 10 | def concaveman2d(points, hull, concavity=2.0, lengthThreshold=0.0): 11 | points = np.array(points).astype(np.double) 12 | hull = np.array(hull).astype(np.int32) 13 | 14 | if len(points.shape) != 2: 15 | raise ValueError('points must be a 2-D array') 16 | 17 | if len(hull.shape) != 1: 18 | raise ValueError('hull must be a 1-D array') 19 | 20 | if np.any(hull >= len(points)) or np.any(hull < 0): 21 | raise ValueError('hull indices out of bounds') 22 | 23 | p_concave_points_c = _ffi.new('double**') 24 | p_num_concave_points = _ffi.new('size_t*') 25 | p_free = _ffi.new('void(**)(void*)') 26 | 27 | points_c = _ffi.cast('double*', points.ctypes.data) 28 | hull_c = _ffi.cast('int*', hull.ctypes.data) 29 | _lib.pyconcaveman2d(points_c, len(points), 30 | hull_c, len(hull), 31 | concavity, lengthThreshold, 32 | p_concave_points_c, p_num_concave_points, 33 | p_free) 34 | 35 | num_concave_points = p_num_concave_points[0] 36 | concave_points_c = p_concave_points_c[0] 37 | 38 | buffer = _ffi.buffer(concave_points_c, 8 * 2 * num_concave_points) 39 | 40 | concave_points = np.frombuffer(buffer, dtype=np.double) 41 | concave_points = concave_points.reshape((num_concave_points, 2)) 42 | concave_points = concave_points.copy() 43 | 44 | print('concave_points:', concave_points) 45 | 46 | p_free[0](concave_points_c) 47 | 48 | return concave_points 49 | 50 | 51 | if __name__ == '__main__': 52 | concaveman2d([[0, 0], [.25, .15], [1, 0], [1, 1]], [0, 2, 3]) 53 | -------------------------------------------------------------------------------- /src/main/python/demo.py: -------------------------------------------------------------------------------- 1 | from concaveman import concaveman2d 2 | import matplotlib as mpl 3 | mpl.use('Qt5Agg') 4 | import matplotlib.pyplot as plt 5 | import json 6 | import numpy as np 7 | from scipy.spatial import ConvexHull 8 | 9 | 10 | with open('../../../data/points-1k.json', 'r') as f: 11 | pts = json.load(f) 12 | pts = np.array(pts) 13 | 14 | h = ConvexHull(pts) 15 | cc = concaveman2d(pts, h.vertices, 2, 0.005) 16 | 17 | plt.plot(pts[:,0], pts[:,1], 'b*', 18 | cc[:,0].tolist() + [ cc[0,0] ], 19 | cc[:,1].tolist() + [ cc[0,1] ], 'r-') 20 | plt.show() 21 | -------------------------------------------------------------------------------- /src/test/cpp/test_concaveman.cpp: -------------------------------------------------------------------------------- 1 | #if 0 2 | g++ -std=c++11 -I../../../main/cpp -o test_concaveman 3 | exit 0 4 | #endif 5 | 6 | static void test_00_rtree_basic() { 7 | typedef rtree myrtree; 8 | typedef rtree myrtree2; 9 | typedef rtree myrtree3; 10 | typedef rtree myrtree4; 11 | myrtree tree; 12 | myrtree2 tree2; 13 | myrtree3 tree3; 14 | myrtree4 tree4; 15 | 16 | // myrtree::bounds_type bounds ; 17 | for (auto i = 0; i < 500; i++) { 18 | auto a = static_cast(random() % 100); 19 | auto b = static_cast(random() % 100); 20 | auto c = static_cast(random() % 100); 21 | auto d = static_cast(random() % 100); 22 | myrtree::bounds_type bounds = { std::min(a, c), std::min(b, d), 23 | std::max(a, c), std::max(b, d) }; 24 | tree.insert(i, bounds); 25 | tree2.insert(i, bounds); 26 | tree3.insert(i, bounds); 27 | tree4.insert(i, bounds); 28 | // std::cout << "i: " << i << ", tree: " << tree.to_string() << std::endl; 29 | } 30 | // tree.insert(1, { 1, 2, 1, 2 }); 31 | // tree.insert(2, { 0, 1, 0, 1 }); 32 | // tree.insert(2, { 0, 1, 0, 3 }); 33 | auto isect = tree.intersection({0, 0, 50, 50}); 34 | auto isect2 = tree2.intersection({0, 0, 50, 50}); 35 | std::set s, s2; 36 | for (auto &node : isect) 37 | s.insert(node.get().data()); 38 | for (auto &node : isect2) 39 | s2.insert(node.get().data()); 40 | std::cout << s.size() << " " << (s == s2) << std::endl; 41 | for (auto data : s) 42 | std::cout << data << " "; 43 | std::cout << std::endl; 44 | // std::cout << tree.to_string() << std::endl; 45 | } 46 | 47 | /* void test_01_rtree_erase() { 48 | typedef rtree myrtree; 49 | myrtree tree; 50 | tree.insert(1, { 1, 2, 1, 2 }); 51 | tree.insert(2, { 0, 1, 0, 1 }); 52 | tree.insert(3, { 0, 1, 0, 3 }); 53 | std::cout << tree.to_string() << std::endl << std::endl; 54 | tree.erase(1, { 1, 2, 1, 2 }); 55 | tree.erase(2, { 0, 1, 0, 1 }); 56 | std::cout << tree.to_string() << std::endl << std::endl; 57 | tree.insert(1, { 1, 2, 1, 2 }); 58 | tree.insert(2, { 0, 1, 0, 1 }); 59 | std::cout << tree.to_string() << std::endl << std::endl; 60 | } */ 61 | 62 | /* void test_02_node_deleter() { 63 | std::cout << "test_02_node() : "; 64 | typedef double T; 65 | typedef Node node_type; 66 | typename node_type::type_ptr last = nullptr; 67 | last = new node_type({ 5.0, 6.0 }); 68 | last->insert({ 4.0, 3.0 }); 69 | last->insert({ 1.0, 2.0 }); 70 | last->free(); 71 | delete last; 72 | std::cout << "PASSED" << std::endl; 73 | } */ 74 | 75 | static void test_03_sqSegDist() { 76 | std::cout << "test_03_sqSegDist() : "; 77 | 78 | auto a = sqSegDist({ 0, 0 }, { 0, 1 }, { 1, 0 }); 79 | std::cout << "a: " << a << std::endl; 80 | assert(a == 0.5); 81 | 82 | auto b = sqSegDist({ 0, 1 }, { 0, 1 }, { 1, 0 }); 83 | std::cout << "b: " << b << std::endl; 84 | assert(b == 0); 85 | 86 | auto c = sqSegDist({ -1, 0 }, { 0, 0 }, { 0, 1 }); 87 | std::cout << "c: " << c << std::endl; 88 | assert(c == 1); 89 | 90 | auto d = sqSegDist({ -1, -1 }, { 0, 0 }, { 0, 1 }); 91 | std::cout << "d: " << d << std::endl; 92 | assert(d == 2); 93 | 94 | std::cout << "PASSED" << std::endl; 95 | } 96 | 97 | 98 | static void test_04_sqSegSegDist() { 99 | 100 | } 101 | 102 | 103 | static void test_05_CircularList() { 104 | std::cout << "test_05_CircularList() : "; 105 | typedef double T; 106 | typedef Node node_type; 107 | typedef typename node_type::point_type point_type; 108 | auto lst = make_unique>(); 109 | auto a = lst->insert(nullptr, point_type { 1, 2 }); 110 | auto b = a->insert(point_type { 3, 4 }); 111 | auto c = b->insert(point_type { 5, 6 }); 112 | std::cout << a->data().p[0] << " " << 113 | b->data().p[0] << " " << c->data().p[0] << std::endl; 114 | { 115 | std::unique_ptr> taker(std::move(lst)); 116 | } 117 | assert(lst.get() == nullptr); 118 | std::cout << "PASSED" << std::endl; 119 | } 120 | 121 | 122 | static void test_06_priority_queue() { 123 | std::cout << "test_06_priority_queue() : "; 124 | typedef double T; 125 | typedef Node node_type; 126 | typedef std::tuple tuple_type; 127 | std::priority_queue, compare_first> queue; 128 | 129 | queue.push(std::make_tuple(-5.0, node_type({ 1, 2 }))); 130 | queue.push(std::make_tuple(-4.0, node_type({ 3, 4 }))); 131 | queue.push(std::make_tuple(-1.0, node_type({ 5, 6 }))); 132 | queue.push(std::make_tuple(-10.0, node_type({ 7, 8 }))); 133 | queue.push(std::make_tuple(-0.5, node_type({ 9, 10 }))); 134 | 135 | while (!queue.empty()) { 136 | std::cout << std::get<0>(queue.top()) << std::endl; 137 | queue.pop(); 138 | } 139 | 140 | std::cout << "PASSED" << std::endl; 141 | } 142 | 143 | 144 | static void test_07_concaveman() { 145 | std::cout << "test_07_concaveman() : "; 146 | typedef double T; 147 | typedef std::array point_type; 148 | std::vector points { 149 | {0, 0}, 150 | {1, 0}, 151 | {0.25, 0.15}, 152 | {1, 1} 153 | }; 154 | std::vector hull { 155 | 0, 1, 3 156 | }; 157 | auto concave = concaveman(points, hull, 2, 1); 158 | for (auto &p : concave) { 159 | std::cout << p[0] << " " << p[1] << std::endl; 160 | } 161 | std::cout << "PASSED" << std::endl; 162 | } 163 | 164 | 165 | static void test_08_intersects() { 166 | std::cout << "test_08_intersects() : "; 167 | 168 | std::cout << intersects({ 0, 0 }, { 1, 0 }, { 0, 1 }, { 1, 1 }) << std::endl; 169 | std::cout << intersects({ 0, 0 }, { 1, 0 }, { 0, 0 }, { 1, 0 }) << std::endl; 170 | std::cout << intersects({ 0, 0 }, { 1, 0 }, { 0.5, -0.5 }, { 0.5, 0.5 }) << std::endl; 171 | 172 | std::cout << "PASSED" << std::endl; 173 | } 174 | 175 | 176 | static void test_09_orient2d() { 177 | std::cout << "test_09_orient2d() : "; 178 | 179 | std::cout << orient2d({ 0, 0 }, { 1, 0 }, { 0, 1 }) << std::endl; 180 | std::cout << orient2d({ 0, 0 }, { 1, 0 }, { 0, 0 }) << std::endl; 181 | std::cout << orient2d({ 0, 0 }, { 1, 0 }, { 0.5, -0.5 }) << std::endl; 182 | std::cout << orient2d({ 0, 1 }, { 1, 0 }, { 0, 0 }) << std::endl; 183 | 184 | std::cout << "PASSED" << std::endl; 185 | } 186 | 187 | #if 0 188 | 189 | int main() { 190 | // test_00_rtree_basic(); 191 | // test_01_rtree_erase(); 192 | // test_02_node_deleter(); 193 | // test_03_sqSegDist(); 194 | // test_05_CircularList(); 195 | // test_06_priority_queue(); 196 | test_07_concaveman(); 197 | // test_08_intersects(); 198 | // test_09_orient2d(); 199 | } 200 | 201 | #endif 202 | --------------------------------------------------------------------------------