Skip to content

Commit 802cc60

Browse files
committed
Implement stroke_rects
1 parent 5f101e0 commit 802cc60

File tree

4 files changed

+63
-14
lines changed

4 files changed

+63
-14
lines changed

docs/source/drawing_shapes.rst

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,12 @@ left corner of the blue square becomes x pixels from the left and y pixels from
1818
Drawing rectangles
1919
------------------
2020

21-
There are three methods that draw rectangles on the canvas:
21+
There are four methods that draw rectangles on the canvas:
2222

2323
- ``fill_rect(x, y, width, height=None)``: Draws a filled rectangle. If ``height`` is None, it is set to the same value as ``width``.
2424
- ``stroke_rect(x, y, width, height=None)``: Draws a rectangular outline. If ``height`` is None, it is set to the same value as ``width``.
2525
- ``fill_rects(x, y, width, height=None)``: Draws filled rectangles. Where ``x``, ``y``, ``width`` and ``height`` are either integers, lists of integers or NumPy arrays. If ``height`` is None, it is set to the same value as ``width``.
26+
- ``stroke_rects(x, y, width, height=None)``: Draws rectangular outlines. Where ``x``, ``y``, ``width`` and ``height`` are either integers, lists of integers or NumPy arrays. If ``height`` is None, it is set to the same value as ``width``.
2627

2728
You can also clear a certain canvas rectangle area:
2829

@@ -42,7 +43,7 @@ You can also clear a certain canvas rectangle area:
4243
4344
.. image:: images/rect.png
4445

45-
``fill_rects`` is a blazingly fast way of drawing up to a million rectangles at once:
46+
``fill_rects`` and ``stroke_rects`` are blazingly fast ways of drawing up to a million rectangles at once:
4647

4748
.. code:: Python
4849

examples/particle_system.ipynb

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@
2222
"source": [
2323
"n_particles = 100_000\n",
2424
"\n",
25-
"x = np.array(np.random.rayleigh(100, n_particles), dtype=int)\n",
26-
"y = np.random.randint(0, 499, n_particles)\n",
25+
"x = np.array(np.random.rayleigh(250, n_particles), dtype=np.int32)\n",
26+
"y = np.array(np.random.rayleigh(250, n_particles), dtype=np.int32)\n",
2727
"size = np.random.randint(1, 3, n_particles)"
2828
]
2929
},
@@ -84,7 +84,7 @@
8484
"cell_type": "markdown",
8585
"metadata": {},
8686
"source": [
87-
"# Moving particles"
87+
"# Stroked particles"
8888
]
8989
},
9090
{
@@ -93,7 +93,29 @@
9393
"metadata": {},
9494
"outputs": [],
9595
"source": [
96-
"n = 2_000"
96+
"n_particles = 5_000\n",
97+
"\n",
98+
"x = np.array(np.random.rayleigh(250, n_particles), dtype=np.int32)\n",
99+
"y = np.array(np.random.rayleigh(250, n_particles), dtype=np.int32)\n",
100+
"size = np.random.randint(5, 10, n_particles)\n",
101+
"\n",
102+
"canvas = Canvas(size=(800, 500))\n",
103+
"\n",
104+
"canvas.fill_style = '#c0dced'\n",
105+
"canvas.stroke_style = '#3279a8'\n",
106+
"canvas.line_width = 1.5\n",
107+
"\n",
108+
"canvas.fill_rects(x, y, size)\n",
109+
"canvas.stroke_rects(x, y, size)\n",
110+
"\n",
111+
"canvas"
112+
]
113+
},
114+
{
115+
"cell_type": "markdown",
116+
"metadata": {},
117+
"source": [
118+
"# Moving particles"
97119
]
98120
},
99121
{
@@ -102,8 +124,7 @@
102124
"metadata": {},
103125
"outputs": [],
104126
"source": [
105-
"canvas = Canvas(size=(800, 500))\n",
106-
"canvas"
127+
"n = 2_000"
107128
]
108129
},
109130
{
@@ -131,8 +152,8 @@
131152
"metadata": {},
132153
"outputs": [],
133154
"source": [
134-
"x = np.array(np.random.rayleigh(100, n_particles), dtype=int)\n",
135-
"y = np.random.randint(0, 499, n_particles)\n",
155+
"x = np.array(np.random.rayleigh(250, n_particles), dtype=np.int32)\n",
156+
"y = np.array(np.random.rayleigh(250, n_particles), dtype=np.int32)\n",
136157
"size = np.random.randint(1, 3, n_particles)\n",
137158
"speed_x = np.random.randint(-40, 40, n_particles)\n",
138159
"speed_y = np.random.randint(-40, 40, n_particles)"
@@ -144,6 +165,10 @@
144165
"metadata": {},
145166
"outputs": [],
146167
"source": [
168+
"canvas = Canvas(size=(800, 500))\n",
169+
"\n",
170+
"display(canvas)\n",
171+
"\n",
147172
"canvas.fill_style = 'green'\n",
148173
"\n",
149174
"for _ in range(75):\n",

ipycanvas/canvas.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ def stroke_rect(self, x, y, width, height=None):
118118
self._send_canvas_command('strokeRect', (x, y, width, height))
119119

120120
def fill_rects(self, x, y, width, height=None):
121-
"""Draw filled rectangles of size ``(width, height)`` at the ``(x, y)`` position with a given ``fill_style``.
121+
"""Draw filled rectangles of sizes ``(width, height)`` at the ``(x, y)`` positions.
122122
123123
Where ``x``, ``y``, ``width`` and ``height`` arguments are NumPy arrays, lists or scalar values.
124124
If ``height`` is None, it is set to the same value as width.
@@ -137,6 +137,26 @@ def fill_rects(self, x, y, width, height=None):
137137

138138
self._send_canvas_command('fillRects', args, buffers)
139139

140+
def stroke_rects(self, x, y, width, height=None):
141+
"""Draw a rectangular outlines of sizes ``(width, height)`` at the ``(x, y)`` positions.
142+
143+
Where ``x``, ``y``, ``width`` and ``height`` arguments are NumPy arrays, lists or scalar values.
144+
If ``height`` is None, it is set to the same value as width.
145+
"""
146+
args = []
147+
buffers = []
148+
149+
populate_args(x, args, buffers)
150+
populate_args(y, args, buffers)
151+
populate_args(width, args, buffers)
152+
153+
if height is None:
154+
args.append(args[-1])
155+
else:
156+
populate_args(height, args, buffers)
157+
158+
self._send_canvas_command('strokeRects', args, buffers)
159+
140160
def clear_rect(self, x, y, width, height=None):
141161
"""Clear the specified rectangular area of size ``(width, height)`` at the ``(x, y)`` position, making it fully transparent."""
142162
if height is None:

src/widget.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,10 @@ class CanvasModel extends DOMWidgetModel {
8181
this.putImageData(command.args, buffers);
8282
break;
8383
case 'fillRects':
84-
this.fillRects(command.args, buffers);
84+
this.drawRects(command.args, buffers, 'fillRect');
85+
break;
86+
case 'strokeRects':
87+
this.drawRects(command.args, buffers, 'strokeRect');
8588
break;
8689
case 'set':
8790
this.setAttr(command.attr, command.value);
@@ -113,7 +116,7 @@ class CanvasModel extends DOMWidgetModel {
113116
this.ctx.drawImage(offscreenCanvas, dx, dy);
114117
}
115118

116-
private fillRects(args: any[], buffers: any) {
119+
private drawRects(args: any[], buffers: any, commandName: string) {
117120
const x = getArg(args[0], buffers);
118121
const y = getArg(args[1], buffers);
119122
const width = getArg(args[2], buffers);
@@ -122,7 +125,7 @@ class CanvasModel extends DOMWidgetModel {
122125
const numberRects = Math.min(x.length, y.length, width.length, height.length);
123126

124127
for (let idx = 0; idx < numberRects; ++idx) {
125-
this.ctx.fillRect(x.getItem(idx), y.getItem(idx), width.getItem(idx), height.getItem(idx));
128+
this.executeCommand(commandName, [x.getItem(idx), y.getItem(idx), width.getItem(idx), height.getItem(idx)]);
126129
}
127130
}
128131

0 commit comments

Comments
 (0)