Skip to content

Commit 37cf1ea

Browse files
cleemesserChris Lee-Messergithub-actions[bot]
authored
Add stroke path to Path2D interface per html canvas spec (#350)
* Add stroke path to Path2D interface per html canvas spec (#1) * add stroke(path: Path2D) form to canvas --------- Co-authored-by: Chris Lee-Messer <[email protected]> * add visual test of canvas.stroke(path) * Update Playwright Snapshots * black ipycanvas/canvas.py * fix bug falling through to fillPath-add missing break * initial documentation stroke(Path2D) * update initial stroke(path) uitest to replicate SVG fig * black format code block in drawing_paths.rst * Update Playwright Snapshots --------- Co-authored-by: Chris Lee-Messer <[email protected]> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
1 parent 5ade708 commit 37cf1ea

File tree

5 files changed

+69
-6
lines changed

5 files changed

+69
-6
lines changed

docs/drawing_paths.rst

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ There are two ways for creating and drawing a path in ipycanvas.
66
Using Path2D
77
------------
88

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.
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. This means they do not (yet) support Path2D methods like Path2D.move_to, draw_line, etc.
10+
Using the Path2D class is very useful and efficient when you want to reuse the same path multiple times. Path2D objects may be stroked or filled.
1111

1212
See https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths for documentation about SVG paths.
1313

@@ -37,6 +37,11 @@ See https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths for document
3737
canvas.fill_style = "blue"
3838
canvas.fill(path4)
3939
40+
# draw a sinusoidal curve using quadratic bezier curves
41+
path5 = Path2D("M 10 150 Q 52.5 10 95 150 T 180 150")
42+
canvas.line_width = 2.5
43+
canvas.stroke_style = "black"
44+
canvas.stroke(path5)
4045
canvas
4146
4247
.. image:: images/path2d.png

ipycanvas/canvas.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
"beginPath",
6161
"closePath",
6262
"stroke",
63+
"strokePath",
6364
"fillPath",
6465
"fill",
6566
"moveTo",
@@ -1247,9 +1248,21 @@ def close_path(self):
12471248
"""
12481249
self._canvas_manager.send_draw_command(self, COMMANDS["closePath"])
12491250

1250-
def stroke(self):
1251-
"""Stroke (outlines) the current path with the current ``stroke_style``."""
1252-
self._canvas_manager.send_draw_command(self, COMMANDS["stroke"])
1251+
# https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/stroke
1252+
# stroke(), stroke(path)
1253+
def stroke(self, path2d: Path2D = None):
1254+
"""Stroke (outlines) the current path with the current ``stroke_style``.
1255+
If @path2d is passed, that Path2D object will be rendered with the
1256+
current ``stroke_style``"""
1257+
1258+
if isinstance(path2d, Path2D):
1259+
self._canvas_manager.send_draw_command(
1260+
self,
1261+
COMMANDS["strokePath"],
1262+
[widget_serialization["to_json"](path2d, None)],
1263+
)
1264+
else:
1265+
self._canvas_manager.send_draw_command(self, COMMANDS["stroke"])
12531266

12541267
def fill(self, rule_or_path="nonzero"):
12551268
"""Fill the current path with the current ``fill_style`` and given the rule, or fill the given Path2D.

src/widget.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ const COMMANDS = [
8585
'beginPath',
8686
'closePath',
8787
'stroke',
88+
'strokePath',
8889
'fillPath',
8990
'fill',
9091
'moveTo',
@@ -279,6 +280,9 @@ export class CanvasManagerModel extends WidgetModel {
279280
case 'strokePolygon':
280281
this.currentCanvas.strokePolygon(args, buffers);
281282
break;
283+
case 'strokePath':
284+
await this.currentCanvas.strokePath(args, buffers);
285+
break;
282286
case 'fillPath':
283287
await this.currentCanvas.fillPath(args, buffers);
284288
break;
@@ -1056,9 +1060,15 @@ export class CanvasModel extends DOMWidgetModel {
10561060
this.ctx.stroke();
10571061
}
10581062

1059-
async fillPath(args: any[], buffers: any) {
1063+
async strokePath(args: any[], buffers: any) {
10601064
const [serializedPath] = args;
1065+
const path = await unpack_models(serializedPath, this.widget_manager);
10611066

1067+
this.ctx.stroke(path.value);
1068+
}
1069+
1070+
async fillPath(args: any[], buffers: any) {
1071+
const [serializedPath] = args;
10621072
const path = await unpack_models(serializedPath, this.widget_manager);
10631073

10641074
this.ctx.fill(path.value);
4.84 KB
Loading

ui-tests/tests/notebooks/ipycanvas.ipynb

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -989,6 +989,41 @@
989989
"canvas.fill_rect(0, 0, canvas.width, canvas.height)\n",
990990
"canvas"
991991
]
992+
},
993+
{
994+
"cell_type": "code",
995+
"execution_count": null,
996+
"id": "7be13b55",
997+
"metadata": {},
998+
"outputs": [],
999+
"source": [
1000+
"# test canvas.stroke(path: Path2D)\n",
1001+
"from ipycanvas import Path2D, Canvas\n",
1002+
"\n",
1003+
"canvas = Canvas(width=320, height=320)\n",
1004+
"\n",
1005+
"canvas.fill_style = \"green\"\n",
1006+
"canvas.stroke_style = \"black\"\n",
1007+
"canvas.line_width = 2\n",
1008+
"\n",
1009+
"# This more complicated path is from\n",
1010+
"# https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths\n",
1011+
"# under the Arcs section. There is no equivalent to fill-opacity:\"0.5\"\n",
1012+
"# instead use global_alpha for the fill.\n",
1013+
"p = Path2D(\"\"\"\n",
1014+
" M 10 315\n",
1015+
" L 110 215\n",
1016+
" A 30 50 0 0 1 162.55 162.45\n",
1017+
" L 172.55 152.45\n",
1018+
" A 30 50 -45 0 1 215.1 109.9\n",
1019+
" L 315 10\"\"\"\n",
1020+
")\n",
1021+
"canvas.global_alpha = 0.5\n",
1022+
"canvas.fill(p)\n",
1023+
"canvas.global_alpha = 1.0\n",
1024+
"canvas.stroke(p)\n",
1025+
"canvas"
1026+
]
9921027
}
9931028
],
9941029
"metadata": {

0 commit comments

Comments
 (0)