Skip to content

Commit 31bdcb2

Browse files
feat: add p5 blocks and execution (#26)
* feat: add dependency on p5 * feat: add p5 blocks and execution * feat: run code on button click * chore: some lint and format * chore: remove generated code output * feat: add a keyboard shortcut to run code * feat: use zelos and add comments to index.ts * feat: load useful default blocks * feat: remove unneeded toolbox blocks * feat: improve styling
1 parent a723288 commit 31bdcb2

File tree

13 files changed

+1204
-564
lines changed

13 files changed

+1204
-564
lines changed

.github/workflows/pages.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ name: Deploy static content to Pages
44
on:
55
# Runs on pushes targeting the default branch
66
push:
7-
branches: ["main"]
7+
branches: ['main']
88

99
# Allows you to run this workflow manually from the Actions tab
1010
workflow_dispatch:
@@ -18,14 +18,14 @@ permissions:
1818
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
1919
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
2020
concurrency:
21-
group: "pages"
21+
group: 'pages'
2222
cancel-in-progress: false
2323

2424
jobs:
2525
# Single deploy job since we're just deploying
2626
build:
2727
runs-on: ubuntu-latest
28-
steps:
28+
steps:
2929
- name: Checkout
3030
uses: actions/checkout@v4
3131
- name: Setup Node

package-lock.json

Lines changed: 27 additions & 43 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,10 @@
4343
"devDependencies": {
4444
"@blockly/dev-scripts": "^4.0.1",
4545
"@blockly/dev-tools": "^8.0.2",
46+
"@blockly/field-colour": "^5.0.4",
4647
"@eslint/eslintrc": "^2.1.2",
4748
"@eslint/js": "^8.49.0",
49+
"@types/p5": "^1.7.6",
4850
"@typescript-eslint/eslint-plugin": "^6.7.2",
4951
"@typescript-eslint/parser": "^6.7.2",
5052
"blockly": "^11.1.0",
@@ -69,6 +71,7 @@
6971
"extends": "@blockly/eslint-config"
7072
},
7173
"dependencies": {
72-
"@blockly/keyboard-navigation": "^0.6.2"
74+
"@blockly/keyboard-navigation": "^0.6.2",
75+
"p5": "^1.9.4"
7376
}
7477
}

src/blocks/p5_blocks.js

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
import * as Blockly from 'blockly/core';
2+
3+
// p5 Basic Setup Blocks
4+
5+
const p5SetupJson = {
6+
'type': 'p5_setup',
7+
'message0': 'setup %1',
8+
'args0': [
9+
{
10+
'type': 'input_statement',
11+
'name': 'STATEMENTS',
12+
},
13+
],
14+
'colour': 300,
15+
'tooltip': 'Setup the p5 canvas. This code is run once.',
16+
'helpUrl': '',
17+
};
18+
19+
const p5Setup = {
20+
init: function () {
21+
this.jsonInit(p5SetupJson);
22+
// The setup block can't be removed.
23+
this.setDeletable(false);
24+
},
25+
};
26+
27+
const p5DrawJson = {
28+
'type': 'p5_draw',
29+
'message0': 'draw %1',
30+
'args0': [
31+
{
32+
'type': 'input_statement',
33+
'name': 'STATEMENTS',
34+
},
35+
],
36+
'colour': 300,
37+
'tooltip': 'Draw on the canvas. This code is run continuously.',
38+
'helpUrl': '',
39+
};
40+
41+
const p5Draw = {
42+
init: function () {
43+
this.jsonInit(p5DrawJson);
44+
// The draw block can't be removed.
45+
this.setDeletable(false);
46+
},
47+
};
48+
49+
const p5CanvasJson = {
50+
'type': 'p5_canvas',
51+
'message0': 'create canvas with width %1 height %2',
52+
'args0': [
53+
{
54+
'type': 'field_number',
55+
'name': 'WIDTH',
56+
'value': 400,
57+
'max': 400,
58+
'precision': 1,
59+
},
60+
{
61+
'type': 'field_number',
62+
'name': 'HEIGHT',
63+
'value': 400,
64+
'max': 400,
65+
'precision': 1,
66+
},
67+
],
68+
'previousStatement': null,
69+
'nextStatement': null,
70+
'colour': 300,
71+
'tooltip': 'Create a p5 canvas of the specified size.',
72+
'helpUrl': '',
73+
};
74+
75+
const p5Canvas = {
76+
init: function () {
77+
this.jsonInit(p5CanvasJson);
78+
// The canvas block can't be moved or disconnected from its parent.
79+
this.setMovable(false);
80+
this.setDeletable(false);
81+
},
82+
};
83+
84+
const background = {
85+
'type': 'p5_background_color',
86+
'message0': 'Set background color to %1',
87+
'args0': [
88+
{
89+
'type': 'input_value',
90+
'name': 'COLOR',
91+
},
92+
],
93+
'previousStatement': null,
94+
'nextStatement': null,
95+
'colour': 195,
96+
'tooltip': 'Set the background color of the canvas',
97+
'helpUrl': '',
98+
};
99+
100+
const stroke = {
101+
'type': 'p5_stroke',
102+
'message0': 'Set stroke color to %1',
103+
'args0': [
104+
{
105+
'type': 'input_value',
106+
'name': 'COLOR',
107+
},
108+
],
109+
'previousStatement': null,
110+
'nextStatement': null,
111+
'colour': 195,
112+
'tooltip': 'Set the stroke color',
113+
'helpUrl': '',
114+
};
115+
116+
const fill = {
117+
'type': 'p5_fill',
118+
'message0': 'Set fill color to %1',
119+
'args0': [
120+
{
121+
'type': 'input_value',
122+
'name': 'COLOR',
123+
},
124+
],
125+
'previousStatement': null,
126+
'nextStatement': null,
127+
'colour': 195,
128+
'tooltip': 'Set the fill color',
129+
'helpUrl': '',
130+
};
131+
132+
const ellipse = {
133+
'type': 'p5_ellipse',
134+
'message0': 'draw ellipse %1 x %2 y %3 width %4 height %5',
135+
'args0': [
136+
{
137+
'type': 'input_dummy',
138+
},
139+
{
140+
'type': 'input_value',
141+
'name': 'X',
142+
'check': 'Number',
143+
},
144+
{
145+
'type': 'input_value',
146+
'name': 'Y',
147+
'check': 'Number',
148+
},
149+
{
150+
'type': 'input_value',
151+
'name': 'WIDTH',
152+
'check': 'Number',
153+
},
154+
{
155+
'type': 'input_value',
156+
'name': 'HEIGHT',
157+
'check': 'Number',
158+
},
159+
],
160+
'previousStatement': null,
161+
'nextStatement': null,
162+
'colour': 230,
163+
'tooltip': 'Draw an ellipse on the canvas.',
164+
'helpUrl': 'https://p5js.org/reference/#/p5/ellipse',
165+
};
166+
167+
// Create the block definitions for all the JSON-only blocks.
168+
// This does not register their definitions with Blockly.
169+
const jsonBlocks = Blockly.common.createBlockDefinitionsFromJsonArray([
170+
background,
171+
stroke,
172+
fill,
173+
ellipse,
174+
]);
175+
176+
export const blocks = {
177+
'p5_setup': p5Setup,
178+
'p5_draw': p5Draw,
179+
'p5_canvas': p5Canvas,
180+
...jsonBlocks,
181+
};

src/blocks/p5_generators.js

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/**
2+
* @license
3+
* Copyright 2023 Google LLC
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
import {Order} from 'blockly/javascript';
8+
9+
// Export all the code generators for our custom blocks,
10+
// but don't register them with Blockly yet.
11+
// This file has no side effects!
12+
export const forBlock = Object.create(null);
13+
14+
forBlock['text_print'] = function (block, generator) {
15+
const msg = generator.valueToCode(block, 'TEXT', Order.NONE) || "''";
16+
return `sketch.text(${msg}, 20, 20);\n`;
17+
};
18+
19+
forBlock['p5_setup'] = function (block, generator) {
20+
const statements = generator.statementToCode(block, 'STATEMENTS');
21+
const code = `sketch.setup = function() {
22+
${statements}};\n`;
23+
return code;
24+
};
25+
26+
forBlock['p5_draw'] = function (block, generator) {
27+
const statements = generator.statementToCode(block, 'STATEMENTS');
28+
const code = `sketch.draw = function() {
29+
${statements}};\n`;
30+
return code;
31+
};
32+
33+
forBlock['p5_canvas'] = function (block) {
34+
const width = block.getFieldValue('WIDTH') || 400;
35+
const height = block.getFieldValue('HEIGHT') || 400;
36+
return `sketch.createCanvas(${width}, ${height});\n`;
37+
};
38+
39+
forBlock['p5_background_color'] = function (block, generator) {
40+
const color = generator.valueToCode(block, 'COLOR', Order.ATOMIC) || `'#fff'`;
41+
const code = `sketch.background(${color});\n`;
42+
return code;
43+
};
44+
45+
forBlock['p5_stroke'] = function (block, generator) {
46+
const color = generator.valueToCode(block, 'COLOR', Order.ATOMIC) || `'#fff'`;
47+
const code = `sketch.stroke(${color});\n`;
48+
return code;
49+
};
50+
51+
forBlock['p5_fill'] = function (block, generator) {
52+
const color = generator.valueToCode(block, 'COLOR', Order.ATOMIC) || `'#fff'`;
53+
const code = `sketch.fill(${color});\n`;
54+
return code;
55+
};
56+
57+
forBlock['p5_ellipse'] = function (block, generator) {
58+
const x = generator.valueToCode(block, 'X', Order.NONE) || 0;
59+
const y = generator.valueToCode(block, 'Y', Order.NONE) || 0;
60+
const width = generator.valueToCode(block, 'WIDTH', Order.NONE) || 0;
61+
const height = generator.valueToCode(block, 'HEIGHT', Order.NONE) || 0;
62+
return `sketch.ellipse(${x}, ${y}, ${width}, ${height});\n`;
63+
};

0 commit comments

Comments
 (0)