Here's a more generalized solution, based on @cvgmt's approach.
Toy models:
tm1={{-10,-5},{10,-5},{10,5},{-0.75,5},{-0.75,3},{2,3},{2,-3},{-2,-3},{-2,3},{0.75,3},{0.75,5},{-10,5}};
tm2={{-10,-5},{10,-5},{10,5},{0,4.5},{0,3.5},{2,3},{2,-3},{-2,-3},{-2,3},{0,3},{0,5},{-10,5}};
tm3={{-10,-5},{10,-5},{10,5},{0,4.5},{1,3.5},{2,3},{2,-3},{-2,-3},{-2,3},{0,3},{0,5},{-10,5}};
tm4={{-10,-5},{10,-5},{10,5},{0,5},{0,3},{2,3},{2,-3},{-2,-3},{-2,3},{0,3},{0,5},{-10,5}};
tm5={{-10,-5},{10,-5},{10,4.5},{-0.75,4.5},{-0.75,3.5},{2,3.5},{2,-3},{-2,-3},{-2,3},{0.75,3},{0.75,5},{-10,5}};
tm6={{-10,-5},{10,-5},{10,7},{-9,4.5},{-9,3.5},{2,-2},{-2,-3},{-2,3},{9,3.5},{9,4.5},{-10,7}};
tm7={{-10,-5},{10,-5},{10,5},{.5,5},{-1,4},{.5,3},{-1,2},{.5,1},{-1,0},{.5,-1},{2,-1},{2,-3},{-2,-3},{-2,-1},{-.5,-1},{1,0},{-.5,1},{1,2},{-.5,3},{1,4},{-.5,5},{-10,5}};
tm8={{13876.088285207748`,13583.902501240373`},{13901.698801517487`,13628.228394724429`},{13919.968444108963`,13659.692779097706`},{13935.85636305809`,13687.496636971831`},{13963.85636305809`,13735.496636971831`},{13980.172897577286`,13764.05057232827`},{13998.918132781982`,13796.608086518943`},{14040.546700874964`,13868.696582235396`},{14060.004526236118`,13903.382270960472`},{14087.774329972268`,13945.03697647041`},{14106.99999988079`,14057.987787880003`},{14107.`,14051.3012342304`},{14051.902840881348`,13830.91259828031`},{13871.37843067944`,13861.`},{13874.793525728837`,13861.`},{14022.955277819834`,13838.205884293207`},{14115.38800375802`,13998.422609171696`},{14128.616750717163`,14021.82731544599`},{14159.856363117695`,14076.496637016535`},{14187.85636305809`,14124.496636971831`},{14203.167274781636`,14151.290732435882`},{13703.167274781636`,14151.290732435882`}};
tm9={{0,0},{2,0},{11,4},{-1,4},{8,1},{6,7},{7,0},{10,0},{10,6},{0,6}};
tm10={{0,0},{10,0},{10,6},{8,6},{-1,4},{7,2},{3,2},{11,4},{2,6},{0,6}};
tm11={{0,0},{2,0},{11,4},{-1,4},{4,0.5},{6,7},{7,0},{10,0},{10,6},{0,6}};
tms = {tm1, tm2, tm3, tm4, tm5, tm6, tm7, tm8, tm9, tm10, tm11};
First, we go along with @cvgmt go get the reg
:
outerContour[points_]:=Module[{cb,reg,segs,fcs,mc,reg1,mc1,polys,segs1,tal},
cb=CoordinateBounds[points,.5];
reg=RegionDifference[BoundaryMesh[Rectangle@@Transpose@cb],Polygon@points];
Get rid of first four (those are from the rectangle), to get the intersected line segments:
segs=Drop[Sort@MeshCells[reg,1][[All,1]],4];
We turn these segments into an undirected graph; its FindFundamentalCycles
give us a set of polygons which we can merge to get a non-intersecting polygon representation, which is then triangulated:
fcs=FindFundamentalCycles@Graph[UndirectedEdge@@@segs];
mc=MeshCoordinates@reg;
reg1=DiscretizeGraphics@Graphics`PolygonUtils`PolygonUnion[Polygon@#&/@(mc[[#]]&/@fcs[[All,All,1]])];
mc1=MeshCoordinates@reg1;
polys=MeshCells[reg1,2][[All,1]];
FInally, those triangles' segments are counted with Tally
, so those that are only counted once, make up the boundary we are after:
segs1=Sort/@Flatten[Transpose[{#,RotateLeft@#}]&/@polys,1];
Partition[mc1[[Flatten[Pick[tal=Tally@segs1;tal[[All,1]],tal[[All,2]],1],1]]],2]
];
{Graphics@{Thread@Line@Transpose[{#,RotateLeft@#}]},Graphics@{Red,Thread@Line@Transpose[{outerC=outerContour[#];outerC,RotateLeft@outerC}]}}&/@tms
FindFundamentalCycles
seems to do the job, though it's not obvious why it always generates cycles that cover 100% of any Polygon@points
. But it does, in all my toy model cases.
UPD.
Based on @azerbajdzan solution, a bit cleaner:
contourPolygon1[x_] :=
Polygon[#[[1]][[FirstCase[#[[2]], _List, #[[2]], All]]]] &@
CrossingPolygon[x]
Graphics[{EdgeForm[Black], Thread@Line@Transpose[{#, RotateLeft@#}],
FaceForm[None], EdgeForm[{Red}], contourPolygon1[#]}] & /@ tms //
Multicolumn[#] &