This repository has been archived on 2023-03-25. You can view files and clone it, but cannot push or open issues or pull requests.
mightyscape-1.1-deprecated/extensions/fablabchemnitz/box_maker_elliptical_box/inkscape_helper/Ellipse.py
2021-07-23 02:36:56 +02:00

158 lines
5.5 KiB
Python

from __future__ import division
from math import *
from inkscape_helper.Coordinate import Coordinate
class Ellipse(object):
"""Used as a base class for EllipticArc."""
nr_points = 1024 #used for piecewise linear circumference calculation (ellipse circumference is tricky to calculate)
# approximate circumfere: c = pi * (3 * (a + b) - sqrt(10 * a * b + 3 * (a ** 2 + b ** 2)))
def __init__(self, x_radius, y_radius):
self.y_radius = y_radius
self.x_radius = x_radius
self.distances = [0]
theta = 0
self.angle_step = 2 * pi / self.nr_points
for i in range(self.nr_points):
prev_dist = self.distances[-1]
prev_coord = self.coordinate_at_theta(theta)
theta += self.angle_step
x, y = x_radius * cos(theta), y_radius * sin(theta)
self.distances.append(prev_dist + hypot(prev_coord.x - x, prev_coord.y - y))
@property
def circumference(self):
return self.distances[-1]
def curvature(self, theta):
c = self.coordinate_at_theta(theta)
return (self.x_radius*self.y_radius)/((cos(theta)**2*self.y_radius**2 + sin(theta)**2*self.x_radius**2)**(3/2))
def tangent(self, theta):
angle = self.theta_at_angle(theta)
return Coordinate(cos(angle), sin(angle))
def coordinate_at_theta(self, theta):
"""Coordinate of the point at theta."""
return Coordinate(self.x_radius * cos(theta), self.y_radius * sin(theta))
def dist_from_theta(self, theta_start, theta_end):
"""Distance accross the surface from point at angle theta_end to point at angle theta_end. Measured in positive (CCW) sense."""
#print 'thetas ', theta_start, theta_end # TODO: figure out why are there so many with same start and end?
# make sure thetas are between 0 and 2 * pi
theta_start %= 2 * pi
theta_end %= 2 * pi
i1 = int(theta_start / self.angle_step)
p1 = theta_start % self.angle_step
l1 = self.distances[i1 + 1] - self.distances[i1]
i2 = int(theta_end / self.angle_step)
p2 = theta_end % self.angle_step
l2 = self.distances[i2 + 1] - self.distances[i2]
if theta_start <= theta_end:
len = self.distances[i2] - self.distances[i1] + l2 * p2 - l1 * p1
else:
len = self.circumference + self.distances[i2] - self.distances[i1]
return len
def theta_from_dist(self, theta_start, dist):
"""Returns the angle that you get when starting at theta_start and moving a distance (dist) in CCW direction"""
si = int(theta_start / self.angle_step) % self.nr_points
p = theta_start % self.angle_step
piece_length = self.distances[si + 1] - self.distances[si]
start_dist = self.distances[si] + p * piece_length
end_dist = dist + start_dist
if end_dist > self.circumference: # wrap around zero angle
end_dist -= self.circumference
min_idx = 0
max_idx = self.nr_points
while max_idx - min_idx > 1: # binary search
half_idx = min_idx + (max_idx - min_idx) // 2
if self.distances[half_idx] < end_dist:
min_idx = half_idx
else:
max_idx = half_idx
step_dist = self.distances[max_idx] - self.distances[min_idx]
return (min_idx + (end_dist - self.distances[min_idx]) / step_dist) * self.angle_step
def theta_at_angle(self, angle):
cf = 0
if angle > pi / 2:
cf = pi
if angle > 3 * pi / 2:
cf = 2 * pi
return atan(self.x_radius/self.y_radius * tan(angle)) + cf
def skewTransform(self, l, a2, b2):
x0 = a2**2
x1 = b2**2
x2 = l**2
x3 = x0*x2
x4 = x0 - x1 + x3
x5 = 2*a2*b2
x6 = x0 + x1 + x3
x7 = sqrt((-x5 + x6)*(x5 + x6))
x9 = 1/(x4 - x7)
x10 = x6 - x7
x11 = l*x10
x12 = b2**4
x13 = 4*x12
x14 = x10**2
x15 = 4*x1
x16 = sqrt(-x10*x15 + x13 + x14*x2 + x14)
x17 = 2*atan(x9*(x11 - x16))
x18 = sqrt(2)
x19 = sqrt(x10)
x20 = b2*x18*x19/2
x21 = x0/2
x22 = x1/2
x23 = x2*x21
x24 = x21 - x22 + x23
x25 = x7/2
x27 = 1/(x24 - x25)
x28 = x21 + x22 + x23
x29 = x28 - x25
x30 = l*x29
x31 = x14/4
x32 = 2*x1
x33 = sqrt(x12 + x2*x31 - x29*x32 + x31)
x34 = 2*atan(x27*(x30 - x33))
x35 = x20*sqrt(1/(-x1*cos(x34)**2 + x29))*sin(x34)
x36 = x18/2
x37 = -x19*x36
x39 = 2*atan(x9*(x11 + x16))
x40 = 2*atan(x27*(x30 + x33))
x41 = x20*sqrt(1/(-x1*cos(x40)**2 + x29))*sin(x40)
x42 = 1/(x4 + x7)
x43 = x6 + x7
x44 = l*x43
x45 = x43**2
x46 = sqrt(x13 - x15*x43 + x2*x45 + x45)
x47 = 2*atan(x42*(x44 - x46))
x48 = sqrt(x43)
x49 = b2*x18*x48/2
x50 = 1/(x24 + x25)
x51 = x25 + x28
x52 = l*x51
x53 = x45/4
x54 = sqrt(x12 + x2*x53 - x32*x51 + x53)
x55 = 2*atan(x50*(x52 - x54))
x56 = x49*sqrt(1/(-x1*cos(x55)**2 + x51))*sin(x55)
x57 = -x36*x48
x59 = 2*atan(x42*(x44 + x46))
x60 = 2*atan(x50*(x52 + x54))
x61 = x49*sqrt(1/(-x1*cos(x60)**2 + x51))*sin(x60)
#solutions (alpha, a1, b1)
(x17, -x35, x19*x36)
(x17, x35, x37)
(x39, -x41, x19*x36)
(x39, x41, x37)
(x47, -x56, x36*x48)
(x47, x56, x57)
(x59, -x61, x36*x48)
(x59, x61, x57)