"""Utility functions for working with regions.See Also--------:func:`locan.process.filter.select_by_region`:func:`locan.process.properties.misc.distance_to_region`:func:`locan.process.properties.misc.distance_to_region_boundary`"""from__future__importannotationsfromtypingimportTYPE_CHECKING,Anyimportnumpyasnpimportnumpy.typingasnptfromshapely.opsimportunary_unionfromlocan.data.regions.regionimport(AxisOrientedCuboid,AxisOrientedHypercuboid,AxisOrientedRectangle,EmptyRegion,Interval,LineSegment2D,Region,Region2D,RoiRegion,)fromlocan.dependenciesimportHAS_DEPENDENCYifHAS_DEPENDENCY["open3d"]:passifTYPE_CHECKING:pass__all__:list[str]=["get_region_from_intervals","regions_union","expand_region","surrounding_region",]
[docs]defget_region_from_intervals(intervals:npt.ArrayLike,)->(Interval|LineSegment2D|AxisOrientedRectangle|AxisOrientedCuboid|AxisOrientedHypercuboid):""" Constructor for instantiating an axis-oriented region from list of (min, max) bounds. Parameters ---------- intervals The region bounds for each dimension of shape (dimension, 2). Returns ------- Interval | AxisOrientedRectangle | AxisOrientedCuboid | AxisOrientedHypercuboid """ifnp.shape(intervals)==(2,)ornp.shape(intervals)==(1,2):returnInterval.from_intervals(intervals)elifnp.shape(intervals)==(2,2):returnAxisOrientedRectangle.from_intervals(intervals)elifnp.shape(intervals)==(3,2):returnAxisOrientedCuboid.from_intervals(intervals)elifnp.shape(intervals)[0]>3andnp.shape(intervals)[1]==2:returnAxisOrientedHypercuboid.from_intervals(intervals)else:raiseTypeError("intervals must be of shape (dimension, 2).")
[docs]defregions_union(regions:list[Region])->EmptyRegion|Region2D:""" Return the union of `regions`. Parameters ---------- regions Original region(s) Returns -------- Region """ifall([isinstance(region,Region2D|RoiRegion)forregioninregions]):shapely_objects=[reg.shapely_objectforreginregions]# type: ignore[attr-defined]unified_regions=unary_union(shapely_objects)ifunified_regions.is_empty:returnEmptyRegion()else:returnRegion2D.from_shapely(unified_regions)# type: ignoreelse:raiseNotImplementedError("regions must all be Region2D")
[docs]defexpand_region(region:Region,distance:int|float=100,support:Region|None=None,**kwargs:Any,)->Region:""" Expand a region by `distance`. If region contains a list of regions, the unification of all expanded regions is returned. Parameters ---------- region Original region(s) distance Distance by which the region is expanded orthogonal to its boundary. support A region defining the maximum outer boundary. kwargs Other parameters passed to :func:`shapely.geometry.buffer` for :class:`Region2D` objects. Returns -------- Region """ifdistance==0:expanded_region=regionelse:expanded_region=region.buffer(distance,**kwargs)ifsupportisnotNone:expanded_region=support.intersection(expanded_region)try:returnRegion2D.from_shapely(expanded_region)# type: ignoreexceptAttributeError:returnexpanded_region
[docs]defsurrounding_region(region:Region,distance:int|float=100,support:Region|None=None,**kwargs:Any,)->Region:""" Define surrounding region by extending a region and returning the extended region excluding the input region. If region contains a list of regions, the unification of all extended regions is returned. Parameters ---------- region Original region(s) distance Distance by which the region is extended orthogonal to its boundary. support A region defining the maximum outer boundary. kwargs Other parameters passed to :func:`shapely.geometry.buffer` for :class:`Region2D` objects. Returns -------- Region """extended_region=expand_region(region,distance=distance,support=support,**kwargs)ifisinstance(extended_region,Region2D|RoiRegion):surrounding_region_=extended_region.symmetric_difference(region)returnRegion2D.from_shapely(surrounding_region_)# type: ignoreelse:raiseNotImplementedError("Only 2-dimensional function has been implemented.")