This is the API documentation of LineDream. This was auto generated from the current release.
from .primitives.Line import Line
from .primitives.BaseShape import BaseShape
from .primitives.Ellipse import Circle, Ellipse, Point, Arc
from .primitives.Rectangle import Rectangle, Square
from .primitives.Rectangle import Rectangle, Square
from .primitives.Group import Group
from .primitives.Text import Text
from .enviornment.Canvas import _Canvas
from .helpers.CircleMath import CircleMath
# from .primitives.TextLine import TextLine
# from .enviornment.Tweaks import Tweaks
Canvas = _Canvas
"""
Canvas Singleton Object. This controls the output 'canvas', and rendering of the draw loop.
### Attributes
- width
- height
"""
# Note, I had to do this Canvas=_Canvas thing so these ^ doc strings would work.
__all__ = [
'Line',
'Circle',
'Ellipse',
'Arc',
'Point',
'Rectangle',
'Square',
'Group',
'CircleMath',
'BaseShape',
'Canvas',
'Text'
]
class Line(BaseShape):
'''This is how you create a line with two or more vertices'''
def __init__(self, vertices:List[Tuple]=None, **kwargs):
super().__init__(**kwargs)
if vertices:
for (x,y) in vertices:
self.add_vertex(x,y,0)
This is how you create a line with two or more vertices
def __init__(self, vertices:List[Tuple]=None, **kwargs):
super().__init__(**kwargs)
if vertices:
for (x,y) in vertices:
self.add_vertex(x,y,0)
class Circle(Ellipse):
def __init__(self,x,y, radius, **kwargs):
super().__init__(x,y, radius, radius, **kwargs)
def scale(self, percent, _percent_y=0):
self.radius_x = self.radius_x * (percent/100)
self.radius_y = self.radius_y * (percent/100)
return self
This is how to create an ellipse
def __init__(self,x,y, radius, **kwargs):
super().__init__(x,y, radius, radius, **kwargs)
This is the init for creating an __init__ :param x: x coord of center :param y: y coord of center :param radius_x: radius x :param radius_y: radius y :param kwargs: style kwargs
.. todo:: THIS NEEDS WORK
def scale(self, percent, _percent_y=0):
self.radius_x = self.radius_x * (percent/100)
self.radius_y = self.radius_y * (percent/100)
return self
class Ellipse(BaseShape):
'''This is how to create an ellipse'''
def __init__(self,x,y, radius_x, radius_y, **kwargs):
'''This is the init for creating an __init__
:param x: x coord of center
:param y: y coord of center
:param radius_x: radius x
:param radius_y: radius y
:param kwargs: style kwargs
.. todo:: THIS NEEDS WORK
'''
super().__init__(**kwargs)
self.x = x
""" x coord of center"""
self.y = y
self.radius_x = radius_x
self.radius_y = radius_y
self.is_circle=True
def add_vertex(self, *coords):
raise Exception("Ellipses do not have vertexes")
def transform(self, x=0, y=0, z=0):
'''
Translate the ellipse by x/y/z
:param x:
:param y:
:param z:
:return:
'''
self.x = self.x + x
self.y = self.y + y
return self
def scale(self, percent_x, percent_y=100, **kwargs):
self.radius_x = self.radius_x * (percent_x/100)
self.radius_y = self.radius_y * (percent_y/100)
return self
@property
def svg_object(self):
return drawsvg.Ellipse(self.x, self.y, self.radius_x, self.radius_y,
fill=self.fill_color, stroke=self.stroke_color,
stroke_width=self.stroke_width, fill_opacity=self.fill_opacity)
This is how to create an ellipse
def __init__(self,x,y, radius_x, radius_y, **kwargs):
'''This is the init for creating an __init__
:param x: x coord of center
:param y: y coord of center
:param radius_x: radius x
:param radius_y: radius y
:param kwargs: style kwargs
.. todo:: THIS NEEDS WORK
'''
super().__init__(**kwargs)
self.x = x
""" x coord of center"""
self.y = y
self.radius_x = radius_x
self.radius_y = radius_y
self.is_circle=True
This is the init for creating an __init__ :param x: x coord of center :param y: y coord of center :param radius_x: radius x :param radius_y: radius y :param kwargs: style kwargs
.. todo:: THIS NEEDS WORK
x coord of center
def add_vertex(self, *coords):
raise Exception("Ellipses do not have vertexes")
Append a set of vertexes to the primitive shape
def transform(self, x=0, y=0, z=0):
'''
Translate the ellipse by x/y/z
:param x:
:param y:
:param z:
:return:
'''
self.x = self.x + x
self.y = self.y + y
return self
Translate the ellipse by x/y/z :param x: :param y: :param z: :return:
def scale(self, percent_x, percent_y=100, **kwargs):
self.radius_x = self.radius_x * (percent_x/100)
self.radius_y = self.radius_y * (percent_y/100)
return self
class Arc(BaseShape):
'''This is how to create an ellipse'''
def __init__(self, x, y, radius, start_angle, end_angle, x_y_start_coords=False, **kwargs):
'''This is the init for creating an __init__
:param x: x coord of center
:param y: y coord of center
:param radius_x: radius x
:param radius_y: radius y
:param x_y_start_coords: If true, this will use the x,y params as where the 0 degree location is - not the center of the arc. For example:
If you specify Arc(10, 15, 3, 0, 270) the arc start will be at (10,15).
:param kwargs: style kwargs
.. todo:: THIS NEEDS WORK
'''
super().__init__(**kwargs)
if x_y_start_coords:
d_x, d_y = CircleMath.distance_to_coords(start_angle, radius)
x = x-d_x
y = y-d_y
self.x = x
self.y = y
self.radius = radius
self.start_angle = start_angle
self.end_angle = end_angle
self.is_arc = True
@property
def start_coords(self):
'''For the give arc, get the x,y coordinates of the start of the arc'''
d_x, d_y = CircleMath.distance_to_coords(self.start_angle, self.radius)
return self.x + d_x, self.y +d_y
@property
def end_coords(self):
'''For the give arc, get the x,y coordinates of the end start of the arc'''
d_x, d_y = CircleMath.distance_to_coords(self.end_angle, self.radius)
return self.x + d_x, self.y + d_y
def scale(self, percent, _percent_y=0, origin=None):
self.radius = self.radius * (percent/100)
return self
def rotate(self, theta, origin=None, axis=None):
self.start_angle +=theta
self.end_angle += theta
return self
@property
def svg_object(self):
return drawsvg.Arc(cx=self.x, cy=self.y, r=self.radius, cw=True,
start_deg=self.start_angle, end_deg=self.end_angle,
fill=self.fill_color, stroke=self.stroke_color,
stroke_width=self.stroke_width, fill_opacity=self.fill_opacity,
# close=shape.close_path
)
This is how to create an ellipse
def __init__(self, x, y, radius, start_angle, end_angle, x_y_start_coords=False, **kwargs):
'''This is the init for creating an __init__
:param x: x coord of center
:param y: y coord of center
:param radius_x: radius x
:param radius_y: radius y
:param x_y_start_coords: If true, this will use the x,y params as where the 0 degree location is - not the center of the arc. For example:
If you specify Arc(10, 15, 3, 0, 270) the arc start will be at (10,15).
:param kwargs: style kwargs
.. todo:: THIS NEEDS WORK
'''
super().__init__(**kwargs)
if x_y_start_coords:
d_x, d_y = CircleMath.distance_to_coords(start_angle, radius)
x = x-d_x
y = y-d_y
self.x = x
self.y = y
self.radius = radius
self.start_angle = start_angle
self.end_angle = end_angle
self.is_arc = True
This is the init for creating an __init__ :param x: x coord of center :param y: y coord of center :param radius_x: radius x :param radius_y: radius y :param x_y_start_coords: If true, this will use the x,y params as where the 0 degree location is - not the center of the arc. For example: If you specify Arc(10, 15, 3, 0, 270) the arc start will be at (10,15). :param kwargs: style kwargs
.. todo:: THIS NEEDS WORK
For the give arc, get the x,y coordinates of the start of the arc
For the give arc, get the x,y coordinates of the end start of the arc
def scale(self, percent, _percent_y=0, origin=None):
self.radius = self.radius * (percent/100)
return self
def rotate(self, theta, origin=None, axis=None):
self.start_angle +=theta
self.end_angle += theta
return self
Rotate the shape by the given angle along the given axis.
:param theta: The angle by which to rotate (in degrees) :type theta: float
:param axis: The axis along which to rotate (defaults to the z-axis) :type axis: np.ndarray or list
class Point(Circle):
def __init__(self,x,y, **kwargs):
super().__init__(x,y, .5, **kwargs)
def scale(self, percent_x=1, _percent_y=1):
raise Exception('Cannot scale a Point')
This is how to create an ellipse
def __init__(self,x,y, **kwargs):
super().__init__(x,y, .5, **kwargs)
This is the init for creating an __init__ :param x: x coord of center :param y: y coord of center :param radius_x: radius x :param radius_y: radius y :param kwargs: style kwargs
.. todo:: THIS NEEDS WORK
def scale(self, percent_x=1, _percent_y=1):
raise Exception('Cannot scale a Point')
class Rectangle(BaseShape):
def __init__(self, c_x, c_y, width, height, **kwargs):
"""
Creates a Rectangle Primitive
Parameters
----------
c_x : int
center of rectangle x coord
c_y: int
center of rectangle y coord
:param width:
:param height:
:param kwargs:
"""
super().__init__(**kwargs)
self.c_x = c_x
self.c_y = c_y
self.width = width
self.height = height
self.close_path = True
# top left
t_l_x = c_x - (width/2)
t_l_y = c_y - (height/2)
# top right
t_r_x = c_x + (width/2)
t_r_y = c_y - (height/2)
# bottom right
b_r_x = c_x + (width / 2)
b_r_y = c_y + (height / 2)
# bottom left
b_l_x = c_x - (width/2)
b_l_y = c_y + (height/2)
# this order is very important.
# It adds the vertex's in a clockwise rotation
self.add_vertex(t_l_x, t_l_y)
self.add_vertex(t_r_x, t_r_y)
self.add_vertex(b_r_x, b_r_y)
self.add_vertex(b_l_x, b_l_y)
All provided primitives inherit from this class. If a user wants to make their own primitive, its recommended to inherit from this class so the new primative is added to the draw queue.
def __init__(self, c_x, c_y, width, height, **kwargs):
"""
Creates a Rectangle Primitive
Parameters
----------
c_x : int
center of rectangle x coord
c_y: int
center of rectangle y coord
:param width:
:param height:
:param kwargs:
"""
super().__init__(**kwargs)
self.c_x = c_x
self.c_y = c_y
self.width = width
self.height = height
self.close_path = True
# top left
t_l_x = c_x - (width/2)
t_l_y = c_y - (height/2)
# top right
t_r_x = c_x + (width/2)
t_r_y = c_y - (height/2)
# bottom right
b_r_x = c_x + (width / 2)
b_r_y = c_y + (height / 2)
# bottom left
b_l_x = c_x - (width/2)
b_l_y = c_y + (height/2)
# this order is very important.
# It adds the vertex's in a clockwise rotation
self.add_vertex(t_l_x, t_l_y)
self.add_vertex(t_r_x, t_r_y)
self.add_vertex(b_r_x, b_r_y)
self.add_vertex(b_l_x, b_l_y)
Creates a Rectangle Primitive
class Square(Rectangle):
def __init__(self, c_x: int, c_y: int, width, **kwargs):
"""
Create a square primitive
:param c_x: center of square x coordinate
:param c_y: center of square y coordinate
:param width: height/width of the square.
:param kwargs: style dict
"""
super().__init__(c_x, c_y, width, width, **kwargs)
All provided primitives inherit from this class. If a user wants to make their own primitive, its recommended to inherit from this class so the new primative is added to the draw queue.
def __init__(self, c_x: int, c_y: int, width, **kwargs):
"""
Create a square primitive
:param c_x: center of square x coordinate
:param c_y: center of square y coordinate
:param width: height/width of the square.
:param kwargs: style dict
"""
super().__init__(c_x, c_y, width, width, **kwargs)
Create a square primitive
:param c_x: center of square x coordinate :param c_y: center of square y coordinate :param width: height/width of the square. :param kwargs: style dict
class Group(BaseShape):
'''This will create a group. It also adds inkscape/axidraw compatible attributes
:param label: The label of the group
:param id: The id of the group
'''
def __init__(self,label=None,id=None, **kwargs):
super().__init__(**kwargs)
self.is_group = True
if label == None:
label = str(uuid.uuid4())
self.label = label
if id == None:
id = label
self.id = id
self.items = []
@property
def svg_label(self):
return f'inkscape:label="{self.label}"'
def add_item(self, item):
'''Add a shape to the group'''
self.items.append(item)
_Canvas.remove_by_id(item.id)
This will create a group. It also adds inkscape/axidraw compatible attributes :param label: The label of the group :param id: The id of the group
def __init__(self,label=None,id=None, **kwargs):
super().__init__(**kwargs)
self.is_group = True
if label == None:
label = str(uuid.uuid4())
self.label = label
if id == None:
id = label
self.id = id
self.items = []
def add_item(self, item):
'''Add a shape to the group'''
self.items.append(item)
_Canvas.remove_by_id(item.id)
Add a shape to the group
class CircleMath:
'''
A class with helper methods for circles
'''
@staticmethod
def distance_to_coords(degrees, distance):
angle_radians = degrees * math.pi / 180
x = math.cos(angle_radians) * distance
y = math.sin(angle_radians) * distance
return x, y
@staticmethod
def rotate(p, origin=(0, 0), degrees=0):
# https://stackoverflow.com/a/58781388/6775715
angle = np.deg2rad(degrees)
R = np.array([[np.cos(angle), -np.sin(angle)],
[np.sin(angle), np.cos(angle)]])
o = np.atleast_2d(origin)
p = np.atleast_2d(p)
rv = np.squeeze((R @ (p.T - o.T) + o.T).T)
#todo: remove this once native numpy arrays are supported
return rv.tolist()
@staticmethod
def degrees_to_radians(degrees):
return (math.radians(degrees / math.pi))
A class with helper methods for circles
@staticmethod
def distance_to_coords(degrees, distance):
angle_radians = degrees * math.pi / 180
x = math.cos(angle_radians) * distance
y = math.sin(angle_radians) * distance
return x, y
@staticmethod
def rotate(p, origin=(0, 0), degrees=0):
# https://stackoverflow.com/a/58781388/6775715
angle = np.deg2rad(degrees)
R = np.array([[np.cos(angle), -np.sin(angle)],
[np.sin(angle), np.cos(angle)]])
o = np.atleast_2d(origin)
p = np.atleast_2d(p)
rv = np.squeeze((R @ (p.T - o.T) + o.T).T)
#todo: remove this once native numpy arrays are supported
return rv.tolist()
@staticmethod
def degrees_to_radians(degrees):
return (math.radians(degrees / math.pi))
class BaseShape(object):
'''All provided primitives inherit from this class. If a user wants to make their own primitive, its recommended to
inherit from this class so the new primative is added to the draw queue.'''
def __init__(self,**kwargs):
self._vertices = np.array([])
self._fill_color='none'
self._stroke_color='black'
self._stroke_width=1
self._close_path = False
self._fill_opacity = 1.0
self.is_circle=False
self.is_arc=False
self.is_group = False
self.is_text = False
self.id = str(uuid.uuid4())
for k,v in kwargs.items():
if hasattr(self, k):
setattr(self, k, v)
else:
sys.stderr.write(f'attr "{k}" does not exist.')
# print(f'attr "{k}" does not exist.')
_Canvas.draw_queue.append(self)
# def __str__(self):
# return f'<{__class__} fill_color: {self.fill_color}>'
#
# def __repr__(self):
# return f'<{__class__} fill_color: {self.fill_color}>'
@property
def fill_color(self):
'''The fill color of the shape'''
return self._fill_color
@fill_color.setter
def fill_color(self, v):
self._fill_color = v
@property
def fill_opacity(self):
'''The fill opacity of the shape'''
return self._fill_opacity
@fill_opacity.setter
def fill_opacity(self, v):
self._fill_opacity = v
@property
def close_path(self):
'''Upon rendering the shape, the last vertex will connect to the first vertex'''
return self._close_path
@close_path.setter
def close_path(self, v:bool):
self._close_path = v
@property
def stroke_color(self):
'''The stroke color of the shape'''
return self._stroke_color
@stroke_color.setter
def stroke_color(self, v):
self._stroke_color = v
@property
def stroke_width(self):
'''The stroke width of the shape'''
return self._stroke_width
@stroke_width.setter
def stroke_width(self, v):
self._stroke_width = v
@property
def vertices(self):
'''The list of vertices'''
return self._vertices
@property
def first_vertex(self):
'''The first vertex of the shape'''
# or do you just raise an exception?
rv = (None, None)
if self.vertices.size > 0:
_rv = self.vertices[0]
rv = (_rv[0], _rv[1])
return rv
# Not sure why this was here, but it was not used so I'm commenting out.
# @property
# def last_vertex(self):
# # or do you just raise an exception?
# rv = (None, None)
# if self.vertices.size > 0:
# _rv = self.vertices[-1]
# rv = (_rv[0], _rv[1])
#
# return rv
# @property
# def length(self):
# x = np.array(xcoordinates)
# y = np.array(ycoordinates)
#
# dist_array = (x[:-1] - x[1:]) ** 2 + (y[:-1] - y[1:]) ** 2
#
# np.sum(np.sqrt(dist_array))
@property
def min_x(self):
'''The smallest x value of all the vertices'''
rv = self.first_vertex[0]
_v = self.vertices.tolist()
for x,y, z, _ in _v:
if x < rv:
rv = x
return rv
@property
def max_x(self):
'''The largest x value of all the vertices'''
rv = self.first_vertex[0]
_v = self.vertices.tolist()
for x,y, z, _ in _v:
if x > rv:
rv = x
return rv
@property
def min_y(self):
'''The smallest y value of all the vertices'''
rv = self.first_vertex[1]
_v = self.vertices.tolist()
for x,y, z, _ in _v:
if y < rv:
rv = y
return rv
@property
def max_y(self):
'''The largest y value of all the vertices'''
rv = self.first_vertex[1]
_v = self.vertices.tolist()
for x,y, z, _ in _v:
if y > rv:
rv = y
return rv
@property
def center(self):
'''The center of the shape based on the min/max x/y'''
x = ((self.max_x - self.min_x)/2) + self.min_x
y = ((self.max_y - self.min_y)/2) + self.min_y
return (x,y)
# def rotate(self, degrees, origin=None):
#
# if not origin:
# x = [p[0] for p in self.vertices]
# y = [p[1] for p in self.vertices]
# origin = (max(x) + min(x)) / 2, (max(y) + min(y)) / 2
#
# self._vertices = CircleMath.rotate(self.vertices, origin=origin, degrees=degrees)
def add_vertex(self, x, y, z=0):
'''Append a set of vertexes to the primitive shape'''
l = self._vertices.tolist()
l.append([x,y,z,1])
self._vertices = np.array(l)
return self
def transform(self, x, y, z=0):
'''Transform the vertices based on x/y coords'''
# THIS IS DEFAULT BEHAVIOR IF IT IS NOT OVERRIDEN IN THE DERIVED CLASS.
# This will work for shapes/objects that user vertex's.. but not for things like Circles
#
# for idx, (o_x,o_y) in enumerate(self._vertices):
# o_x = o_x + x
# o_y = o_y + y
#
# self._vertices[idx] = (o_x, o_y)
# tmat = matrix.translation_matrix(x, y, z)
translate_matrix = np.identity(4)
translate_matrix[0, -1] = x
translate_matrix[1, -1] = y
translate_matrix[2, -1] = z
self._vertices = np.dot(self._vertices, translate_matrix.T)[:, :4]
return self
def rotate(self, theta, origin=None, axis=np.array([0, 0, 1])):
"""Rotate the shape by the given angle along the given axis.
:param theta: The angle by which to rotate (in degrees)
:type theta: float
:param axis: The axis along which to rotate (defaults to the z-axis)
:type axis: np.ndarray or list
"""
#CONVERT DEGREES TO RADIANS
theta = theta * math.pi / 180
axis = np.array(axis[:])
#
# tmat = matrix.rotation_matrix(axis, theta)
#
# NOTE: THIS ALL MIGHT NEED TO BE RADIANS
# This might be the wrong interpretatino...
x = axis[0]
y = axis[1]
z = axis[2]
# x, y, z = _normalize(axis)
s = np.sin(theta)
c = np.cos(theta)
c1 = 1 - c
rotation = np.identity(4)
rotation[0, 0] = x * x * c1 + c
rotation[0, 1] = x * y * c1 - z * s
rotation[0, 2] = x * z * c1 + y * s
rotation[1, 0] = y * x * c1 + z * s
rotation[1, 1] = y * y * c1 + c
rotation[1, 2] = y * z * c1 - x * s
rotation[2, 0] = x * z * c1 - y * s
rotation[2, 1] = y * z * c1 + x * s
rotation[2, 2] = z * z * c1 + c
if origin == None:
x_c,y_c = self.center
else:
x_c = origin[0]
y_c = origin[1]
self.transform(-x_c, -y_c)
rotation[0, -1] = x_c
rotation[1, -1] = y_c
rotation[2, -1] = 1
self._vertices = np.dot(self._vertices, rotation.T)[:, :4]
# self.transform(x_c, y_c)
# self._vertices = self._vertices.dot(rotation)
return self
def rotate_x(self, theta):
"""This is a convenience function to rotate the shape along the x axis.
:param theta: The angle by which to rotate (in degrees)
:type theta: float
:returns: The rotation matrix used to apply the transformation.
:rtype: np.ndarray
"""
self.rotate(theta, axis=np.array([1, 0, 0]))
return self
def rotate_y(self, theta):
"""This is a convenience function to rotate the shape along the y axis.
:param theta: The angle by which to rotate (in degrees)
:type theta: float
:returns: The rotation matrix used to apply the transformation.
:rtype: np.ndarray
"""
self.rotate(theta, axis=np.array([0, 1, 0]))
return self
def rotate_z(self, theta):
"""This is a convenience function to rotate the shape along the z axis.
:param theta:The angle by which to rotate (in degrees)
:type theta: float
:returns: The rotation matrix used to apply the transformation.
:rtype: np.ndarray
"""
self.rotate(theta, axis=np.array([0, 0, 1]))
return self
def scale(self, amt, amt_y=None, origin=None):
sys.stderr.write("Scale is not fully implemented yet.\n")
if amt_y==None:
amt_y = amt
scale_matrix = np.identity(4)
scale_matrix[0, 0] = amt
scale_matrix[1, 1] = amt_y
scale_matrix[2, 2] = 1 # default for z
self._vertices = self._vertices.dot(scale_matrix)
# raise Exception('Inherited class should implement')
return self
@property
def svg_object(self):
if len(self.vertices) > 0:
verts = self.vertices
verts = verts.tolist()
# # verts = vertices.tolist()
if len(verts)== 0 :
print(f'verts contains one item of {[[0.0, 0.0, 0.0]]} ... continuing.')
return None
start_l = verts.pop(0)
start_x = start_l[0]
start_y = start_l[1]
other_verts = []
for o_v in verts:
x = o_v[0]
y = o_v[1]
# z = o_v[2]
other_verts.append(x)
other_verts.append(y)
return drawsvg.Lines(start_x, start_y, *other_verts, fill=self.fill_color, stroke=self.stroke_color,
stroke_width=self.stroke_width, fill_opacity=self.fill_opacity, close=self.close_path)
else:
raise Exception("This does not have vertices or a custom svg_object, so it cannot be rendered.")
All provided primitives inherit from this class. If a user wants to make their own primitive, its recommended to inherit from this class so the new primative is added to the draw queue.
def __init__(self,**kwargs):
self._vertices = np.array([])
self._fill_color='none'
self._stroke_color='black'
self._stroke_width=1
self._close_path = False
self._fill_opacity = 1.0
self.is_circle=False
self.is_arc=False
self.is_group = False
self.is_text = False
self.id = str(uuid.uuid4())
for k,v in kwargs.items():
if hasattr(self, k):
setattr(self, k, v)
else:
sys.stderr.write(f'attr "{k}" does not exist.')
# print(f'attr "{k}" does not exist.')
_Canvas.draw_queue.append(self)
The fill color of the shape
The fill opacity of the shape
Upon rendering the shape, the last vertex will connect to the first vertex
The stroke color of the shape
The stroke width of the shape
The list of vertices
The first vertex of the shape
The smallest x value of all the vertices
The largest x value of all the vertices
The smallest y value of all the vertices
The largest y value of all the vertices
The center of the shape based on the min/max x/y
def add_vertex(self, x, y, z=0):
'''Append a set of vertexes to the primitive shape'''
l = self._vertices.tolist()
l.append([x,y,z,1])
self._vertices = np.array(l)
return self
Append a set of vertexes to the primitive shape
def transform(self, x, y, z=0):
'''Transform the vertices based on x/y coords'''
# THIS IS DEFAULT BEHAVIOR IF IT IS NOT OVERRIDEN IN THE DERIVED CLASS.
# This will work for shapes/objects that user vertex's.. but not for things like Circles
#
# for idx, (o_x,o_y) in enumerate(self._vertices):
# o_x = o_x + x
# o_y = o_y + y
#
# self._vertices[idx] = (o_x, o_y)
# tmat = matrix.translation_matrix(x, y, z)
translate_matrix = np.identity(4)
translate_matrix[0, -1] = x
translate_matrix[1, -1] = y
translate_matrix[2, -1] = z
self._vertices = np.dot(self._vertices, translate_matrix.T)[:, :4]
return self
Transform the vertices based on x/y coords
def rotate(self, theta, origin=None, axis=np.array([0, 0, 1])):
"""Rotate the shape by the given angle along the given axis.
:param theta: The angle by which to rotate (in degrees)
:type theta: float
:param axis: The axis along which to rotate (defaults to the z-axis)
:type axis: np.ndarray or list
"""
#CONVERT DEGREES TO RADIANS
theta = theta * math.pi / 180
axis = np.array(axis[:])
#
# tmat = matrix.rotation_matrix(axis, theta)
#
# NOTE: THIS ALL MIGHT NEED TO BE RADIANS
# This might be the wrong interpretatino...
x = axis[0]
y = axis[1]
z = axis[2]
# x, y, z = _normalize(axis)
s = np.sin(theta)
c = np.cos(theta)
c1 = 1 - c
rotation = np.identity(4)
rotation[0, 0] = x * x * c1 + c
rotation[0, 1] = x * y * c1 - z * s
rotation[0, 2] = x * z * c1 + y * s
rotation[1, 0] = y * x * c1 + z * s
rotation[1, 1] = y * y * c1 + c
rotation[1, 2] = y * z * c1 - x * s
rotation[2, 0] = x * z * c1 - y * s
rotation[2, 1] = y * z * c1 + x * s
rotation[2, 2] = z * z * c1 + c
if origin == None:
x_c,y_c = self.center
else:
x_c = origin[0]
y_c = origin[1]
self.transform(-x_c, -y_c)
rotation[0, -1] = x_c
rotation[1, -1] = y_c
rotation[2, -1] = 1
self._vertices = np.dot(self._vertices, rotation.T)[:, :4]
# self.transform(x_c, y_c)
# self._vertices = self._vertices.dot(rotation)
return self
Rotate the shape by the given angle along the given axis.
:param theta: The angle by which to rotate (in degrees) :type theta: float
:param axis: The axis along which to rotate (defaults to the z-axis) :type axis: np.ndarray or list
def rotate_x(self, theta):
"""This is a convenience function to rotate the shape along the x axis.
:param theta: The angle by which to rotate (in degrees)
:type theta: float
:returns: The rotation matrix used to apply the transformation.
:rtype: np.ndarray
"""
self.rotate(theta, axis=np.array([1, 0, 0]))
return self
This is a convenience function to rotate the shape along the x axis.
:param theta: The angle by which to rotate (in degrees) :type theta: float
:returns: The rotation matrix used to apply the transformation. :rtype: np.ndarray
def rotate_y(self, theta):
"""This is a convenience function to rotate the shape along the y axis.
:param theta: The angle by which to rotate (in degrees)
:type theta: float
:returns: The rotation matrix used to apply the transformation.
:rtype: np.ndarray
"""
self.rotate(theta, axis=np.array([0, 1, 0]))
return self
This is a convenience function to rotate the shape along the y axis.
:param theta: The angle by which to rotate (in degrees) :type theta: float
:returns: The rotation matrix used to apply the transformation. :rtype: np.ndarray
def rotate_z(self, theta):
"""This is a convenience function to rotate the shape along the z axis.
:param theta:The angle by which to rotate (in degrees)
:type theta: float
:returns: The rotation matrix used to apply the transformation.
:rtype: np.ndarray
"""
self.rotate(theta, axis=np.array([0, 0, 1]))
return self
This is a convenience function to rotate the shape along the z axis.
:param theta:The angle by which to rotate (in degrees) :type theta: float
:returns: The rotation matrix used to apply the transformation. :rtype: np.ndarray
def scale(self, amt, amt_y=None, origin=None):
sys.stderr.write("Scale is not fully implemented yet.\n")
if amt_y==None:
amt_y = amt
scale_matrix = np.identity(4)
scale_matrix[0, 0] = amt
scale_matrix[1, 1] = amt_y
scale_matrix[2, 2] = 1 # default for z
self._vertices = self._vertices.dot(scale_matrix)
# raise Exception('Inherited class should implement')
return self
Canvas Singleton Object. This controls the output 'canvas', and rendering of the draw loop.
class Text(BaseShape):
'''This is how to create an ellipse'''
def __init__(self, text, x,y, width=200, font_size=18.0, align="left", line_spacing=1.0, font_name='futural', **kwargs):
''''''
super().__init__(**kwargs)
self.x = x
self.y = y
self.text = text
self.is_text=True
# This is a unique data structure to vpype. The canvas render has a different way to render them.
self.a = text_block(text, width, font_name, font_size, align, 1.0)
self.a.translate(x, y)
self.text_lines = self.a.lines
def add_vertex(self, *coords):
raise Exception("Cannot accept additional vertexes")
def transform(self, *coords):
self.a.translate(*coords)
This is how to create an ellipse
def __init__(self, text, x,y, width=200, font_size=18.0, align="left", line_spacing=1.0, font_name='futural', **kwargs):
''''''
super().__init__(**kwargs)
self.x = x
self.y = y
self.text = text
self.is_text=True
# This is a unique data structure to vpype. The canvas render has a different way to render them.
self.a = text_block(text, width, font_name, font_size, align, 1.0)
self.a.translate(x, y)
self.text_lines = self.a.lines
def add_vertex(self, *coords):
raise Exception("Cannot accept additional vertexes")
Append a set of vertexes to the primitive shape
def transform(self, *coords):
self.a.translate(*coords)
Transform the vertices based on x/y coords