Source code for physdes.generic

r"""
Generic Operations for Physical Design (src\physdes\generic.py)

This code defines a set of generic operations that are commonly used in physical design
calculations. These operations are designed to work with different types of objects,
including simple numbers (scalars) and more complex objects like intervals.

The code contains four main functions: overlap, contain, intersection, and min_dist.
Each of these functions takes two inputs, typically referred to as 'lhs' (left-hand side)
and 'rhs' (right-hand side), and performs a specific operation on them.

The 'overlap' function checks if two objects overlap or are equal. It returns True if they
do overlap, and False otherwise. This function is useful for determining if two physical
entities share some common space or value.

The 'contain' function checks if one object contains another. It returns True if the
left-hand side object contains the right-hand side object, and False otherwise. This can
be used to determine if one physical entity is completely within another.

The 'intersection' function finds the common part between two objects. If the objects are
numbers, it returns the number if they're equal. For more complex objects, it returns the
overlapping region. This is useful for finding where two physical entities meet or share
space.

The 'min_dist' function calculates the minimum Manhattan distance between two objects. For
numbers, it simply calculates the absolute difference. For more complex objects, it uses a
special method to determine the closest points between the objects. This can be used to
find how far apart two physical entities are.

Each of these functions is designed to work with both simple numbers and more complex
objects. They do this by first checking if the input objects have special methods (like
'overlaps', 'contains', 'intersect_with', or 'min_dist_with'). If these methods exist,
the functions use them. If not, they fall back to simpler calculations assuming the inputs
are just numbers.

The code also includes many examples (in the form of doctests) to show how each function
works with different types of inputs. These examples help illustrate the behavior of the
functions and can be used to automatically test that the functions are working correctly.

Overall, this code provides a flexible set of tools for performing common operations in
physical design calculations, capable of working with both simple numbers and more complex
geometric objects.
"""

from typing import Any, Union


[docs] def overlap(lhs: Any, rhs: Any) -> bool: """ The `overlap` function checks if two objects have an overlapping property or are equal. :param lhs: The `lhs` parameter represents the left-hand side object that we want to check for overlap with the `rhs` parameter :param rhs: The parameter `rhs` is the right-hand side of the comparison. It can be any object that supports the `overlaps` method or a scalar value :return: a boolean value. .. svgbob:: :align: center <-- lhs --> <-- rhs --> Examples: >>> overlap(1, 1) True >>> overlap(1, 3) False >>> from physdes.interval import Interval >>> overlap(Interval(1, 2), Interval(2, 3)) True >>> overlap(Interval(1, 2), Interval(3, 4)) False >>> overlap(Interval(1, 2), 2) True >>> overlap(Interval(1, 2), 4) False >>> overlap(2, Interval(2, 3)) True >>> overlap(1, Interval(3, 4)) False >>> overlap(1, Interval(1, 2)) True """ if hasattr(lhs, "overlaps"): result = lhs.overlaps(rhs) elif hasattr(rhs, "overlaps"): result = rhs.overlaps(lhs) else: # assume scalar result = lhs == rhs return bool(result)
[docs] def contain(lhs: Any, rhs: Any) -> bool: """ The `contain` function checks if one object contains another object. :param lhs: The `lhs` parameter represents the left-hand side of the comparison, while the `rhs` parameter represents the right-hand side of the comparison :param rhs: The `rhs` parameter represents the right-hand side of the comparison. It can be any value or object that you want to check if it is contained within the `lhs` object :return: a boolean value. .. svgbob:: :align: center <---- lhs ----> <-- rhs --> Examples: >>> contain(1, 1) True >>> contain(1, 3) False >>> from physdes.interval import Interval >>> contain(Interval(1, 4), Interval(2, 3)) True >>> contain(Interval(1, 2), Interval(3, 4)) False >>> contain(Interval(1, 2), 2) True >>> contain(Interval(1, 2), 4) False >>> contain(2, Interval(2, 3)) False >>> contain(1, Interval(3, 4)) False """ if hasattr(lhs, "contains"): result = lhs.contains(rhs) elif hasattr(rhs, "contains"): result = False else: # assume scalar result = lhs == rhs return bool(result)
[docs] def intersection(lhs: Any, rhs: Any) -> Any: """ The `intersection` function returns the intersection of two objects if they have an `intersect_with` method, otherwise it returns the objects themselves if they are equal. :param lhs: The `lhs` parameter represents the left-hand side of the intersection operation, while the `rhs` parameter represents the right-hand side of the intersection operation :param rhs: The `rhs` parameter is the second input to the `intersection` function. It represents the right-hand side of the intersection operation :return: the intersection of `lhs` and `rhs`. .. svgbob:: :align: center <-- lhs --> <-- rhs --> ---> intersection Examples: >>> print(intersection(1, 1)) 1 >>> from physdes.interval import Interval >>> print(intersection(Interval(1, 2), Interval(2, 3))) [2, 2] >>> print(intersection(Interval(1, 2), 2)) [2, 2] >>> print(intersection(2, Interval(2, 3))) [2, 2] >>> print(intersection(1, Interval(1, 2))) [1, 1] >>> print(intersection(Interval(1, 2), Interval(1, 2))) [1, 2] >>> print(intersection(Interval(1, 2), Interval(2, 3))) [2, 2] >>> print(intersection(Interval(1, 2), 2)) [2, 2] """ if hasattr(lhs, "intersect_with"): return lhs.intersect_with(rhs) elif hasattr(rhs, "intersect_with"): return rhs.intersect_with(lhs) else: # assume scalar assert lhs == rhs return lhs
[docs] def min_dist(lhs: Any, rhs: Any) -> Any: """ The `min_dist` function calculates the minimum Manhattan distance between two objects, using their `min_dist_with` method if available, or by subtracting them if they are scalars. :param lhs: The `lhs` parameter represents the left-hand side value or object that we want to calculate the minimum Manhattan distance with :param rhs: The parameter `rhs` represents the right-hand side value or object that we want to compare with the left-hand side value or object `lhs` :return: the minimum Manhattan distance between `lhs` and `rhs`. .. svgbob:: :align: center <-- lhs --> <-- dist --> <-- rhs --> Examples: >>> min_dist(1, 1) 0 >>> min_dist(1, 3) 2 >>> from physdes.interval import Interval >>> min_dist(Interval(1, 2), Interval(2, 3)) 0 >>> min_dist(Interval(1, 2), Interval(3, 4)) 1 >>> min_dist(Interval(1, 2), 2) 0 >>> min_dist(Interval(1, 2), 4) 2 >>> min_dist(2, Interval(2, 3)) 0 >>> min_dist(1, Interval(3, 4)) 2 >>> min_dist(1, Interval(1, 2)) 0 >>> min_dist(Interval(1, 2), Interval(1, 2)) 0 >>> min_dist(Interval(1, 2), Interval(2, 3)) 0 >>> min_dist(Interval(1, 2), 2) 0 >>> min_dist(2, Interval(2, 3)) 0 """ if hasattr(lhs, "min_dist_with"): return lhs.min_dist_with(rhs) elif hasattr(rhs, "min_dist_with"): return rhs.min_dist_with(lhs) else: # assume scalar return abs(lhs - rhs)
[docs] def nearest(lhs: Any, rhs: Any) -> Any: """ The `nearest` function returns the nearest point on lhs to rhs. :param lhs: The `lhs` parameter represents the object on which to find the nearest point. :param rhs: The `rhs` parameter represents the point to which the nearest point is to be found. :return: the nearest point on lhs to rhs. Examples: >>> nearest(1, 1) 1 >>> nearest(1, 3) 1 >>> from physdes.interval import Interval >>> nearest(Interval(1, 2), 4) 2 >>> nearest(Interval(1, 5), 4) 4 """ if hasattr(lhs, "nearest_to"): return lhs.nearest_to(rhs) else: # assume scalar return lhs
# def min_dist_change(lhs, rhs): # """ # The `min_dist_change` function calculates the minimum Manhattan distance change between two objects. # # :param lhs: The `lhs` parameter represents the left-hand side value or object that you want to # compare # :param rhs: The `rhs` parameter represents the right-hand side value or object that we want to # compare with the `lhs` parameter # :return: The function `min_dist_change` returns the minimum Manhattan distance change between `lhs` and `rhs`. # # Examples: # >>> min_dist_change(1, 1) # 0 # >>> min_dist_change(1, 3) # 2 # >>> min_dist_change(Interval(1, 2), Interval(2, 3)) # 0 # >>> min_dist_change(Interval(1, 2), Interval(3, 4)) # 1 # >>> min_dist_change(Interval(1, 2), 2) # 0 # >>> min_dist_change(Interval(1, 2), 4) # 2 # >>> min_dist_change(2, Interval(2, 3)) # 0 # >>> min_dist_change(1, Interval(3, 4)) # 2 # >>> min_dist_change(1, Interval(1, 2)) # 0 # >>> min_dist_change(Interval(1, 2), Interval(1, 2)) # 0 # >>> min_dist_change(Interval(1, 2), Interval(2, 3)) # 0 # >>> min_dist_change(Interval(1, 2), 2) # 0 # """ # if hasattr(lhs, "min_dist_change_with"): # return lhs.min_dist_change_with(rhs) # elif hasattr(rhs, "min_dist_change_with"): # return rhs.min_dist_change_with(lhs) # else: # assume scalar # return abs(lhs - rhs)
[docs] def measure_of(obj: Any) -> Union[int, Any]: """ The `measure_of` function calculates the measure of an object. :param obj: The `obj` parameter represents the object for which the measure is to be calculated. :return: the measure of the object. Examples: >>> measure_of(1) 1 >>> from physdes.interval import Interval >>> measure_of(Interval(1, 2)) 1 """ if hasattr(obj, "measure"): return obj.measure() else: return 1
[docs] def center(obj: Any) -> Any: """ The `center` function calculates the center of an object. :param obj: The `obj` parameter represents the object for which the center is to be calculated. :return: the center of the object. Examples: >>> center(1) 1 >>> from physdes.interval import Interval >>> center(Interval(1, 3)) 2 """ if hasattr(obj, "get_center"): return obj.get_center() else: return obj # assume scalar
[docs] def lower(obj: Any) -> Any: """ The `lower` function calculates the lower corner of an object. :param obj: The `obj` parameter represents the object for which the lower corner is to be calculated. :return: the lower corner of the object. Examples: >>> lower(1) 1 >>> from physdes.interval import Interval >>> lower(Interval(1, 3)) 1 """ if hasattr(obj, "lower_corner"): return obj.lower_corner() else: return obj # assume scalar
[docs] def upper(obj: Any) -> Any: """ The `upper` function calculates the upper corner of an object. :param obj: The `obj` parameter represents the object for which the upper corner is to be calculated. :return: the upper corner of the object. Examples: >>> upper(1) 1 >>> from physdes.interval import Interval >>> upper(Interval(1, 3)) 3 """ if hasattr(obj, "upper_corner"): return obj.upper_corner() else: return obj # assume scalar
[docs] def displacement(lhs: Any, rhs: Any) -> Any: """ The `displacement` function calculates the displacement between two objects or scalars. :param lhs: The `lhs` parameter represents the left-hand side of the displacement operation. It can be either an object that has a `displace` method or a scalar value :param rhs: The `rhs` parameter represents the displacement value that needs to be subtracted from the `lhs` parameter :return: the displacement between `lhs` and `rhs`. If `lhs` has a `displace` method, it calls that method passing `rhs` as an argument. Otherwise, it assumes `lhs` is a scalar and returns the difference between `lhs` and `rhs`. Examples: >>> displacement(1, 1) 0 >>> displacement(1, 3) -2 >>> from physdes.interval import Interval >>> print(displacement(Interval(1, 2), Interval(2, 3))) [-1, -1] >>> print(displacement(Interval(1, 2), Interval(3, 4))) [-2, -2] >>> from physdes.point import Point >>> print(displacement(Point(1, 2), Point(3, 4))) <-2, -2> """ if hasattr(lhs, "displace"): return lhs.displace(rhs) else: # assume scalar return lhs - rhs
if __name__ == "__main__": import doctest doctest.testmod()