diff --git a/.ci/Datasets/Geometry_Engine/Modify/Fillet.json b/.ci/Datasets/Geometry_Engine/Modify/Fillet.json new file mode 100644 index 000000000..5246a703c --- /dev/null +++ b/.ci/Datasets/Geometry_Engine/Modify/Fillet.json @@ -0,0 +1 @@ +{ "_t" : "BH.oM.Data.Library.Dataset", "SourceInformation" : { "_t" : "BH.oM.Data.Library.Source", "SourceLink" : "", "Title" : "Fillet", "Author" : "Christopher Short", "ItemReference" : "", "Version" : "", "Publisher" : "", "Schema" : "", "Language" : "", "Location" : "", "Copyright" : "", "Contributors" : "", "Confidence" : "High", "BHoM_Guid" : "a1b7b4cf-0b2a-4d04-a0b2-bd9e18956890", "Name" : "" }, "TimeOfCreation" : { "$date" : 1789037741096 }, "Data" : [{ "_t" : "BH.oM.Test.UnitTests.UnitTest", "Method" : { "_t" : "System.Reflection.MethodBase", "TypeName" : "{ \"_t\" : \"System.Type\", \"Name\" : \"BH.Engine.Geometry.Modify, Geometry_Engine, Version=9.0.0.0, Culture=neutral, PublicKeyToken=null\", \"_bhomVersion\" : \"9.3\" }", "MethodName" : "Fillet", "Parameters" : ["{ \"_t\" : \"System.Type\", \"Name\" : \"BH.oM.Geometry.Polyline\", \"_bhomVersion\" : \"9.3\" }", "{ \"_t\" : \"System.Type\", \"Name\" : \"System.Double, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\", \"_bhomVersion\" : \"9.3\" }", "{ \"_t\" : \"System.Type\", \"Name\" : \"System.Double, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\", \"_bhomVersion\" : \"9.3\" }", "{ \"_t\" : \"System.Type\", \"Name\" : \"System.Double, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\", \"_bhomVersion\" : \"9.3\" }"], "_bhomVersion" : "9.3" }, "Data" : [{ "_t" : "BH.oM.Test.UnitTests.TestData", "Inputs" : [{ "_t" : "BH.oM.Geometry.Polyline", "ControlPoints" : [{ "_t" : "BH.oM.Geometry.Point", "X" : -8.6165725230395473, "Y" : 14.647882460889299, "Z" : 0.0 }, { "_t" : "BH.oM.Geometry.Point", "X" : 7.0568169740295179, "Y" : 22.317839023284794, "Z" : 0.0 }, { "_t" : "BH.oM.Geometry.Point", "X" : 16.171837816296637, "Y" : 13.20281818101768, "Z" : 0.0 }] }, 3.2799999999999998, 9.9999999999999995E-07, 9.9999999999999995E-07], "Outputs" : [{ "_t" : "BH.oM.Geometry.PolyCurve", "Curves" : [{ "_t" : "BH.oM.Geometry.Line", "Start" : { "_t" : "BH.oM.Geometry.Point", "X" : -8.6165725230395473, "Y" : 14.647882460889299, "Z" : 0.0 }, "End" : { "_t" : "BH.oM.Geometry.Point", "X" : 4.952423510946157, "Y" : 21.288029456244001, "Z" : 0.0 }, "Infinite" : false }, { "_t" : "BH.oM.Geometry.Arc", "CoordinateSystem" : { "_t" : "BH.oM.Geometry.CoordinateSystem.Cartesian", "X" : { "_t" : "BH.oM.Geometry.Vector", "X" : -0.43955286398082632, "Y" : 0.89821672204777681, "Z" : 0.0 }, "Y" : { "_t" : "BH.oM.Geometry.Vector", "X" : 0.89821672204777681, "Y" : 0.43955286398082632, "Z" : 0.0 }, "Z" : { "_t" : "BH.oM.Geometry.Vector", "X" : 0.0, "Y" : 0.0, "Z" : -0.94594594594594561 }, "Origin" : { "_t" : "BH.oM.Geometry.Point", "X" : 6.3941569048032676, "Y" : 18.341878607927292, "Z" : 0.0 } }, "Radius" : 3.2800000000000007, "StartAngle" : 0.0, "EndAngle" : 1.2404989719656419 }, { "_t" : "BH.oM.Geometry.Line", "Start" : { "_t" : "BH.oM.Geometry.Point", "X" : 8.7134671470951428, "Y" : 20.661188850219169, "Z" : 0.0 }, "End" : { "_t" : "BH.oM.Geometry.Point", "X" : 16.171837816296637, "Y" : 13.20281818101768, "Z" : 0.0 }, "Infinite" : false }] }], "BHoM_Guid" : "56636840-7400-4786-a9bc-a57580d803a4", "Name" : "" }, { "_t" : "BH.oM.Test.UnitTests.TestData", "Inputs" : [{ "_t" : "BH.oM.Geometry.Polyline", "ControlPoints" : [{ "_t" : "BH.oM.Geometry.Point", "X" : -4.5153854936660922, "Y" : -1.2790362342573496, "Z" : 0.0 }, { "_t" : "BH.oM.Geometry.Point", "X" : -6.4920778557001766, "Y" : 9.06952966109756, "Z" : 0.0 }, { "_t" : "BH.oM.Geometry.Point", "X" : 8.817598281622633, "Y" : 6.1238704549291212, "Z" : 0.0 }, { "_t" : "BH.oM.Geometry.Point", "X" : -11.4144294239027, "Y" : 1.0852428654304769, "Z" : 0.0 }, { "_t" : "BH.oM.Geometry.Point", "X" : -4.39910947236997, "Y" : -10.348565895354911, "Z" : 0.0 }, { "_t" : "BH.oM.Geometry.Point", "X" : 7.6160793948960306, "Y" : -2.0929683833302071, "Z" : 0.0 }, { "_t" : "BH.oM.Geometry.Point", "X" : 8.5075288914996374, "Y" : 3.0231765536991864, "Z" : 0.0 }] }, 3.2799999999999998, 9.9999999999999995E-07, 9.9999999999999995E-07], "Outputs" : [{ "_t" : "BH.oM.Geometry.PolyCurve", "Curves" : [{ "_t" : "BH.oM.Geometry.Line", "Start" : { "_t" : "BH.oM.Geometry.Point", "X" : -4.5153854936660922, "Y" : -1.2790362342573496, "Z" : 0.0 }, "End" : { "_t" : "BH.oM.Geometry.Point", "X" : -6.4920778557001766, "Y" : 9.06952966109756, "Z" : 0.0 }, "Infinite" : false }, { "_t" : "BH.oM.Geometry.Line", "Start" : { "_t" : "BH.oM.Geometry.Point", "X" : -6.4920778557001766, "Y" : 9.06952966109756, "Z" : 0.0 }, "End" : { "_t" : "BH.oM.Geometry.Point", "X" : 8.817598281622633, "Y" : 6.1238704549291212, "Z" : 0.0 }, "Infinite" : false }, { "_t" : "BH.oM.Geometry.Line", "Start" : { "_t" : "BH.oM.Geometry.Point", "X" : 8.817598281622633, "Y" : 6.1238704549291212, "Z" : 0.0 }, "End" : { "_t" : "BH.oM.Geometry.Point", "X" : -7.0699244114085475, "Y" : 2.167207715285342, "Z" : 0.0 }, "Infinite" : false }, { "_t" : "BH.oM.Geometry.Arc", "CoordinateSystem" : { "_t" : "BH.oM.Geometry.CoordinateSystem.Cartesian", "X" : { "_t" : "BH.oM.Geometry.Vector", "X" : -0.24166073643897693, "Y" : 0.97036080323958429, "Z" : 0.0 }, "Y" : { "_t" : "BH.oM.Geometry.Vector", "X" : -0.9703608032395844, "Y" : -0.24166073643897695, "Z" : 0.0 }, "Z" : { "_t" : "BH.oM.Geometry.Vector", "X" : 0.0, "Y" : 0.0, "Z" : 0.95346992796453145 }, "Origin" : { "_t" : "BH.oM.Geometry.Point", "X" : -6.2772771958887033, "Y" : -1.0155757193404942, "Z" : 0.0 } }, "Radius" : 3.2799999999999998, "StartAngle" : 0.0, "EndAngle" : 1.8770493552506224 }, { "_t" : "BH.oM.Geometry.Line", "Start" : { "_t" : "BH.oM.Geometry.Point", "X" : -9.0729917560966751, "Y" : -2.7309124495019947, "Z" : 0.0 }, "End" : { "_t" : "BH.oM.Geometry.Point", "X" : -6.2054733868322671, "Y" : -7.404492112115256, "Z" : 0.0 }, "Infinite" : false }, { "_t" : "BH.oM.Geometry.Arc", "CoordinateSystem" : { "_t" : "BH.oM.Geometry.CoordinateSystem.Cartesian", "X" : { "_t" : "BH.oM.Geometry.Vector", "X" : -0.85235200006340595, "Y" : -0.52296851529314048, "Z" : 0.0 }, "Y" : { "_t" : "BH.oM.Geometry.Vector", "X" : 0.52296851529314048, "Y" : -0.85235200006340595, "Z" : 0.0 }, "Z" : { "_t" : "BH.oM.Geometry.Vector", "X" : 0.0, "Y" : 0.0, "Z" : 0.99866470480034186 }, "Origin" : { "_t" : "BH.oM.Geometry.Point", "X" : -3.4097588266242957, "Y" : -5.6891553819537553, "Z" : 0.0 } }, "Radius" : 3.2799999999999998, "StartAngle" : 0.0, "EndAngle" : 1.6224798343074009 }, { "_t" : "BH.oM.Geometry.Line", "Start" : { "_t" : "BH.oM.Geometry.Point", "X" : -1.5522845619228924, "Y" : -8.3925216826928875, "Z" : 0.0 }, "End" : { "_t" : "BH.oM.Geometry.Point", "X" : 6.4790490848229965, "Y" : -2.8742182415416782, "Z" : 0.0 }, "Infinite" : false }, { "_t" : "BH.oM.Geometry.Arc", "CoordinateSystem" : { "_t" : "BH.oM.Geometry.CoordinateSystem.Cartesian", "X" : { "_t" : "BH.oM.Geometry.Vector", "X" : 0.56630312948213501, "Y" : -0.82419704290827212, "Z" : 0.0 }, "Y" : { "_t" : "BH.oM.Geometry.Vector", "X" : 0.82419704290827212, "Y" : 0.56630312948213501, "Z" : 0.0 }, "Z" : { "_t" : "BH.oM.Geometry.Vector", "X" : 0.0, "Y" : 0.0, "Z" : 0.71475402237226937 }, "Origin" : { "_t" : "BH.oM.Geometry.Point", "X" : 4.6215748201215936, "Y" : -0.17085194080254595, "Z" : 0.0 } }, "Radius" : 3.2799999999999998, "StartAngle" : 0.0, "EndAngle" : 0.79627233310391998 }, { "_t" : "BH.oM.Geometry.Line", "Start" : { "_t" : "BH.oM.Geometry.Point", "X" : 7.8528895435875201, "Y" : -0.73388405170948756, "Z" : 0.0 }, "End" : { "_t" : "BH.oM.Geometry.Point", "X" : 8.5075288914996374, "Y" : 3.0231765536991864, "Z" : 0.0 }, "Infinite" : false }] }], "BHoM_Guid" : "7fb58783-5d04-4def-8f86-b8b270bd94d8", "Name" : "" }, { "_t" : "BH.oM.Test.UnitTests.TestData", "Inputs" : [{ "_t" : "BH.oM.Geometry.Polyline", "ControlPoints" : [{ "_t" : "BH.oM.Geometry.Point", "X" : -2.0808069096255757, "Y" : -26.933828106748301, "Z" : 0.0 }, { "_t" : "BH.oM.Geometry.Point", "X" : -11.853598780332099, "Y" : -17.161036236041781, "Z" : 0.0 }, { "_t" : "BH.oM.Geometry.Point", "X" : 10.973892678855524, "Y" : -18.109142910065543, "Z" : 0.0 }, { "_t" : "BH.oM.Geometry.Point", "X" : -14.406193671934549, "Y" : -30.799186085460576, "Z" : 0.0 }, { "_t" : "BH.oM.Geometry.Point", "X" : 13.599418853075187, "Y" : -33.643506107531877, "Z" : 0.0 }, { "_t" : "BH.oM.Geometry.Point", "X" : 6.7438782870571758, "Y" : -25.256408606552402, "Z" : 0.0 }, { "_t" : "BH.oM.Geometry.Point", "X" : -2.0808069096255757, "Y" : -26.933828106748301, "Z" : 0.0 }] }, 3.2799999999999998, 9.9999999999999995E-07, 9.9999999999999995E-07], "Outputs" : [{ "_t" : "BH.oM.Geometry.PolyCurve", "Curves" : [{ "_t" : "BH.oM.Geometry.Line", "Start" : { "_t" : "BH.oM.Geometry.Point", "X" : -3.3078478336026658, "Y" : -25.706787182771212, "Z" : 0.0 }, "End" : { "_t" : "BH.oM.Geometry.Point", "X" : -5.9081979381781631, "Y" : -23.106437078195714, "Z" : 0.0 }, "Infinite" : false }, { "_t" : "BH.oM.Geometry.Arc", "CoordinateSystem" : { "_t" : "BH.oM.Geometry.CoordinateSystem.Cartesian", "X" : { "_t" : "BH.oM.Geometry.Vector", "X" : -0.70710678118654746, "Y" : -0.70710678118654768, "Z" : 0.0 }, "Y" : { "_t" : "BH.oM.Geometry.Vector", "X" : -0.70710678118654768, "Y" : 0.70710678118654746, "Z" : 0.0 }, "Z" : { "_t" : "BH.oM.Geometry.Vector", "X" : 0.0, "Y" : 0.0, "Z" : -0.67715432298275968 }, "Origin" : { "_t" : "BH.oM.Geometry.Point", "X" : -3.588887695886287, "Y" : -20.787126835903837, "Z" : 0.0 } }, "Radius" : 3.2800000000000007, "StartAngle" : 0.0, "EndAngle" : 2.3977041789262676 }, { "_t" : "BH.oM.Geometry.Line", "Start" : { "_t" : "BH.oM.Geometry.Point", "X" : -3.4527750129544224, "Y" : -17.509952239159702, "Z" : 0.0 }, "End" : { "_t" : "BH.oM.Geometry.Point", "X" : -1.7238797583850065, "Y" : -17.581759390307951, "Z" : 0.0 }, "Infinite" : false }, { "_t" : "BH.oM.Geometry.Arc", "CoordinateSystem" : { "_t" : "BH.oM.Geometry.CoordinateSystem.Cartesian", "X" : { "_t" : "BH.oM.Geometry.Vector", "X" : 0.041497769186547885, "Y" : 0.99913859656833404, "Z" : 0.0 }, "Y" : { "_t" : "BH.oM.Geometry.Vector", "X" : 0.99913859656833393, "Y" : -0.041497769186547878, "Z" : 0.0 }, "Z" : { "_t" : "BH.oM.Geometry.Vector", "X" : 0.0, "Y" : 0.0, "Z" : -0.48394509730039648 }, "Origin" : { "_t" : "BH.oM.Geometry.Point", "X" : -1.8599924413168836, "Y" : -20.858933987052087, "Z" : 0.0 } }, "Radius" : 3.2799999999999998, "StartAngle" : 0.0, "EndAngle" : 2.6364353558550579 }, { "_t" : "BH.oM.Geometry.Line", "Start" : { "_t" : "BH.oM.Geometry.Point", "X" : -0.39313184807700985, "Y" : -23.792655173531809, "Z" : 0.0 }, "End" : { "_t" : "BH.oM.Geometry.Point", "X" : -4.2964939121528793, "Y" : -25.744336205569745, "Z" : 0.0 }, "Infinite" : false }, { "_t" : "BH.oM.Geometry.Arc", "CoordinateSystem" : { "_t" : "BH.oM.Geometry.CoordinateSystem.Cartesian", "X" : { "_t" : "BH.oM.Geometry.Vector", "X" : -0.44721359549995926, "Y" : 0.89442719099991519, "Z" : 0.0 }, "Y" : { "_t" : "BH.oM.Geometry.Vector", "X" : -0.89442719099991519, "Y" : -0.44721359549995926, "Z" : 0.0 }, "Z" : { "_t" : "BH.oM.Geometry.Vector", "X" : 0.0, "Y" : 0.0, "Z" : 0.5353001451462408 }, "Origin" : { "_t" : "BH.oM.Geometry.Point", "X" : -2.8296333189130127, "Y" : -28.678057392049467, "Z" : 0.0 } }, "Radius" : 3.2800000000000007, "StartAngle" : 0.0, "EndAngle" : 2.5767296029215183 }, { "_t" : "BH.oM.Geometry.Line", "Start" : { "_t" : "BH.oM.Geometry.Point", "X" : -3.1610534146960596, "Y" : -31.941270642836361, "Z" : 0.0 }, "End" : { "_t" : "BH.oM.Geometry.Point", "X" : 5.7093541442111864, "Y" : -32.84217141053788, "Z" : 0.0 }, "Infinite" : false }, { "_t" : "BH.oM.Geometry.Arc", "CoordinateSystem" : { "_t" : "BH.oM.Geometry.CoordinateSystem.Cartesian", "X" : { "_t" : "BH.oM.Geometry.Vector", "X" : -0.10104271212897675, "Y" : -0.99488208865454042, "Z" : 0.0 }, "Y" : { "_t" : "BH.oM.Geometry.Vector", "X" : 0.99488208865454031, "Y" : -0.10104271212897672, "Z" : 0.0 }, "Z" : { "_t" : "BH.oM.Geometry.Vector", "X" : 0.0, "Y" : 0.0, "Z" : 0.70634759202153163 }, "Origin" : { "_t" : "BH.oM.Geometry.Point", "X" : 6.0407742399942306, "Y" : -29.578958159750982, "Z" : 0.0 } }, "Radius" : 3.2800000000000047, "StartAngle" : 0.0, "EndAngle" : 2.3572675702614969 }, { "_t" : "BH.oM.Geometry.Line", "Start" : { "_t" : "BH.oM.Geometry.Point", "X" : 8.5803377997822778, "Y" : -27.50314098914162, "Z" : 0.0 }, "End" : { "_t" : "BH.oM.Geometry.Point", "X" : 7.9788663953297103, "Y" : -26.767298313481565, "Z" : 0.0 }, "Infinite" : false }, { "_t" : "BH.oM.Geometry.Arc", "CoordinateSystem" : { "_t" : "BH.oM.Geometry.CoordinateSystem.Cartesian", "X" : { "_t" : "BH.oM.Geometry.Vector", "X" : 0.77425718286220968, "Y" : 0.63287108860041541, "Z" : 0.0 }, "Y" : { "_t" : "BH.oM.Geometry.Vector", "X" : -0.63287108860041541, "Y" : 0.77425718286220968, "Z" : 0.0 }, "Z" : { "_t" : "BH.oM.Geometry.Vector", "X" : 0.0, "Y" : 0.0, "Z" : 0.87881937697629819 }, "Origin" : { "_t" : "BH.oM.Geometry.Point", "X" : 5.4393028355416631, "Y" : -28.843115484090927, "Z" : 0.0 } }, "Radius" : 3.2799999999999994, "StartAngle" : 0.0, "EndAngle" : 1.0733822354080471 }, { "_t" : "BH.oM.Geometry.Line", "Start" : { "_t" : "BH.oM.Geometry.Point", "X" : 4.8267988918146996, "Y" : -25.620812127962129, "Z" : 0.0 }, "End" : { "_t" : "BH.oM.Geometry.Point", "X" : -0.37603364758382729, "Y" : -26.609780296608132, "Z" : 0.0 }, "Infinite" : false }, { "_t" : "BH.oM.Geometry.Arc", "CoordinateSystem" : { "_t" : "BH.oM.Geometry.CoordinateSystem.Cartesian", "X" : { "_t" : "BH.oM.Geometry.Vector", "X" : 0.18673900723383036, "Y" : -0.98240955979536537, "Z" : 0.0 }, "Y" : { "_t" : "BH.oM.Geometry.Vector", "X" : -0.98240955979536537, "Y" : -0.18673900723383036, "Z" : 0.0 }, "Z" : { "_t" : "BH.oM.Geometry.Vector", "X" : 0.0, "Y" : 0.0, "Z" : -0.82671287996087883 }, "Origin" : { "_t" : "BH.oM.Geometry.Point", "X" : -0.98853759131079078, "Y" : -23.387476940479335, "Z" : 0.0 } }, "Radius" : 3.2799999999999994, "StartAngle" : 0.0, "EndAngle" : 0.97323987380973198 }] }], "BHoM_Guid" : "65fd7b3c-2c42-4bf2-8740-8f0b90040098", "Name" : "" }, { "_t" : "BH.oM.Test.UnitTests.TestData", "Inputs" : [{ "_t" : "BH.oM.Geometry.Polyline", "ControlPoints" : [{ "_t" : "BH.oM.Geometry.Point", "X" : 26.055736357939953, "Y" : -25.601158502651746, "Z" : 0.0 }, { "_t" : "BH.oM.Geometry.Point", "X" : 33.948010501854164, "Y" : -29.658454365368208, "Z" : 0.0 }, { "_t" : "BH.oM.Geometry.Point", "X" : 41.840284645768371, "Y" : -33.715750228084673, "Z" : 0.0 }] }, 3.2799999999999998, 9.9999999999999995E-07, 9.9999999999999995E-07], "Outputs" : [{ "_t" : "BH.oM.Geometry.PolyCurve", "Curves" : [{ "_t" : "BH.oM.Geometry.Line", "Start" : { "_t" : "BH.oM.Geometry.Point", "X" : 26.055736357939953, "Y" : -25.601158502651746, "Z" : 0.0 }, "End" : { "_t" : "BH.oM.Geometry.Point", "X" : 33.948010501854164, "Y" : -29.658454365368208, "Z" : 0.0 }, "Infinite" : false }, { "_t" : "BH.oM.Geometry.Line", "Start" : { "_t" : "BH.oM.Geometry.Point", "X" : 33.948010501854164, "Y" : -29.658454365368208, "Z" : 0.0 }, "End" : { "_t" : "BH.oM.Geometry.Point", "X" : 41.840284645768371, "Y" : -33.715750228084673, "Z" : 0.0 }, "Infinite" : false }] }], "BHoM_Guid" : "ab706a2b-85e3-41be-817b-6868760c2cbb", "Name" : "" }, { "_t" : "BH.oM.Test.UnitTests.TestData", "Inputs" : [{ "_t" : "BH.oM.Geometry.Polyline", "ControlPoints" : [{ "_t" : "BH.oM.Geometry.Point", "X" : -2.2695742232816443, "Y" : -49.918461094214301, "Z" : 0.0 }, { "_t" : "BH.oM.Geometry.Point", "X" : -1.5573184028592664, "Y" : -41.549455204251352, "Z" : 0.0 }, { "_t" : "BH.oM.Geometry.Point", "X" : -4.1392457518903853, "Y" : -40.303007518512189, "Z" : 0.0 }, { "_t" : "BH.oM.Geometry.Point", "X" : -14.645019103120475, "Y" : -40.481071473617781, "Z" : 0.0 }, { "_t" : "BH.oM.Geometry.Point", "X" : -12.330187686747749, "Y" : -50.096525049319894, "Z" : 0.0 }, { "_t" : "BH.oM.Geometry.Point", "X" : -13.398571417381305, "Y" : -51.253940757506257, "Z" : 0.0 }] }, 3.2799999999999998, 9.9999999999999995E-07, 9.9999999999999995E-07], "Outputs" : [{ "_t" : "BH.oM.Geometry.PolyCurve", "Curves" : [{ "_t" : "BH.oM.Geometry.Line", "Start" : { "_t" : "BH.oM.Geometry.Point", "X" : -2.2695742232816443, "Y" : -49.918461094214301, "Z" : 0.0 }, "End" : { "_t" : "BH.oM.Geometry.Point", "X" : -1.5573184028592664, "Y" : -41.549455204251352, "Z" : 0.0 }, "Infinite" : false }, { "_t" : "BH.oM.Geometry.Line", "Start" : { "_t" : "BH.oM.Geometry.Point", "X" : -1.5573184028592664, "Y" : -41.549455204251352, "Z" : 0.0 }, "End" : { "_t" : "BH.oM.Geometry.Point", "X" : -4.1392457518903853, "Y" : -40.303007518512189, "Z" : 0.0 }, "Infinite" : false }, { "_t" : "BH.oM.Geometry.Line", "Start" : { "_t" : "BH.oM.Geometry.Point", "X" : -4.1392457518903853, "Y" : -40.303007518512189, "Z" : 0.0 }, "End" : { "_t" : "BH.oM.Geometry.Point", "X" : -10.554071235501338, "Y" : -40.411733374166609, "Z" : 0.0 }, "Infinite" : false }, { "_t" : "BH.oM.Geometry.Arc", "CoordinateSystem" : { "_t" : "BH.oM.Geometry.CoordinateSystem.Cartesian", "X" : { "_t" : "BH.oM.Geometry.Vector", "X" : -0.016946718543288081, "Y" : 0.99985639405397342, "Z" : 0.0 }, "Y" : { "_t" : "BH.oM.Geometry.Vector", "X" : -0.99985639405397342, "Y" : -0.016946718543288081, "Z" : 0.0 }, "Z" : { "_t" : "BH.oM.Geometry.Vector", "X" : 0.0, "Y" : 0.0, "Z" : 0.97605046458892153 }, "Origin" : { "_t" : "BH.oM.Geometry.Point", "X" : -10.498485998679353, "Y" : -43.691262346663642, "Z" : 0.0 } }, "Radius" : 3.2800000000000002, "StartAngle" : 0.0, "EndAngle" : 1.790094057706928 }, { "_t" : "BH.oM.Geometry.Line", "Start" : { "_t" : "BH.oM.Geometry.Point", "X" : -13.687379528556864, "Y" : -44.458958937189706, "Z" : 0.0 }, "End" : { "_t" : "BH.oM.Geometry.Point", "X" : -12.330187686747749, "Y" : -50.096525049319894, "Z" : 0.0 }, "Infinite" : false }, { "_t" : "BH.oM.Geometry.Line", "Start" : { "_t" : "BH.oM.Geometry.Point", "X" : -12.330187686747749, "Y" : -50.096525049319894, "Z" : 0.0 }, "End" : { "_t" : "BH.oM.Geometry.Point", "X" : -13.398571417381305, "Y" : -51.253940757506257, "Z" : 0.0 }, "Infinite" : false }] }], "BHoM_Guid" : "08c54009-7f3d-4fa5-9b29-d1a108e69a64", "Name" : "" }, { "_t" : "BH.oM.Test.UnitTests.TestData", "Inputs" : [{ "_t" : "BH.oM.Geometry.Polyline", "ControlPoints" : [{ "_t" : "BH.oM.Geometry.Point", "X" : 26.055736357939953, "Y" : -25.601158502651746, "Z" : 0.0 }, { "_t" : "BH.oM.Geometry.Point", "X" : 33.948010501854164, "Y" : -29.658454365368208, "Z" : 0.0 }, { "_t" : "BH.oM.Geometry.Point", "X" : 41.840284645768371, "Y" : -33.715750228084673, "Z" : 0.0 }] }, 0.0, 9.9999999999999995E-07, 9.9999999999999995E-07], "Outputs" : [{ "_t" : "BH.oM.Geometry.PolyCurve", "Curves" : [{ "_t" : "BH.oM.Geometry.Polyline", "ControlPoints" : [{ "_t" : "BH.oM.Geometry.Point", "X" : 26.055736357939953, "Y" : -25.601158502651746, "Z" : 0.0 }, { "_t" : "BH.oM.Geometry.Point", "X" : 33.948010501854164, "Y" : -29.658454365368208, "Z" : 0.0 }, { "_t" : "BH.oM.Geometry.Point", "X" : 41.840284645768371, "Y" : -33.715750228084673, "Z" : 0.0 }] }] }], "BHoM_Guid" : "353e6eb6-b0dc-4820-820e-7a821d946f85", "Name" : "" }, { "_t" : "BH.oM.Test.UnitTests.TestData", "Inputs" : [{ "_t" : "BH.oM.Geometry.Polyline", "ControlPoints" : [{ "_t" : "BH.oM.Geometry.Point", "X" : 20.680645937491363, "Y" : -48.351578267897722, "Z" : 0.0 }, { "_t" : "BH.oM.Geometry.Point", "X" : 26.055736357939953, "Y" : -30.404316128055978, "Z" : 0.0 }, { "_t" : "BH.oM.Geometry.Point", "X" : 33.948010501854164, "Y" : -34.46161199077244, "Z" : 0.0 }, { "_t" : "BH.oM.Geometry.Point", "X" : 40.716674889177597, "Y" : -47.116480592793778, "Z" : 0.0 }] }, 3.2799999999999998, 9.9999999999999995E-07, 0.61689044238027346], "Outputs" : [{ "_t" : "BH.oM.Geometry.PolyCurve", "Curves" : [{ "_t" : "BH.oM.Geometry.Line", "Start" : { "_t" : "BH.oM.Geometry.Point", "X" : 20.680645937491363, "Y" : -48.351578267897722, "Z" : 0.0 }, "End" : { "_t" : "BH.oM.Geometry.Point", "X" : 24.923566694257172, "Y" : -34.184595928297092, "Z" : 0.0 }, "Infinite" : false }, { "_t" : "BH.oM.Geometry.Arc", "CoordinateSystem" : { "_t" : "BH.oM.Geometry.CoordinateSystem.Cartesian", "X" : { "_t" : "BH.oM.Geometry.Vector", "X" : -0.9579596945638218, "Y" : 0.2869028121005251, "Z" : 0.0 }, "Y" : { "_t" : "BH.oM.Geometry.Vector", "X" : 0.2869028121005251, "Y" : 0.95795969456382191, "Z" : 0.0 }, "Z" : { "_t" : "BH.oM.Geometry.Vector", "X" : 0.0, "Y" : 0.0, "Z" : -0.98314540865002598 }, "Origin" : { "_t" : "BH.oM.Geometry.Point", "X" : 28.06567449242651, "Y" : -35.125637151986815, "Z" : 0.0 } }, "Radius" : 3.2800000000000025, "StartAngle" : 0.0, "EndAngle" : 1.7546557922477781 }, { "_t" : "BH.oM.Geometry.Line", "Start" : { "_t" : "BH.oM.Geometry.Point", "X" : 29.565311781697609, "Y" : -32.208534479706039, "Z" : 0.0 }, "End" : { "_t" : "BH.oM.Geometry.Point", "X" : 33.948010501854164, "Y" : -34.46161199077244, "Z" : 0.0 }, "Infinite" : false }, { "_t" : "BH.oM.Geometry.Line", "Start" : { "_t" : "BH.oM.Geometry.Point", "X" : 33.948010501854164, "Y" : -34.46161199077244, "Z" : 0.0 }, "End" : { "_t" : "BH.oM.Geometry.Point", "X" : 40.716674889177597, "Y" : -47.116480592793778, "Z" : 0.0 }, "Infinite" : false }] }], "BHoM_Guid" : "d9e6f5a1-fc90-4458-b2e2-0561c90f9baa", "Name" : "" }], "BHoM_Guid" : "3faa8887-9326-425b-8597-5441f4feb44e", "Name" : "" }], "BHoM_Guid" : "574fa359-f677-4ba7-b3f2-6a6b402c7228", "Name" : "Fillet", "_bhomVersion" : "9.3" } \ No newline at end of file diff --git a/Geometry_Engine/Modify/Fillet.cs b/Geometry_Engine/Modify/Fillet.cs new file mode 100644 index 000000000..dec22fbdb --- /dev/null +++ b/Geometry_Engine/Modify/Fillet.cs @@ -0,0 +1,322 @@ +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2026, the respective contributors. All rights reserved. + * + * Each contributor holds copyright over their respective contributions. + * The project versioning (Git) records all such contribution source information. + * + * + * The BHoM is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * The BHoM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using BH.oM.Base.Attributes; +using BH.oM.Geometry; +using BH.oM.Quantities.Attributes; + +namespace BH.Engine.Geometry +{ + public static partial class Modify + { + /***************************************************/ + /**** Public Methods ****/ + /***************************************************/ + + [Description("Fillets a Polyline by inserting an arc of exactly the requested radius at each internal vertex.\n" + + "A corner that cannot achieve that radius is left sharp and named in a warning; the end points of an open Polyline stay sharp.")] + [Input("polyline", "Polyline to fillet.")] + [Input("radius", "Fillet radius, which must be greater than zero. A corner either achieves it exactly or is left sharp.", typeof(Length))] + [Input("distTol", "Distance tolerance used for checking point coincidence and segment lengths equal to zero.", typeof(Length))] + [Input("angleTol", "Angle tolerance for treating a joint as running straight through or doubling back on itself.", typeof(Angle))] + [Output("polyCurve", "The filleted curve, as trimmed Lines joined by Arc fillets, or null where there is nothing to fillet.")] + public static PolyCurve Fillet(this Polyline polyline, double radius, + double distTol = Tolerance.Distance, double angleTol = Tolerance.Angle) + { + if (polyline == null) + { + Base.Compute.RecordError("Cannot fillet a null Polyline."); + return null; + } + + List pts = polyline.ControlPoints; + if (pts == null || pts.Count < 2) + { + Base.Compute.RecordError("The Polyline needs at least two control points to be filleted."); + return null; + } + + if (double.IsNaN(radius)) + { + Base.Compute.RecordError("The fillet radius is not a number, so no corner has been rounded."); + return null; + } + + if (radius <= 0) + { + Base.Compute.RecordWarning("The fillet radius is zero or negative, so no corner has been rounded. Polyline returned as a PolyCurve without modification."); + return new PolyCurve { Curves = new List { polyline } }; + } + + bool closed = polyline.IsClosed(distTol); + + List vertexIndices = FilletVertices(pts, closed, distTol); + if (vertexIndices.Count < 3) + { + Base.Compute.RecordError("The Polyline has fewer than three distinct vertices, so it has no corner to round."); + return null; + } + + List vertices = vertexIndices.Select(i => pts[i]).ToList(); + + // A closed polyline carries a segment from its last vertex back to its first; an open one does not. + int segCount = closed ? vertices.Count : vertices.Count - 1; + + List noRoom; + List degenerate; + double[] trim = FilletTrimLengths(vertices, vertexIndices, segCount, closed, radius, distTol, angleTol, out noRoom, out degenerate); + Arc[] arcs = FilletArcs(vertices, vertexIndices, trim, radius, distTol, degenerate); + + WarnOnSkippedCorners(radius, noRoom, degenerate); + + return FilletedCurve(vertices, trim, arcs, segCount, closed, distTol); + } + + /***************************************************/ + /**** Private Methods ****/ + /***************************************************/ + + // The polyline's vertices as control point indices, with coincident points collapsed so that every + // vertex is further than distTol from its neighbours. Indices, so a warning can name the control point. + private static List FilletVertices(IList pts, bool closed, double distTol) + { + List vertices = new List(); + + for (int i = 0; i < pts.Count; i++) + { + // Measured against the last vertex KEPT, or a there-and-back jitter retains two close vertices. + if (vertices.Count == 0 || pts[i].Distance(pts[vertices[vertices.Count - 1]]) > distTol) + vertices.Add(i); + } + + // The loop above never compares against the first vertex, so a closed polyline can still end on one + // that has collapsed onto it. + while (closed && vertices.Count > 1 && pts[vertices[0]].Distance(pts[vertices[vertices.Count - 1]]) <= distTol) + vertices.RemoveAt(vertices.Count - 1); + + return vertices; + } + + /***************************************************/ + + // How far back each corner is cut, and zero where it is left sharp. Every trim buys the full radius, so + // this decides which corners are rounded: those left sharp are reported through noRoom and degenerate. + private static double[] FilletTrimLengths(List vertices, List vertexIndices, int segCount, bool closed, + double radius, double distTol, double angleTol, out List noRoom, out List degenerate) + { + int nVerts = vertices.Count; + + noRoom = new List(); + degenerate = new List(); + + double[] segLengths = new double[segCount]; + for (int s = 0; s < segCount; s++) + { + // The modulo wraps the closing segment of a closed polyline back round to the first vertex. + segLengths[s] = vertices[s].Distance(vertices[(s + 1) % nVerts]); + } + + // What the radius costs at each corner, and zero where the vertex is no candidate for filleting. + double[] required = new double[nVerts]; + + for (int i = 0; i < nVerts; i++) + { + if (!closed && (i == 0 || i == nVerts - 1)) + continue; + + //Vectors go in opposite directions from the corner, so the angle between them is the internal angle of the joint. + Point corner = vertices[i]; + Vector v1 = vertices[(i - 1 + nVerts) % nVerts] - corner; + Vector v2 = vertices[(i + 1) % nVerts] - corner; + + if (v1.Length() < distTol || v2.Length() < distTol) + { + degenerate.Add(vertexIndices[i]); + continue; + } + + double theta = v1.Angle(v2); + + // There is nothing to round on a joint that runs straight through, or that doubles back on itself. + if (theta < angleTol || Math.Abs(Math.PI - theta) < angleTol) + { + degenerate.Add(vertexIndices[i]); + continue; + } + + double cornerTrim = radius / Math.Tan(theta / 2.0); + + // A trim this small cannot be told from no trim at all. + if (cornerTrim <= distTol) + { + degenerate.Add(vertexIndices[i]); + continue; + } + + required[i] = cornerTrim; + } + + // A corner is charged what its neighbours have asked for whether or not those neighbours end up + // filleted, so that the outcome does not depend on which corner happened to be tested first. + double[] trim = new double[nVerts]; + + for (int i = 0; i < nVerts; i++) + { + if (required[i] <= 0) + continue; + + // Segment i - 1 arrives at this vertex and segment i leaves it. + if (FilletSegmentFits(required, segLengths, nVerts, (i - 1 + nVerts) % nVerts, distTol) + && FilletSegmentFits(required, segLengths, nVerts, i, distTol)) + trim[i] = required[i]; + else + noRoom.Add(vertexIndices[i]); + } + + return trim; + } + + /***************************************************/ + + // Whether a segment can carry the trims both of its end corners have asked for and keep a straight + // length between them; the margin is what stops two fillets meeting in a line of no length. + private static bool FilletSegmentFits(double[] required, double[] segLengths, int nVerts, int seg, double distTol) + { + return required[seg] + required[(seg + 1) % nVerts] <= segLengths[seg] - distTol * 2.0; + } + + /***************************************************/ + + // The fillet arc at each vertex, or null where the vertex is not filleted or its arc could not be built. + // Resolving every arc before assembly keeps the result contiguous, a null arc being read as no trim. + private static Arc[] FilletArcs(List vertices, List vertexIndices, double[] trim, double radius, double distTol, List degenerate) + { + int nVerts = vertices.Count; + Arc[] arcs = new Arc[nVerts]; + + for (int i = 0; i < nVerts; i++) + { + if (trim[i] <= 0) + continue; + + arcs[i] = CornerArc(vertices[(i - 1 + nVerts) % nVerts], vertices[i], vertices[(i + 1) % nVerts], trim[i], radius, distTol); + + // An arc that could not be built leaves the corner sharp, so it belongs with the degenerate ones. + if (arcs[i] == null) + degenerate.Add(vertexIndices[i]); + } + + return arcs; + } + + /***************************************************/ + + // The arc rounding a single corner at the requested radius, tangent to both adjacent segments at the + // trim distance from the corner, or null where no valid arc exists. + private static Arc CornerArc(Point prev, Point corner, Point next, double trim, double radius, double distTol) + { + Vector v1 = (prev - corner).Normalise(); + Vector v2 = (next - corner).Normalise(); + + double theta = v1.Angle(v2); + + // The arc sweeps pi - theta. Create.ArcByCentre guards a sweep near pi but not one near zero, and + // CartesianCoordinateSystem throws on parallel vectors, so a doubling-back joint is caught here. + if (Math.PI - theta < Tolerance.Angle) + return null; + + // Offset by the radius asked for rather than by one recovered back out of the trim. Non-zero + // bisector, because the sweep check above holds theta away from pi. + Vector bisector = (v1 + v2).Normalise(); + Point centre = corner + bisector * (radius / Math.Sin(theta / 2.0)); + + return BH.Engine.Geometry.Create.ArcByCentre(centre, corner + v1 * trim, corner + v2 * trim, distTol); + } + + /***************************************************/ + + // Warns for the corners left sharp, by control point number. The two causes are kept apart because too + // short a segment is answered by a smaller radius and a joint with no corner is not. + private static void WarnOnSkippedCorners(double radius, List noRoom, List degenerate) + { + if (noRoom.Count > 0) + Base.Compute.RecordWarning("The fillet radius of " + radius + " could not be achieved at control point(s) " + FilletVertexNumbers(noRoom) + ", where an adjacent segment is too short to carry it, so those corners have been left sharp."); + + if (degenerate.Count > 0) + Base.Compute.RecordWarning("No arc could be inserted at control point(s) " + FilletVertexNumbers(degenerate) + " - the joint runs straight through, doubles back on itself or is otherwise degenerate, so those corners have been left sharp."); + } + + /***************************************************/ + + // Vertices as a readable list, ascending because a vertex can join one of these lists at either the + // trim or the arc stage, and zero based to match the Polyline's ControlPoints. + private static string FilletVertexNumbers(List vertices) + { + return string.Join(", ", vertices.OrderBy(i => i)); + } + + /***************************************************/ + + // The trimmed Lines and corner Arcs as a single PolyCurve, with a closing Line where a closed polyline's + // last curve does not already meet its first. + private static PolyCurve FilletedCurve(List vertices, double[] trim, Arc[] arcs, int segCount, bool closed, double distTol) + { + int nVerts = vertices.Count; + List output = new List(); + + for (int s = 0; s < segCount; s++) + { + int vEnd = (s + 1) % nVerts; + + Point pA = vertices[s]; + Point pB = vertices[vEnd]; + Vector dir = (pB - pA).Normalise(); + + // A corner is only cut back where its arc was built, so a line always runs to something that + // closes it. + double tStart = arcs[s] == null ? 0 : trim[s]; + double tEnd = arcs[vEnd] == null ? 0 : trim[vEnd]; + + output.Add(BH.Engine.Geometry.Create.Line(pA + dir * tStart, pB - dir * tEnd)); + + if (arcs[vEnd] != null) + output.Add(arcs[vEnd]); + } + + if (closed) + { + Point startPoint = output.First().IStartPoint(); + Point endPoint = output.Last().IEndPoint(); + if (startPoint.Distance(endPoint) > distTol) + output.Add(BH.Engine.Geometry.Create.Line(endPoint, startPoint)); + } + + return new PolyCurve { Curves = output }; + } + + /***************************************************/ + } +}