Source code for dtcc_model.geometry.bounds
# Copyright(C) 2023 Anders Logg
# Licensed under the MIT License
from dataclasses import dataclass
from typing import Union
import numpy as np
from dtcc_model.model import Model
from dtcc_model import dtcc_pb2 as proto
[docs]
@dataclass
class Bounds(Model):
"""Represents the boundaries of a rectangular region in the xy plane) with
optional extension along the z-axis (depth)
Attributes
----------
xmin : float
Minimum x-coordinate.
ymin : float
Minimum y-coordinate.
xmax : float
Maximum x-coordinate.
ymax : float
Maximum y-coordinate.
zmin: float
Minimum z-coordinate.
zmax: float
Maximum z-coordinate.
"""
xmin: float = 0.0
ymin: float = 0.0
xmax: float = 0.0
ymax: float = 0.0
zmin: float = 0.0
zmax: float = 0.0
[docs]
def __str__(self):
"""Returns a formatted string representation of the bounds."""
return f"DTCC Bounds {self.bndstr}"
[docs]
def calculate_bounds(self):
"""Calculate the bounds of the object."""
return self
@property
def bndstr(self) -> str:
"""Returns the bounds as a formatted string.
Returns
-------
str
Formatted string of bounds.
"""
return f"[{self.xmin}, {self.xmax}] x [{self.ymin}, {self.ymax}] x [{self.zmin}, {self.zmax}]"
@property
def width(self) -> float:
"""Returns the width of the bounds (x-axis).
Returns
-------
float
Width of the bounds.
"""
return self.xmax - self.xmin
@property
def height(self) -> float:
"""Returns the height of the bounds (y-axis).
Returns
-------
float
Height of the bounds.
"""
return self.ymax - self.ymin
@property
def depth(self) -> float:
"""Returns the depth of the bounds (z-axis).
Returns
-------
float
Depth of the bounds.
"""
return self.zmax - self.zmin
@property
def north(self) -> float:
"""Returns the northernmost coordinate.
Returns
-------
float
Northernmost y-coordinate.
"""
return self.ymax
@property
def south(self) -> float:
"""Returns the southernmost coordinate.
Returns
-------
float
Southernmost y-coordinate.
"""
return self.ymin
@property
def east(self) -> float:
"""Returns the easternmost coordinate.
Returns
-------
float
Easternmost x-coordinate.
"""
return self.xmax
@property
def west(self) -> float:
"""Returns the westernmost coordinate.
Returns
-------
float
Westernmost x-coordinate.
"""
return self.xmin
@property
def tuple(self) -> tuple:
"""Returns the bounds as a tuple.
Returns
-------
tuple
Tuple representation of bounds.
"""
return (self.xmin, self.ymin, self.xmax, self.ymax)
@property
def area(self) -> float:
"""Returns the area enclosed by the bounds.
Returns
-------
float
Area of the bounds.
"""
return self.width * self.height
@property
def volume(self) -> float:
"""Returns the volume enclosed by the bounds.
Returns
-------
float
Volume of the bounds.
"""
return self.width * self.height * self.depth
# FIXME: How to handle z-axis?
@property
def center(self) -> tuple:
"""Returns the center point of the bounds.
Returns
-------
tuple
Center point as (x, y).
"""
return (self.xmin + self.width / 2, self.ymin + self.height / 2)
# FIXME: How to handle z-axis?
[docs]
def buffer(self, distance: float):
"""Increases the size of the bounds by a specified distance.
Parameters
----------
distance : float
The distance to expand the bounds by.
"""
self.xmin -= distance
self.ymin -= distance
self.xmax += distance
self.ymax += distance
return self
# FIXME: How to handle z-axis?
[docs]
def union(self, other):
"""Merges this bounds with another, taking the outermost bounds.
Parameters
----------
other : Bounds
The other bounds to merge with.
"""
self.xmin = min(self.xmin, other.xmin)
self.ymin = min(self.ymin, other.ymin)
self.xmax = max(self.xmax, other.xmax)
self.ymax = max(self.ymax, other.ymax)
return self
# FIXME: How to handle z-axis?
[docs]
def intersect(self, other):
"""Modifies this bounds to be the intersection with another.
Parameters
----------
other : Bounds
The other bounds to intersect with.
"""
self.xmin = max(self.xmin, other.xmin)
self.ymin = max(self.ymin, other.ymin)
self.xmax = min(self.xmax, other.xmax)
self.ymax = min(self.ymax, other.ymax)
return self
[docs]
def contains(self, x: float, y: float, z: float = 0.0, ignore_z:bool = True) -> bool:
"""Check if a point is inside the bounds.
Parameters
----------
x : float
The x-coordinate of the point.
y : float
The y-coordinate of the point.
z : float, optional
The z-coordinate of the point, by default 0.0.
Returns
-------
bool
True if the point is inside the bounds, False otherwise.
"""
if ignore_z:
return self.xmin <= x <= self.xmax and self.ymin <= y <= self.ymax
else:
return self.xmin <= x <= self.xmax and self.ymin <= y <= self.ymax and self.zmin <= z <= self.zmax
[docs]
def contains_bounds(self, other: "Bounds", ignore_z:bool = True) -> bool:
"""Check if another bounds is inside this bounds.
Parameters
----------
other : Bounds
The other bounds to check.
Returns
-------
bool
True if the other bounds is inside this bounds, False otherwise.
"""
if ignore_z:
return self.xmin <= other.xmin and self.xmax >= other.xmax and self.ymin <= other.ymin and self.ymax >= other.ymax
else:
return self.xmin <= other.xmin and self.xmax >= other.xmax and self.ymin <= other.ymin and self.ymax >= other.ymax and self.zmin <= other.zmin and self.zmax >= other.zmax
[docs]
def to_proto(self) -> proto.Bounds:
"""Return a protobuf representation of the Bounds.
Returns
-------
proto.Bounds
A protobuf representation of the Bounds.
"""
pb = proto.Bounds()
pb.xmin = self.xmin
pb.xmax = self.xmax
pb.ymin = self.ymin
pb.ymax = self.ymax
pb.zmin = self.zmin
pb.zmax = self.zmax
return pb
[docs]
def from_proto(self, pb: Union[proto.Bounds, bytes]):
"""Initialize Bounds from a protobuf representation.
Parameters
----------
pb: Union[proto.Bounds, bytes]
The protobuf message or its serialized bytes representation.
"""
if isinstance(pb, bytes):
pb = proto.Bounds.FromString(pb)
self.xmin = pb.xmin
self.xmax = pb.xmax
self.ymin = pb.ymin
self.ymax = pb.ymax
self.zmin = pb.zmin
self.zmax = pb.zmax