# Ákos Halmai, 2023. import math def getArea(points) -> float: """Expects iterable of two-element-tuples. For example: points = ((0, 0), (1, 0), (1, 1), (0, 1), (0, 0))""" area = 0.0 (x1, y1) = points[0] for (x2, y2) in points[1:]: area += x1 * y2 - y1 * x2 (x1, y1) = (x2, y2) return 0.5 * area def getAreaAbs(points) -> float: """Expects iterable of two-element-tuples. For example: points = ((0, 0), (1, 0), (1, 1), (0, 1), (0, 0))""" # Returns float (always): return math.fabs(getArea(points)) def getAreaPrecise(points) -> float: """Expects iterable of two-element-tuples. For example: points = ((0, 0), (1, 0), (1, 1), (0, 1), (0, 0))""" def getPartArea(points): (x1, y1) = points[0] for (x2, y2) in points[1:]: yield x1 * y2 yield -y1 * x2 (x1, y1) = (x2, y2) # Avoids loss of precision by tracking multiple # intermediate partial sums: return 0.5 * math.fsum(getPartArea(points)) def getAreaPreciseAbs(points) -> float: """Expects iterable of two-element-tuples. For example: points = ((0, 0), (1, 0), (1, 1), (0, 1), (0, 0))""" # Returns float (always): return math.fabs(getAreaPrecise(points)) def __isClockwise(*args) -> bool: """Private. Do not use it!""" (x0, y0), (x1, y1), (x2, y2), *_ = args return ((x1 - x0) * (y2 - y1) - (y1 - y0) * (x2 - x1)) > 0.0 def isClockwise(points) -> bool: """Expects iterable of two-element-tuples. For example: points = ((0, 0), (1, 0), (1, 1), (0, 1), (0, 0))""" return __isClockwise(*points) def isConcave(points) -> bool: """Expects iterable of two-element-tuples. For example: points = ((0, 0), (1, 0), (1, 1), (0, 1), (0, 0))""" (x0, y0), (x1, y1), (x2, y2), *_ = points switch = __isClockwise((x0, y0), (x1, y1), (x2, y2)) for (xn, yn) in points[3:]: if switch != __isClockwise((x1, y1), (x2, y2), (xn, yn)): return True # Delayed for performance: ((x0, y0), (x1, y1), (x2, y2)) = ((x1, y1), (x2, y2), (xn, yn)) return False