282 lines
9.1 KiB
Python
282 lines
9.1 KiB
Python
import math
|
|
from .pyside import QtGui, QtCore
|
|
from .viewport import ViewportMapper, to_screenspace_coords
|
|
|
|
|
|
def get_default_path(options):
|
|
width = options['shape.width']
|
|
height = options['shape.height']
|
|
return [
|
|
{
|
|
'point': [0, 0],
|
|
'tangent_in': None,
|
|
'tangent_out': None,
|
|
},
|
|
{
|
|
'point': [width, 0],
|
|
'tangent_in': None,
|
|
'tangent_out': None,
|
|
},
|
|
{
|
|
'point': [width, height],
|
|
'tangent_in': None,
|
|
'tangent_out': None,
|
|
},
|
|
{
|
|
'point': [0, height],
|
|
'tangent_in': None,
|
|
'tangent_out': None,
|
|
},
|
|
]
|
|
|
|
|
|
def offset_path(path, offset, selection=None):
|
|
for i in selection or range(len(path)):
|
|
path[i]['point'][0] += offset.x()
|
|
path[i]['point'][1] += offset.y()
|
|
point = path[i]['tangent_in']
|
|
if point:
|
|
point[0] += offset.x()
|
|
point[1] += offset.y()
|
|
point = path[i]['tangent_out']
|
|
if point:
|
|
point[0] += offset.x()
|
|
point[1] += offset.y()
|
|
|
|
|
|
def auto_tangent(point, previous_point, next_point):
|
|
in_middle = (
|
|
point[0] + previous_point[0]) / 2, (point[1] + previous_point[1]) / 2
|
|
out_middle = (point[0] + next_point[0]) / 2, (point[1] + next_point[1]) / 2
|
|
in_opposite = [2 * point[0] - in_middle[0], 2 * point[1] - in_middle[1]]
|
|
out_opposite = [2 * point[0] - out_middle[0], 2 * point[1] - out_middle[1]]
|
|
return [
|
|
[
|
|
(in_middle[0] + out_opposite[0]) / 2,
|
|
(in_middle[1] + out_opposite[1]) / 2
|
|
],
|
|
[
|
|
(in_opposite[0] + out_middle[0]) / 2,
|
|
(in_opposite[1] + out_middle[1]) / 2
|
|
]]
|
|
|
|
|
|
def offset_tangent(
|
|
start_tangent_pos, end_tangent_pos, center_pos, offset, lock=True):
|
|
start_vector = [
|
|
start_tangent_pos[0] - center_pos[0],
|
|
start_tangent_pos[1] - center_pos[1]]
|
|
|
|
new_start_pos = [
|
|
center_pos[0] + start_vector[0] + offset[0],
|
|
center_pos[1] + start_vector[1] + offset[1]]
|
|
|
|
if not lock:
|
|
return new_start_pos, end_tangent_pos
|
|
|
|
opposite_vector = [
|
|
end_tangent_pos[0] - center_pos[0],
|
|
end_tangent_pos[1] - center_pos[1]]
|
|
|
|
opposite_length = math.sqrt(
|
|
opposite_vector[0] ** 2 + opposite_vector[1] ** 2)
|
|
opposite_angle = math.atan2(opposite_vector[1], opposite_vector[0])
|
|
|
|
new_start_vector = [
|
|
new_start_pos[0] - center_pos[0],
|
|
new_start_pos[1] - center_pos[1]]
|
|
|
|
angle_delta = math.atan2(
|
|
new_start_vector[1],
|
|
new_start_vector[0]) - math.atan2(start_vector[1], start_vector[0])
|
|
new_opposite_angle = opposite_angle + angle_delta
|
|
|
|
new_end_pos = [
|
|
center_pos[0] + opposite_length * math.cos(new_opposite_angle),
|
|
center_pos[1] + opposite_length * math.sin(new_opposite_angle)]
|
|
|
|
return new_start_pos, new_end_pos
|
|
|
|
|
|
def get_path(path):
|
|
painter_path = QtGui.QPainterPath()
|
|
painter_path.moveTo(QtCore.QPointF(*path[0]['point']))
|
|
for i in range(len(path)):
|
|
point = path[i]
|
|
point2 = path[i + 1 if i + 1 < len(path) else 0]
|
|
c1 = QtCore.QPointF(*(point['tangent_out'] or point['point']))
|
|
c2 = QtCore.QPointF(*(point2['tangent_in'] or point2['point']))
|
|
end = QtCore.QPointF(*point2['point'])
|
|
painter_path.cubicTo(c1, c2, end)
|
|
return painter_path
|
|
|
|
|
|
def get_absolute_path(reference_point, relative_path):
|
|
absolute_path = []
|
|
for point in relative_path:
|
|
tin = [
|
|
reference_point[0] + point['tangent_in'][0],
|
|
reference_point[1] + point['tangent_in'][1]
|
|
] if point['tangent_in'] else None
|
|
to = [
|
|
reference_point[0] + point['tangent_out'][0],
|
|
reference_point[1] + point['tangent_out'][1]
|
|
] if point['tangent_out'] else None
|
|
|
|
center = [
|
|
reference_point[0] + point['point'][0],
|
|
reference_point[1] + point['point'][1]]
|
|
absolute_path.append(
|
|
{'point': center, 'tangent_in': tin, 'tangent_out': to})
|
|
return absolute_path
|
|
|
|
|
|
def get_relative_path(reference_point, absolute_path):
|
|
relative_path = []
|
|
for point in absolute_path:
|
|
tin = [
|
|
point['tangent_in'][0] - reference_point[0],
|
|
point['tangent_in'][1] - reference_point[1]
|
|
] if point['tangent_in'] else None
|
|
|
|
to = [
|
|
point['tangent_out'][0] - reference_point[0],
|
|
point['tangent_out'][1] - reference_point[1]
|
|
] if point['tangent_out'] else None
|
|
|
|
point = [
|
|
point['point'][0] - reference_point[0],
|
|
point['point'][1] - reference_point[1]]
|
|
|
|
relative_path.append(
|
|
{'point': point, 'tangent_in': tin, 'tangent_out': to})
|
|
|
|
return relative_path
|
|
|
|
|
|
def get_shape_painter_path(shape, viewportmapper=None):
|
|
if not shape.options['shape.path']:
|
|
return
|
|
|
|
left, top = shape.options['shape.left'], shape.options['shape.top']
|
|
path = get_absolute_path((left, top), shape.options['shape.path'])
|
|
return get_worldspace_qpath(path, viewportmapper)
|
|
|
|
|
|
def get_shape_space_painter_path(
|
|
shape, force_world_space=True, viewportmapper=None):
|
|
path = get_absolute_path(shape)
|
|
if shape.options['shape.space'] == 'world' or force_world_space:
|
|
return get_worldspace_qpath(path, viewportmapper)
|
|
return get_screenspace_qpath(
|
|
path=path,
|
|
anchor=shape.options['shape.anchor'],
|
|
viewport_size=viewportmapper.viewsize)
|
|
|
|
|
|
def get_screenspace_qpath(path, point, anchor, viewport_size):
|
|
if not path:
|
|
return QtGui.QPainterPath()
|
|
|
|
def add(p1, p2):
|
|
return p1[0] + p2[0], p1[1] + p2[1]
|
|
|
|
painter_path = QtGui.QPainterPath()
|
|
start = QtCore.QPointF(*add(path[0]['point'], point))
|
|
painter_path.moveTo(to_screenspace_coords(start, anchor, viewport_size))
|
|
for i in range(len(path)):
|
|
p1 = path[i]
|
|
p2 = path[i + 1 if i + 1 < len(path) else 0]
|
|
c1 = QtCore.QPointF(*add(p1['tangent_out'] or p1['point'], point))
|
|
c2 = QtCore.QPointF(*add(p2['tangent_in'] or p2['point'], point))
|
|
end = QtCore.QPointF(*add(p2['point'], point))
|
|
painter_path.cubicTo(
|
|
to_screenspace_coords(c1, anchor, viewport_size),
|
|
to_screenspace_coords(c2, anchor, viewport_size),
|
|
to_screenspace_coords(end, anchor, viewport_size))
|
|
return painter_path
|
|
|
|
|
|
def get_worldspace_qpath(path, viewportmapper=None):
|
|
if not path:
|
|
return QtGui.QPainterPath()
|
|
viewportmapper = viewportmapper or ViewportMapper()
|
|
painter_path = QtGui.QPainterPath()
|
|
start = QtCore.QPointF(*path[0]['point'])
|
|
painter_path.moveTo(viewportmapper.to_viewport_coords(start))
|
|
for i in range(len(path)):
|
|
point = path[i]
|
|
point2 = path[i + 1 if i + 1 < len(path) else 0]
|
|
c1 = QtCore.QPointF(*(point['tangent_out'] or point['point']))
|
|
c2 = QtCore.QPointF(*(point2['tangent_in'] or point2['point']))
|
|
end = QtCore.QPointF(*point2['point'])
|
|
painter_path.cubicTo(
|
|
viewportmapper.to_viewport_coords(c1),
|
|
viewportmapper.to_viewport_coords(c2),
|
|
viewportmapper.to_viewport_coords(end))
|
|
return painter_path
|
|
|
|
|
|
def rotate_point(x, y, cx, cy, angle):
|
|
x_rotated = (
|
|
math.cos(angle) * (x - cx) - math.sin(angle) * (y - cy) + cx)
|
|
y_rotated = (
|
|
math.sin(angle) * (x - cx) + math.cos(angle) * (y - cy) + cy)
|
|
return x_rotated, y_rotated
|
|
|
|
|
|
def rotate_path(path, angle, center):
|
|
if not path:
|
|
return
|
|
angle_value = math.radians(angle)
|
|
cx, cy = center
|
|
# Helper function to rotate a point
|
|
|
|
rotated_path = []
|
|
for point_data in path:
|
|
x, y = point_data["point"]
|
|
tangent_in = point_data["tangent_in"]
|
|
tangent_out = point_data["tangent_out"]
|
|
|
|
# Rotate the point
|
|
x_rotated, y_rotated = rotate_point(x, y, cx, cy, angle_value)
|
|
|
|
# Rotate the tangents if they exist
|
|
if tangent_in:
|
|
tan_in_x, tan_out_y = tangent_in
|
|
tan_in_rotated = rotate_point(
|
|
tan_in_x, tan_out_y, cx, cy, angle_value)
|
|
else:
|
|
tan_in_rotated = None
|
|
|
|
if tangent_out:
|
|
tan_out_x, tan_out_y = tangent_out
|
|
tan_out_rotated = rotate_point(
|
|
tan_out_x, tan_out_y, cx, cy, angle_value)
|
|
else:
|
|
tan_out_rotated = None
|
|
|
|
# Update the shape path
|
|
rotated_path.append({
|
|
"point": [x_rotated, y_rotated],
|
|
"tangent_in": [
|
|
tan_in_rotated[0], tan_in_rotated[1]]
|
|
if tan_in_rotated else None,
|
|
"tangent_out": [
|
|
tan_out_rotated[0], tan_out_rotated[1]]
|
|
if tan_out_rotated else None})
|
|
return rotated_path
|
|
|
|
|
|
def create_polygon_path(radius, n):
|
|
shape_path = []
|
|
angle_step = 2 * math.pi / n
|
|
for i in range(n):
|
|
x = radius * math.cos(i * angle_step)
|
|
y = radius * math.sin(i * angle_step)
|
|
shape_path.append({
|
|
"point": [x, y],
|
|
"tangent_in": None,
|
|
"tangent_out": None})
|
|
return shape_path
|