#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""2D geometry objects.
:Date: 2019-03-21
.. module:: geometry
:platform: *nix, Windows
:synopsis: Geometry objects.
.. moduleauthor:: Daniel Weschke <daniel.weschke@directbox.de>
"""
import math
import numpy as np
[docs]def translate(vec, *pts):
"""Translate a point or polygon by a given vector.
:param vec: translation vector
:type vec: tuple
:param `*pts`: points to translate
:returns: (point_x, point_y) or (point1, point2, ...)
:rtype: tuple
"""
vx, vy = vec
return tuple([(x+vx, y+vy) for (x, y) in pts])
[docs]def rotate(origin, angle, *pts, **kwargs):
"""Rotate a point or polygon counterclockwise by a given angle around a given
origin. The angle should be given in radians.
:param origin: the center of rotation
:type origin: tuple
:param angle: the rotation angle
:type angle: int or float
:param `*pts`: points to rotate
:param `**kwargs`: options
:returns: (point_x, point_y) or (point1, point2, ...)
:rtype: tuple
"""
ox, oy = origin
# add first point to the end
if kwargs is not None and "closed" in kwargs and kwargs["closed"] is True:
pts += (pts[0],)
result = tuple([(ox + math.cos(angle) * (px - ox) - math.sin(angle) * (py - oy),
oy + math.sin(angle) * (px - ox) + math.cos(angle) * (py - oy))
for (px, py) in pts])
if len(pts) == 1:
return result[0][0], result[0][1]
return result
[docs]def rotate_deg(origin, angle, *pts, **kwargs):
"""Rotate a point or polygon counterclockwise by a given angle around a given
origin. The angle should be given in degrees.
:param origin: the center of rotation
:type origin: tuple
:param angle: the rotation angle
:type angle: int or float
:param `*pts`: points to rotate
:param `**kwargs`: options
:returns: (point_x, point_y) or (point1, point2, ...)
:rtype: tuple
.. seealso::
:meth:`rotate`
"""
return rotate(origin, angle*math.pi/180, *pts, **kwargs)
[docs]def rectangle(width, height):
"""\
:param width: the width of the rectangle
:type width: int or float
:param height: the height of the rectangle
:type height: int or float
:returns: (point1, point2, point3, point4)
:rtype: tuple
"""
pt1 = (-width/2, -height/2)
pt2 = (width/2, -height/2)
pt3 = (width/2, height/2)
pt4 = (-width/2, height/2)
return pt1, pt2, pt3, pt4, pt1
[docs]def square(width):
"""\
:param width: the edge size of the square
:type width: int or float
:returns: (point1, point2, point3, point4)
:rtype: tuple
.. seealso::
:meth:`rectangle`
"""
return rectangle(width, width)
#
# matplotlib format, return lists for x and y
#
[docs]def points(*pts):
"""\
:param `*pts`: points to rearrange
:returns: ((point1_x, point2_x), (point1_y, point2_y), ...)
:rtype: tuple
"""
return zip(*pts)
[docs]def line(point1, point2, samples=2):
"""\
.. math::
y = \\frac{y_2-y_1}{x_2-x_1}(x-x_1) + y_1
:param point1: one end point
:type point1: tuple
:param point2: other end point
:type point2: tuple
:param samples: number of sampling points
:type samples: int
:returns: ((point1_x, point2_x), (points1_y, point2_y)) or
([sample_point1_x, sample_point2_x, ...],
[sample_points1_y, sample_point2_y, ...])
:rtype: tuple
"""
p1x, p1y = point1
p2x, p2y = point2
denominator = (p1x - p2x)
if samples > 2 and denominator > 0:
x = np.linspace(p1x, p2x)
a = (p1y - p2y) / denominator
b = (p1x*p2y - p2x*p1y) / denominator
y = a*x + b
return x, y
return (p1x, p2x), (p1y, p2y) # matplotlib format
[docs]def cubic(point1, angle1, point2, angle2, samples=50):
"""\
:param point1: one end point
:type point1: tuple
:param angle1: the slope at the one end point
:type angle1: int or float
:param point2: other end point
:type point2: tuple
:param angle2: the slope at the other end point
:type angle2: int or float
:param samples: number of sampling points
:type samples: int
:returns: ([sample_point1_x, sample_point2_x, ...],
[sample_points1_y, sample_point2_y, ...])
:rtype: tuple
"""
p1x, p1y = point1
p2x, p2y = point2
x = np.linspace(p1x, p2x, num=samples)
p1ys = math.tan(angle1)
p2ys = math.tan(angle2)
a = (p1x*p1ys + p1x*p2ys - p2x*p1ys - p2x*p2ys - 2*p1y + 2*p2y)/(p1x**3 - 3*p1x**2*p2x + 3*p1x*p2x**2 - p2x**3)
b = (- p1x**2*p1ys - 2*p1x**2*p2ys - p1x*p2x*p1ys + p1x*p2x*p2ys + 3*p1x*p1y - 3*p1x*p2y + 2*p2x**2*p1ys + p2x**2*p2ys + 3*p2x*p1y - 3*p2x*p2y)/(p1x**3 - 3*p1x**2*p2x + 3*p1x*p2x**2 - p2x**3)
c = (p1x**3*p2ys + 2*p1x**2*p2x*p1ys + p1x**2*p2x*p2ys - p1x*p2x**2*p1ys - 2*p1x*p2x**2*p2ys - 6*p1x*p2x*p1y + 6*p1x*p2x*p2y - p2x**3*p1ys)/(p1x**3 - 3*p1x**2*p2x + 3*p1x*p2x**2 - p2x**3)
d = (- p1x**3*p2x*p2ys + p1x**3*p2y - p1x**2*p2x**2*p1ys + p1x**2*p2x**2*p2ys - 3*p1x**2*p2x*p2y + p1x*p2x**3*p1ys + 3*p1x*p2x**2*p1y - p2x**3*p1y)/(p1x**3 - 3*p1x**2*p2x + 3*p1x*p2x**2 - p2x**3)
y = a*x**3 + b*x**2 + c*x + d
return x, y
[docs]def cubic_deg(point1, angle1, point2, angle2):
"""\
:param point1: one end point
:type point1: tuple
:param angle1: the slope at the one end point
:type angle1: int or float
:param point2: other end point
:type point2: tuple
:param angle2: the slope at the other end point
:type angle2: int or float
:returns: ([sample_point1_x, sample_point2_x, ...],
[sample_points1_y, sample_point2_y, ...])
:rtype: tuple
.. seealso::
:meth:`cubic`
"""
return cubic(point1, angle1 * math.pi/180, point2, angle2 * math.pi/180)