#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""2D geometry objects.
:Date: 2019-12-21
.. module:: geometry
:platform: *nix, Windows
:synopsis: Geometry objects.
.. moduleauthor:: Daniel Weschke <daniel.weschke@directbox.de>
"""
import math
import copy
from mathematics import vector, matrix
[docs]class Direction(vector):
"""Direction in local coordinate system"""
def __init__(self, x=1, y=0, z=0):
super().__init__([x, y, z, 0])
def __str__(self):
return str(self.xyz())
[docs]class Point(vector):
"""Point in local coordinate system"""
def __init__(self, x=0, y=0, z=0):
super().__init__([x, y, z, 1])
def __str__(self):
return str(self.xyz())
[docs]class CS(matrix):
"""Coordinate system
"""
def __init__(self, x=[1, 0, 0], y=[0, 1, 0], z=[0, 0, 1]):
super().__init__([[*x, 0], [*y, 0], [*z, 0], [0, 0, 0, 1]])
def __str__(self):
return '[' + ', '.join([str(i) for i in self.get_coordinates()]) + ']'
[docs] @staticmethod
def x90():
return CS((1, 0, 0), (0, 0, -1), (0, 1, 0))
[docs] @staticmethod
def xm90():
return CS((1, 0, 0), (0, 0, 1), (0, -1, 0))
[docs] @staticmethod
def y90():
return CS((0, 0, 1), (0, 1, 0), (-1, 0, 0))
[docs] @staticmethod
def ym90():
return CS((0, 0, -1), (0, 1, 0), (1, 0, 0))
[docs] def get_coordinates(self):
"""Get coordinates in 3d space"""
return self[:3,:3]
[docs]class World():
"""World-space with world-space coordinates
"""
def __init__(self):
self._cs = CS() # Camera
self._objects = []
self._store_init()
def __iter__(self):
"""Returns the Iterator object"""
return iter(self.objects())
def _store_init(self):
"""Initialize or reset calculated values, because a new object was added.
"""
self._bb = None
self._sd = None
[docs] def cs(self, cs=None):
if cs:
self._cs = cs
return self._cs
[docs] def ch_cs(self, cs):
self._cs = self._cs * cs
return self
[docs] def rotate_x(self, theta):
self._cs.rotate_x(theta)
return self
[docs] def rotate_y(self, theta):
self._cs.rotate_y(theta)
return self
[docs] def rotate_z(self, theta):
self._cs.rotate_z(theta)
return self
[docs] def translate(self, tx, ty, tz):
self._cs.translate(tx, ty, tz)
return self
[docs] def scale(self, sx, sy=None, sz=None):
self._cs.scale(sx, sy, sz)
return self
[docs] def objects(self):
return [copy.deepcopy(i).ch_cs(self._cs) for i in self._objects]
[docs] def add(self, *objects):
self._store_init() # calculated values are not correct anymore
[self._objects.append(i) for i in objects]
return self
[docs] def bounding_box(self):
if self._bb is not None:
return self._bb
xmin = math.inf
ymin = math.inf
zmin = math.inf
xmax = -math.inf
ymax = -math.inf
zmax = -math.inf
for i in self._objects:
xi, yi, zi = map(min, i.xyz())
xs, ys, zs = map(max, i.xyz())
#xmax = x if x > xmax: xmax = x
xmin = xi if xi < xmin else xmin
ymin = yi if yi < ymin else ymin
zmin = zi if zi < zmin else zmin
xmax = xs if xs > xmax else xmax
ymax = ys if ys > ymax else ymax
zmax = zs if zs > zmax else zmax
self._bb = xmin, xmax, ymin, ymax, zmin, zmax
return self._bb
[docs] def space_diagonal(self):
if self._sd is not None:
return self._sd
bb = self.bounding_box()
a, b, c = bb[1]-bb[0], bb[3]-bb[2], bb[5]-bb[4]
return math.sqrt(a**2+b**2+c**2)
[docs] def center(self):
bb = self.bounding_box()
self.ch_cs([[1,0,0,-(bb[1]-bb[0])/2],[0,1,0,-(bb[3]-bb[2])/2],[0,0,1,-(bb[5]-bb[4])/2],[0,0,0,1]])
return self
# TODO: Wireframe(list) or Wireframe(matrix) ?
# list of Points
[docs]class Wireframe():
"""Open and closed wireframe object in local coordinate system
This class create its own points (copy).
"""
def __init__(self, *points, closed=False):
self._points = [copy.copy(i) for i in points]
self.closed = closed
def __str__(self):
return '[' + ', '.join([str(point) for point in self._points]) + ']'
def __iter__(self):
"""Returns the Iterator object"""
return iter(self.points())
[docs] def points(self):
"""Get coordinates in 3d space"""
result = [i for i in self._points]
return result if not self.closed else result + [result[0]]
[docs] def xyz(self):
return zip(*[i.xyz() for i in self.points()])
[docs] def rotate_x(self, theta):
self._points = [point.rotate_x(theta) for point in self._points]
return self
[docs] def rotate_y(self, theta):
self._points = [point.rotate_y(theta) for point in self._points]
return self
[docs] def rotate_z(self, theta):
self._points = [point.rotate_z(theta) for point in self._points]
return self
[docs] def translate(self, tx, ty, tz):
self._points = [point.translate(tx, ty, tz) for point in self._points]
return self
[docs] def scale(self, sx, sy=None, sz=None):
if not sy:
sy = sx
sz = sx
self._points = [point.scale(sx, sy, sz) for point in self._points]
return self
[docs] def ch_cs(self, cs):
self._points = [point.ch_cs(cs) for point in self._points]
return self
[docs]class Line(Wireframe):
"""Line a open wireframe object in local coordinate system"""
def __init__(self, point1=Point(-1, 0, 0), point2=Point(1, 0, 0)):
super().__init__(point1, point2)
[docs]class Polygon(Wireframe):
"""Polygon as closed wireframe object in local coordinate system"""
def __init__(self, *points):
super().__init__(*points, closed=True)
[docs]class Circle(Polygon):
"""Circle a closed wireframe object in local coordinate system"""
def __init__(self, radius=1, n=10):
points = []
for i in range(n):
x = radius * math.sin(i*2*math.pi/n)
y = radius * math.cos(i*2*math.pi/n)
points.append(Point(x, y, 0))
super().__init__(*points)