Skip to content
......@@ -20,6 +20,36 @@
"""Module to implement vector.
Example of usage::
v = Vector2D(10, 20)
v = Vector2D((10, 20))
v = Vector2D([10, 20])
v = Vector2D(iterable)
v = Vector2D(v)
v.x
v.y
# array interface
v[0], v[1]
iter(v)
-v
v + v
v += v
v - v
v -= v
v * 2
2 * v
v *= 2
v / 2
v /= 2
"""
####################################################################################################
......@@ -51,17 +81,6 @@ class Vector2DBase(Primitive, Primitive2DMixin):
def __init__(self, *args):
"""
Example of usage::
Vector(1, 3)
Vector((1, 3))
Vector([1, 3])
Vector(iterable)
Vector(vector)
"""
array = self._check_arguments(args)
# call __getitem__ once
......@@ -117,7 +136,7 @@ class Vector2DBase(Primitive, Primitive2DMixin):
##############################################
def copy(self):
def clone(self):
""" Return a copy of self """
return self.__class__(self._v)
......@@ -339,6 +358,20 @@ class Vector2DFloatBase(Vector2DBase):
##############################################
@property
def permute(self):
"""Return a new vector where x and y are permuted.
"""
xp = self._v[1]
yp = self._v[0]
return self.__class__((xp, yp))
##############################################
@property
def parity(self):
......@@ -389,12 +422,12 @@ class Vector2DFloatBase(Vector2DBase):
##############################################
def is_parallel(self, other, cross=False):
def is_parallel(self, other, return_cross=False):
"""Self is parallel with other"""
cross = self.cross(other)
test = round(cross, 7) == 0
if cross:
if return_cross:
return test, cross
else:
return test
......@@ -437,10 +470,7 @@ class Vector2DFloatBase(Vector2DBase):
##############################################
def orientation_with(self, direction):
# Fixme: check all cases
# -> angle_with
def angle_with(self, direction):
"""Return the angle of self on direction"""
......@@ -449,6 +479,8 @@ class Vector2DFloatBase(Vector2DBase):
return angle_sign * math.degrees(angle)
orientation_with = angle_with
####################################################################################################
class Vector2D(Vector2DFloatBase):
......@@ -478,13 +510,13 @@ class Vector2D(Vector2DFloatBase):
##############################################
@staticmethod
def from_ellipse(x_radius, y_radius, angle):
def from_ellipse(radius_x, radius_y, angle):
"""Create the vector (x_radius*cos(angle), y_radius*sin(angle)). *angle* is in degree."""
"""Create the vector (radius_x*cos(angle), radius_y*sin(angle)). *angle* is in degree."""
angle = math.radians(angle)
x = x_radius * cos(angle)
y = y_radius * sin(angle)
x = radius_x * cos(angle)
y = radius_y * sin(angle)
return Vector2D(x, y) # Fixme: classmethod
......@@ -503,6 +535,12 @@ class Vector2D(Vector2DFloatBase):
##############################################
def __rmul__(self, scale):
"""Return a new vector equal to the self scaled by scale"""
return self.__mul__(scale)
##############################################
def __imul__(self, scale):
"""Scale self by scale"""
self._v *= scale
......@@ -523,6 +561,22 @@ class Vector2D(Vector2DFloatBase):
##############################################
def scale(self, scale_x, scale_y):
"""Scale self by scale"""
obj = self.clone()
obj._v *= np.array((scale_x, scale_y))
return obj
##############################################
def divide(self, scale_x, scale_y):
"""Scale self by 1/scale"""
obj = self.clone()
obj._v /= np.array((scale_x, scale_y))
return obj
##############################################
def normalise(self):
"""Normalise the vector"""
self._v /= self.magnitude
......@@ -564,6 +618,12 @@ class NormalisedVector2D(Vector2DFloatBase):
""" Return a new vector equal to the self scaled by scale """
return self.__class__(scale * self._v) # Fixme: Vector2D ?
##############################################
def __rmul__(self, scale):
""" Return a new vector equal to the self scaled by scale """
return self.__mul__(scale)
####################################################################################################
class HomogeneousVector2D(Vector2D):
......
......@@ -23,12 +23,13 @@
"""
# Fixme: get_geometry / as argument
# position versus point
####################################################################################################
import logging
from Patro.GeometryEngine.Bezier import CubicBezier2D
from Patro.GeometryEngine.Bezier import CubicBezier2D, QuadraticBezier2D
from Patro.GeometryEngine.Conic import Circle2D, Ellipse2D, AngularDomain
from Patro.GeometryEngine.Polyline import Polyline2D
from Patro.GeometryEngine.Rectangle import Rectangle2D
......@@ -38,6 +39,7 @@ from .GraphicItemMixin import (
PathStyleItemMixin,
PositionMixin,
TwoPositionMixin,
ThreePositionMixin,
FourPositionMixin,
NPositionMixin,
StartStopAngleMixin,
......@@ -151,7 +153,7 @@ class EllipseItem(PositionMixin, StartStopAngleMixin, PathStyleItemMixin):
##############################################
def __init__(self, scene, position,
x_radius, y_radius,
radius_x, radius_y,
angle,
path_style, user_data,
start_angle=0,
......@@ -162,27 +164,27 @@ class EllipseItem(PositionMixin, StartStopAngleMixin, PathStyleItemMixin):
PositionMixin.__init__(self, position)
StartStopAngleMixin.__init__(self, start_angle, stop_angle)
self._x_radius = x_radius
self._y_radius = y_radius
self._radius_x = radius_x
self._radius_y = radius_y
self._angle = angle
##############################################
@property
def x_radius(self):
return self._x_radius
def radius_x(self):
return self._radius_x
# @x_radius.setter
# def x_radius(self, value):
# self._x_radius = value
# @radius_x.setter
# def radius_x(self, value):
# self._radius_x = value
@property
def y_radius(self):
return self._y_radius
def radius_y(self):
return self._radius_y
# @y_radius.setter
# def y_radius(self, value):
# self._y_radius = value
# @radius_y.setter
# def radius_y(self, value):
# self._radius_y = value
@property
def angle(self):
......@@ -192,7 +194,7 @@ class EllipseItem(PositionMixin, StartStopAngleMixin, PathStyleItemMixin):
def get_geometry(self):
position = self.casted_position
return Ellipse2D(position, self._x_radius, self._y_radius, self._angle)
return Ellipse2D(position, self._radius_x, self._radius_y, self._angle)
####################################################################################################
......@@ -311,3 +313,48 @@ class CubicBezierItem(FourPositionMixin, PathStyleItemMixin):
def get_geometry(self):
positions = self.casted_positions
return CubicBezier2D(*positions)
####################################################################################################
class QuadraticBezierItem(ThreePositionMixin, PathStyleItemMixin):
##############################################
def __init__(self,
scene,
position1, position2, position3,
path_style,
user_data,
):
# Fixme: curve vs path
PathStyleItemMixin.__init__(self, scene, path_style, user_data)
ThreePositionMixin.__init__(self, position1, position2, position3)
# super(CubicBezierItem, self).__init__(path_style)
# self._curve = curve
##############################################
# @property
# def curve(self):
# return self._curve
# @curve.setter
# def curve(self, value):
# self._curve = value
##############################################
def get_geometry(self):
positions = self.casted_positions
return QuadraticBezier2D(*positions)
##############################################
@property
def cubic_positions(self):
if not(hasattr(self, '_cubic_points')):
cubic = self.geometry.to_cubic()
self._cubic_points = list(cubic.points)
return self._cubic_points
......@@ -240,14 +240,13 @@ class TwoPositionMixin:
####################################################################################################
class FourPositionMixin(TwoPositionMixin):
class ThreePositionMixin(TwoPositionMixin):
##############################################
def __init__(self, position1, position2, position3, position4):
def __init__(self, position1, position2, position3):
TwoPositionMixin.__init__(self, position1, position2)
self._position3 = position3
self._position4 = position4
##############################################
......@@ -255,6 +254,22 @@ class FourPositionMixin(TwoPositionMixin):
def position3(self):
return self._position3
@property
def positions(self):
return (self._position1, self._position2, self._position3)
####################################################################################################
class FourPositionMixin(ThreePositionMixin):
##############################################
def __init__(self, position1, position2, position3, position4):
ThreePositionMixin.__init__(self, position1, position2, position3)
self._position4 = position4
##############################################
@property
def position4(self):
return self._position4
......
......@@ -113,7 +113,7 @@ class GraphicPathStyle:
def line_width_as_float(self):
line_width = self._line_width
# Fixme: use scale ?
if isinstance(line_width, float):
if isinstance(line_width, (int, float)):
return line_width
else:
line_width = line_width.replace('pt', '')
......
......@@ -73,9 +73,12 @@ class GraphicSceneScope:
'rectangle': GraphicItem.RectangleItem,
'segment': GraphicItem.SegmentItem,
'polyline': GraphicItem.PolylineItem,
'quadratic_bezier': GraphicItem.QuadraticBezierItem,
'text': GraphicItem.TextItem,
}
_logger = _module_logger.getChild('GraphicSceneScope')
##############################################
def __init__(self, transformation=None):
......@@ -101,6 +104,15 @@ class GraphicSceneScope:
##############################################
def __len__(self):
return self.number_of_items
@property
def number_of_items(self):
return len(self._items)
##############################################
def __iter__(self):
# must be an ordered item list
return iter(self._items.values())
......@@ -257,7 +269,8 @@ class GraphicSceneScope:
try: # Fixme
distance = item.distance_to_point(position)
# print('distance_to_point {:6.2f} {}'.format(distance, item))
if distance <= radius:
# Fixme: distance is None
if distance is not None and distance <= radius:
items.append((distance, item))
except NotImplementedError:
pass
......@@ -283,25 +296,22 @@ class GraphicSceneScope:
# Bezier
if isinstance(item, Bezier.QuadraticBezier2D):
# ctor = self._scene.quadratic_bezier
# raise NotImplementedError
ctor = self._scene.cubic_bezier
points = list(item.to_cubic().points)
ctor = self.quadratic_bezier
elif isinstance(item, Bezier.CubicBezierItem):
ctor = self._scene.cubic_bezier
elif isinstance(item, Bezier.CubicBezier2D):
ctor = self.cubic_bezier
# Conic
elif isinstance(item, Conic.Circle2D):
ctor = self._scene.circle
ctor = self.circle
args = [item.radius]
if item.domain:
kwargs['start_angle'] = item.domain.start
kwargs['stop_angle'] = item.domain.stop
elif isinstance(item, Conic.Ellipse2D):
ctor = self._scene.ellipse
args = [item.x_radius, item.y_radius, item.angle]
ctor = self.ellipse
args = [item.radius_x, item.radius_y, item.angle]
# Line
elif isinstance(item, Line.Line2D):
......@@ -310,38 +320,39 @@ class GraphicSceneScope:
# Path
elif isinstance(item, Path.Path2D):
raise NotImplementedError
self.add_path(item, path_style)
# Polygon
elif isinstance(item, Path.Polygon2D):
elif isinstance(item, Polygon.Polygon2D):
# Fixme: to path
raise NotImplementedError
# Polyline
elif isinstance(item, Polyline.Polyline2D):
ctor = self._scene.polyline
ctor = self.polyline
# fixme: to path
# Rectangle
elif isinstance(item, Rectangle.Rectangle2D):
ctor = self._scene.rectangle
ctor = self.rectangle
# Fixme: to path
# Segment
if isinstance(item, Segment.Segment2D):
ctor = self._scene.segment
elif isinstance(item, Segment.Segment2D):
ctor = self.segment
# Spline
elif isinstance(item, Spline.BSpline2D):
return self._add_spline(item, path_style)
return self.add_spline(item, path_style)
# Triangle
if isinstance(item, Triangle.Triangle2D):
elif isinstance(item, Triangle.Triangle2D):
# Fixme: to path
raise NotImplementedError
# Not implemented
else:
self._logger.warning('Not implemented item {}'.format(item))
raise NotImplementedError
if ctor is not None:
......@@ -351,14 +362,101 @@ class GraphicSceneScope:
##############################################
def add_spline(self, item, path_style):
# def add_quadratic_bezier(self, curve, *args, **kwargs):
# # Fixme:
# cubic = curve.to_cubic()
# return self.cubic_bezier(*cubic.points, *args, **kwargs)
##############################################
def add_spline(self, spline, path_style):
return [
self._scene.cubic_bezier(*bezier.points, path_style, user_data=item)
for bezier in item.to_bezier()
self.cubic_bezier(*bezier.points, path_style, user_data=spline)
for bezier in spline.to_bezier()
]
##############################################
def add_path(self, path, path_style):
items = []
def add_bulge(segment):
arc = segment.bulge_geometry
arc_item = self.circle(
arc.center, arc.radius,
path_style,
start_angle=arc.domain.start,
stop_angle=arc.domain.stop,
user_data=segment,
)
items.append(arc_item)
def add_by_method(method, segment):
item = method(
*segment.points,
path_style,
user_data=segment,
)
items.append(item)
def add_segment(segment):
add_by_method(self.segment, segment)
def add_ellipse(segment):
# add_segment(Segment.Segment2D(*segment.points))
ellipse = segment.geometry
# print(ellipse, ellipse.domain)
arc_item = self.ellipse(
ellipse.center,
ellipse.radius_x,
ellipse.radius_y,
ellipse.angle,
path_style,
start_angle=ellipse.domain.start,
stop_angle=ellipse.domain.stop,
user_data=segment,
)
items.append(arc_item)
def add_quadratic(segment):
add_by_method(self.quadratic_bezier, segment)
def add_cubic(segment):
add_by_method(self.cubic_bezier, segment)
for segment in path:
item = None
if isinstance(segment, Path.LinearSegment):
# if segment._start_radius is True:
# continue
if segment.radius is not None:
add_bulge(segment)
# if segment._closing is True:
# start_segment = path.start_segment
# add_bulge(start_segment)
# add_segment(start_segment)
add_segment(segment)
elif isinstance(segment, Path.QuadraticBezierSegment):
add_quadratic(segment)
elif isinstance(segment, Path.CubicBezierSegment):
add_cubic(segment)
elif isinstance(segment, Path.ArcSegment):
add_ellipse(segment)
elif isinstance(segment, Path.StringedQuadtraticBezierSegment):
pass
elif isinstance(segment, Path.StringedCubicBezierSegment):
pass
##############################################
# def quadratic_bezier(self, p0, p1, p2, *args, **kwargs):
# # Fixme:
# cubic = Bezier.QuadraticBezier2D(p0, p1, p2).to_cubic()
# return self.add_cubic(cubic, *args, **kwargs)
##############################################
def bezier_path(self, points, degree, *args, **kwargs):
"""Add a Bézier curve with the given control points and degree"""
......@@ -368,7 +466,6 @@ class GraphicSceneScope:
elif degree == 2:
# Fixme:
method = self.quadratic_bezier
raise NotImplementedError
elif degree == 3:
method = self.cubic_bezier
else:
......
......@@ -127,7 +127,7 @@ class ReportlabPainter(PdfPainterBase):
# self._canvas.setSubject(subject)
bounding_box = scene.bounding_box
print(bounding_box, bounding_box.x.length, bounding_box.y.length)
# print(bounding_box, bounding_box.x.length, bounding_box.y.length)
self._canvas.translate(-bounding_box.x.inf*cm, -bounding_box.y.inf*cm)
self._canvas.translate(1*cm, 1*cm) # Fixme: margin
......
This diff is collapsed.
......@@ -46,6 +46,7 @@ def __init__():
_color_data.VALENTINA_COLORS ,
):
for name, value in color_set.items():
name = name.replace(' ', '_')
color = Color(value, name=name)
if name in Colors and color != Colors[name]:
pass
......
This diff is collapsed.
......@@ -98,8 +98,8 @@ ApplicationWindow {
scene: application.scene
focus: true
property int pan_speed: 10
property int pan_step: 10
property real pan_speed: 1.5 // scale to speedup the mouse paning
property int pan_step: 10 // px
Keys.onLeftPressed: scene_view.pan_x_y(-pan_step, 0)
Keys.onRightPressed: scene_view.pan_x_y(pan_step, 0)
......@@ -108,7 +108,7 @@ ApplicationWindow {
function pan_x_y(dx, dy) {
var dxy = Qt.point(dx, dy)
console.info('pan', dxy)
// console.info('pan', dxy)
scene_view.pan(dxy)
}
......@@ -150,8 +150,10 @@ ApplicationWindow {
onPositionChanged: {
// console.info('onPositionChanged', mouse.button, mouse_start)
if (mouse_start !== null) {
var dx = (mouse.x - mouse_start.x) / scene_view.pan_speed
var dy = (mouse_start.y - mouse.y) / scene_view.pan_speed
var dx = (mouse.x - mouse_start.x)
var dy = (mouse_start.y - mouse.y)
dx *= scene_view.pan_speed
dy *= scene_view.pan_speed
// if (dx^2 + dy^2 > 100)
scene_view.pan_x_y(dx, dy)
mouse_start = Qt.point(mouse.x, mouse.y)
......@@ -162,7 +164,7 @@ ApplicationWindow {
onWheel: {
var direction = wheel.angleDelta.y > 0
console.info('Mouse wheel', wheel.x, wheel.y, direction)
// console.info('Mouse wheel', wheel.x, wheel.y, direction)
var zoom = scene_view.zoom
if (direction)
zoom *= 2
......
../../../../doc/sphinx/source/_static/logo-128.png
\ No newline at end of file
../../../../doc/sphinx/source/_static/logo-256.png
\ No newline at end of file
../../../../doc/sphinx/source/_static/logo-32.png
\ No newline at end of file
../../../../doc/sphinx/source/_static/logo-512.png
\ No newline at end of file
../../../../doc/sphinx/source/_static/logo-64.png
\ No newline at end of file
../../../../doc/sphinx/source/_static/logo-96.png
\ No newline at end of file
../../../../doc/sphinx/source/_static/make-logo-png
\ No newline at end of file
../../../../doc/sphinx/source/_static/svg/
\ No newline at end of file
......@@ -4,6 +4,8 @@
<file>qtquickcontrols2.conf</file>
</qresource>
<qresource prefix="/">
<file>icons/svg/logo.svg</file>
<file>icons/logo-256.png</file>
<file>icons/36x36/settings-overscan-black.png</file>
<file>icons/36x36/zoom-fit-width.png</file>
<file>icons/36x36/zoom-out-black.png</file>
......
......@@ -22,9 +22,13 @@
####################################################################################################
import logging
from Patro.Common.Logging import Logging
Logging.setup_logging()
_module_logger = logging.getLogger(__name__)
_module_logger.info('Started Patro')
####################################################################################################
from Patro.QtApplication.QmlApplication import Application
......