Skip to content

Commit e36ce3b

Browse files
committed
Implement Path2D
Signed-off-by: martinRenou <[email protected]>
1 parent 6e9f9fa commit e36ce3b

File tree

6 files changed

+133
-4538
lines changed

6 files changed

+133
-4538
lines changed

docs/source/drawing_paths.rst

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,47 @@
11
Drawing paths
22
=============
33

4+
There are two ways for creating and drawing a path in ipycanvas.
5+
6+
Using Path2D
7+
------------
8+
9+
You can define a Path2D given an SVG path. Note that once the path is created, it is read only, you cannot dynamically change the path value.
10+
Using the Path2D class is very useful and efficient when you want to reuse the same path multiple times.
11+
12+
- ``Path2D(value)``: Creates a Path2D given the SVG path string value.
13+
14+
.. code:: Python
15+
16+
from ipycanvas import Canvas, Path2D
17+
18+
canvas = Canvas(width=350, height=350)
19+
20+
path1 = Path2D('M80 80 A 45 45, 0, 0, 0, 125 125 L 125 80 Z')
21+
path2 = Path2D('M230 80 A 45 45, 0, 1, 0, 275 125 L 275 80 Z')
22+
path3 = Path2D('M80 230 A 45 45, 0, 0, 1, 125 275 L 125 230 Z')
23+
path4 = Path2D('M230 230 A 45 45, 0, 1, 1, 275 275 L 275 230 Z')
24+
25+
canvas.fill_style = 'green'
26+
canvas.fill(path1)
27+
28+
canvas.fill_style = 'purple'
29+
canvas.fill(path2)
30+
31+
canvas.fill_style = 'red'
32+
canvas.fill(path3)
33+
34+
canvas.fill_style = 'blue'
35+
canvas.fill(path4)
36+
37+
canvas
38+
39+
.. image:: images/path2d.png
40+
41+
42+
Using Path commands
43+
-------------------
44+
445
A path is a list of points, connected by segments of lines that can be of different shapes, curved or not,
546
of different width and of different color. A path can be closed. To make shapes using paths, we take some
647
extra steps:
@@ -12,7 +53,7 @@ extra steps:
1253
Here are the functions used to perform these steps:
1354

1455
- ``begin_path()``: Creates a new path. Once created, future drawing commands are directed into the path and used to build the path up.
15-
- Draw commands like ``line_to`` and ``arc``
56+
- Path commands like ``line_to`` and ``arc``
1657
- ``close_path()``: Adds a straight line to the path, going to the start of the current path.
1758
- ``stroke()``: Draws the shape by stroking its outline.
1859
- ``fill(rule)``: Draws a solid shape by filling the path's content area. The given fill rule is applied, possible rules are `nonzero` and `evenodd`.
@@ -35,8 +76,8 @@ Here are the functions used to perform these steps:
3576
.. image:: images/triangle.png
3677

3778

38-
Draw commands
39-
-------------
79+
Path commands
80+
+++++++++++++
4081

4182
Here are the available draw commands:
4283

@@ -55,10 +96,10 @@ Here are the available draw commands:
5596

5697

5798
Examples
58-
--------
99+
++++++++
59100

60101
Stroke arcs
61-
+++++++++++
102+
'''''''''''
62103

63104
.. code:: Python
64105
@@ -84,7 +125,7 @@ Stroke arcs
84125
.. image:: images/smiley.png
85126

86127
Fill bezier curves
87-
++++++++++++++++++
128+
''''''''''''''''''
88129

89130
.. code:: Python
90131
@@ -108,7 +149,7 @@ Fill bezier curves
108149
.. image:: images/heart.png
109150

110151
Change the fill rule
111-
++++++++++++++++++++
152+
''''''''''''''''''''
112153

113154
.. code:: Python
114155

docs/source/images/path2d.png

7.04 KB
Loading

ipycanvas/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
# Copyright (c) Martin Renou.
55
# Distributed under the terms of the Modified BSD License.
66

7-
from .canvas import Canvas, RoughCanvas, MultiCanvas, MultiRoughCanvas, hold_canvas # noqa
7+
from .canvas import Path2D, Canvas, RoughCanvas, MultiCanvas, MultiRoughCanvas, hold_canvas # noqa
88
from ._version import __version__, version_info # noqa
99

1010
from .nbextension import _jupyter_nbextension_paths # noqa

ipycanvas/canvas.py

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,38 @@
1111

1212
from traitlets import Bool, Bytes, CInt, Enum, Float, Instance, List, Unicode
1313

14-
from ipywidgets import CallbackDispatcher, Color, DOMWidget, Image, widget_serialization
14+
from ipywidgets import CallbackDispatcher, Color, DOMWidget, Image, Widget, widget_serialization
1515
from ipywidgets.widgets.trait_types import bytes_serialization
1616

1717
from ._frontend import module_name, module_version
1818

1919
from .utils import binary_image, populate_args, to_camel_case, image_bytes_to_array
2020

2121

22+
class Path2D(Widget):
23+
"""Create a Path2D.
24+
25+
Args:
26+
value (str): The path value, e.g. "M10 10 h 80 v 80 h -80 Z"
27+
"""
28+
29+
_model_module = Unicode(module_name).tag(sync=True)
30+
_model_module_version = Unicode(module_version).tag(sync=True)
31+
_view_module = Unicode(module_name).tag(sync=True)
32+
_view_module_version = Unicode(module_version).tag(sync=True)
33+
34+
_model_name = Unicode('Path2DModel').tag(sync=True)
35+
_view_name = Unicode('Path2DView').tag(sync=True)
36+
37+
value = Unicode(allow_none=False, read_only=True).tag(sync=True)
38+
39+
def __init__(self, value):
40+
"""Create a Path2D object given the path string."""
41+
self.set_trait('value', value)
42+
43+
super(Path2D, self).__init__()
44+
45+
2246
class _CanvasBase(DOMWidget):
2347
_model_module = Unicode(module_name).tag(sync=True)
2448
_model_module_version = Unicode(module_version).tag(sync=True)
@@ -373,12 +397,15 @@ def stroke(self):
373397
"""Stroke (outlines) the current path with the current ``stroke_style``."""
374398
self._send_canvas_command('stroke')
375399

376-
def fill(self, rule='nonzero'):
377-
"""Fill the current path with the current ``fill_style`` and given the rule.
400+
def fill(self, rule_or_path='nonzero'):
401+
"""Fill the current path with the current ``fill_style`` and given the rule, or fill the given Path2D.
378402
379403
Possible rules are ``nonzero`` and ``evenodd``.
380404
"""
381-
self._send_canvas_command('fill', (rule, ))
405+
if isinstance(rule_or_path, Path2D):
406+
self._send_canvas_command('fillPath', (widget_serialization['to_json'](rule_or_path, None), ))
407+
else:
408+
self._send_canvas_command('fill', (rule_or_path, ))
382409

383410
def move_to(self, x, y):
384411
"""Move the "pen" to the given ``(x, y)`` coordinates."""

0 commit comments

Comments
 (0)