Skip to content

Commit 60eec1f

Browse files
committed
Implemented 'Random' node with number, string, and boolean generation capabilities
1 parent 7056b76 commit 60eec1f

File tree

7 files changed

+152
-31
lines changed

7 files changed

+152
-31
lines changed

src/CodeGenerator.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,38 @@ class CodeGenerator {
151151
case 'If':
152152
this.handleIfNode(node);
153153
break;
154+
case 'Random':
155+
const randomType = node.properties.type || 'number';
156+
const resultVar = this.getUniqueVariableName('randomResult');
157+
158+
switch (randomType) {
159+
case 'number':
160+
const min = parseFloat(node.properties.min) || 1;
161+
const max = parseFloat(node.properties.max) || 100;
162+
this.addLine(`const ${resultVar} = Math.floor(Math.random() * (${max} - ${min} + 1)) + ${min};`);
163+
break;
164+
165+
case 'string':
166+
const length = parseInt(node.properties.length) || 10;
167+
const charset = node.properties.charset || 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
168+
this.addLine(`let ${resultVar} = '';`);
169+
this.addLine(`for (let i = 0; i < ${length}; i++) {`);
170+
this.indentLevel++;
171+
this.addLine(`${resultVar} += ${JSON.stringify(charset)}[Math.floor(Math.random() * ${charset.length})];`);
172+
this.indentLevel--;
173+
this.addLine(`}`);
174+
break;
175+
176+
case 'boolean':
177+
const probability = parseFloat(node.properties.probability) || 50;
178+
this.addLine(`const ${resultVar} = Math.random() * 100 < ${probability};`);
179+
break;
180+
default:
181+
this.addLine(`// Unknown random type: ${node.properties.type}`);
182+
}
183+
184+
this.nodeOutputs.set(node.id, resultVar);
185+
break;
154186
default:
155187
this.addLine(`// TODO: Implement ${node.type}`);
156188
}

src/VisualScripting.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ const VisualScripting = () => {
227227

228228
const findClickedPort = (x, y) => {
229229
const PORT_WIDTH = 6; // Width of the gray arrow
230-
const PORT_HEIGHT = 10; // Height of the gray arrow
230+
const PORT_HEIGHT = 10; // Height of the gray arrow
231231
const PORT_OFFSET = 5; // Distance from node border
232232
const SCALE_MULTIPLIER = 1.5; // Scale multiplier for hit detection
233233

src/components/GraphInspector.js

Lines changed: 34 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,15 @@ const GraphInspector = ({
1919
const node = selectedNodes[0];
2020
const nodeType = nodeTypes[node.type];
2121

22+
// Helper function to determine if a property should be visible
23+
const isPropertyVisible = (property) => {
24+
if (property.visible === undefined) return true;
25+
if (typeof property.visible === 'function') {
26+
return property.visible(node.properties);
27+
}
28+
return property.visible;
29+
};
30+
2231
return (
2332
<div className={`${styles.container} ${config.isDarkTheme ? styles.containerDark : styles.containerLight}`}>
2433
{/* Header */}
@@ -48,29 +57,31 @@ const GraphInspector = ({
4857
Properties
4958
</div>
5059
{nodeType.properties.map(prop => (
51-
<div key={prop.name} className={styles.propertyContainer}>
52-
<label className={`${styles.propertyLabel} ${config.isDarkTheme ? styles.propertyLabelDark : styles.propertyLabelLight}`}>
53-
{prop.name}
54-
</label>
55-
{prop.type === 'select' ? (
56-
<select
57-
value={node.properties[prop.name] || prop.default}
58-
onChange={(e) => updateNodeProperty(prop.name, e.target.value)}
59-
className={`${styles.input} ${config.isDarkTheme ? styles.inputDark : styles.inputLight}`}
60-
>
61-
{prop.options.map(option => (
62-
<option key={option} value={option}>{option}</option>
63-
))}
64-
</select>
65-
) : (
66-
<input
67-
type={prop.type === 'number' ? 'number' : 'text'}
68-
value={node.properties[prop.name] || prop.default}
69-
onChange={(e) => updateNodeProperty(prop.name, e.target.value)}
70-
className={`${styles.input} ${config.isDarkTheme ? styles.inputDark : styles.inputLight}`}
71-
/>
72-
)}
73-
</div>
60+
isPropertyVisible(prop) && (
61+
<div key={prop.name} className={styles.propertyContainer}>
62+
<label className={`${styles.propertyLabel} ${config.isDarkTheme ? styles.propertyLabelDark : styles.propertyLabelLight}`}>
63+
{prop.name}
64+
</label>
65+
{prop.type === 'select' ? (
66+
<select
67+
value={node.properties[prop.name] || prop.default}
68+
onChange={(e) => updateNodeProperty(prop.name, e.target.value)}
69+
className={`${styles.input} ${config.isDarkTheme ? styles.inputDark : styles.inputLight}`}
70+
>
71+
{prop.options.map(option => (
72+
<option key={option} value={option}>{option}</option>
73+
))}
74+
</select>
75+
) : (
76+
<input
77+
type={prop.type === 'number' ? 'number' : 'text'}
78+
value={node.properties[prop.name] || prop.default}
79+
onChange={(e) => updateNodeProperty(prop.name, e.target.value)}
80+
className={`${styles.input} ${config.isDarkTheme ? styles.inputDark : styles.inputLight}`}
81+
/>
82+
)}
83+
</div>
84+
)
7485
))}
7586
</div>
7687
)}

src/components/MenuBar.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,8 @@ const MenuBar = ({
7171
{menu === 'Help' && [
7272
<button key="example1" onClick={() => handleMenuItemClick('loadExample', 'example1')} className={`${styles.menuItemButton} ${styles[theme]}`}>Example 1: Hello World</button>,
7373
<button key="example2" onClick={() => handleMenuItemClick('loadExample', 'example2')} className={`${styles.menuItemButton} ${styles[theme]}`}>Example 2: Basic Math</button>,
74-
<button key="example3" onClick={() => handleMenuItemClick('loadExample', 'example3')} className={`${styles.menuItemButton} ${styles[theme]}`}>Example 3: If Statement</button>
74+
<button key="example3" onClick={() => handleMenuItemClick('loadExample', 'example3')} className={`${styles.menuItemButton} ${styles[theme]}`}>Example 3: If Statement</button>,
75+
<button key="example4" onClick={() => handleMenuItemClick('loadExample', 'example4')} className={`${styles.menuItemButton} ${styles[theme]}`}>Example 4: Random Number</button>
7576
]}
7677
</div>
7778
)}

src/engine/Renderer.js

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,13 @@ class Renderer {
5454

5555
const inputsHeight = nodeType.inputs.length * 20;
5656
const outputsHeight = nodeType.outputs.length * 20;
57-
const propertiesHeight = (nodeType.properties?.length || 0) * 20;
57+
const propertiesHeight = nodeType.properties ? nodeType.properties.reduce((height, prop) => {
58+
const isVisible = prop.visible === undefined ||
59+
(typeof prop.visible === 'function' ?
60+
prop.visible(node.properties) :
61+
prop.visible);
62+
return height + (isVisible ? 20 : 0);
63+
}, 0) : 0;
5864

5965
const width = Math.max(200, titleWidth + 20, ...descriptionLines.map(line => ctx.measureText(line).width + 20));
6066
const height = 35 + descriptionHeight + Math.max(inputsHeight, outputsHeight) + propertiesHeight;
@@ -316,10 +322,18 @@ class Renderer {
316322
ctx.font = '12px Arial';
317323

318324
nodeType.properties.forEach((prop, index) => {
319-
let displayValue = node.properties[prop.name] !== undefined ? node.properties[prop.name] : prop.default;
320-
const text = `${prop.name}: ${displayValue}`;
321-
currentHeight += 20;
322-
ctx.fillText(text, node.x + 10, node.y + currentHeight);
325+
// Check if property should be visible
326+
const isVisible = prop.visible === undefined ||
327+
(typeof prop.visible === 'function' ?
328+
prop.visible(node.properties) :
329+
prop.visible);
330+
331+
if (isVisible) {
332+
let displayValue = node.properties[prop.name] !== undefined ? node.properties[prop.name] : prop.default;
333+
const text = `${prop.name}: ${displayValue}`;
334+
currentHeight += 20;
335+
ctx.fillText(text, node.x + 10, node.y + currentHeight);
336+
}
323337
});
324338
}
325339
});

src/examples.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,19 @@ const examples = {
4242
{ start: { nodeId: 4, isInput: false, index: 0 }, end: { nodeId: 5, isInput: true, index: 0 } },
4343
{ start: { nodeId: 4, isInput: false, index: 1 }, end: { nodeId: 6, isInput: true, index: 0 } }
4444
]
45+
},
46+
example4: {
47+
nodes: [
48+
{ id: 1, type: 'OnStart', x: 100, y: 100, properties: {} },
49+
{ id: 2, type: 'Random', x: 350, y: 100, properties: { min: '1', max: '100', type: 'number', probability: '50', charset: 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', length: '10' } },
50+
{ id: 3, type: 'Variable', x: 600, y: 100, properties: { name: 'randomNum', type: 'number', initialValue: '0' } },
51+
{ id: 4, type: 'Log', x: 850, y: 100, properties: { message: '', logType: 'log' } },
52+
],
53+
edges: [
54+
{ start: { nodeId: 1, isInput: false, index: 0 }, end: { nodeId: 2, isInput: true, index: 0 } },
55+
{ start: { nodeId: 2, isInput: false, index: 0 }, end: { nodeId: 3, isInput: true, index: 0 } },
56+
{ start: { nodeId: 3, isInput: false, index: 1 }, end: { nodeId: 4, isInput: true, index: 0 } },
57+
]
4558
}
4659
};
4760

src/nodeDefinitions.js

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,12 +230,62 @@ export const nodeTypes = {
230230
properties: [
231231
{ name: 'operator', type: 'select', options: ['==', '===', '!=', '!==', '>', '<', '>=', '<='], default: '==' }
232232
]
233+
},
234+
Random: {
235+
color: '#9C27B0',
236+
inputs: [
237+
{ type: 'control', name: 'In' }
238+
],
239+
outputs: [
240+
{ type: 'control', name: 'Out' },
241+
{ type: 'data', name: 'Result' }
242+
],
243+
description: 'Generates random values of different types',
244+
properties: [
245+
{
246+
name: 'type',
247+
type: 'select',
248+
options: ['number', 'string', 'boolean'],
249+
default: 'number',
250+
visible: true
251+
},
252+
{
253+
name: 'min',
254+
type: 'number',
255+
default: 1,
256+
visible: (props) => props.type === 'number'
257+
},
258+
{
259+
name: 'max',
260+
type: 'number',
261+
default: 100,
262+
visible: (props) => props.type === 'number'
263+
},
264+
{
265+
name: 'length',
266+
type: 'number',
267+
default: 10,
268+
visible: (props) => props.type === 'string'
269+
},
270+
{
271+
name: 'charset',
272+
type: 'string',
273+
default: 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789',
274+
visible: (props) => props.type === 'string'
275+
},
276+
{
277+
name: 'probability',
278+
type: 'number',
279+
default: 50,
280+
visible: (props) => props.type === 'boolean'
281+
}
282+
]
233283
}
234284
};
235285

236286
export const nodeGroups = {
237287
"Control Flow": ["OnStart", "If", "Condition", "WhileLoop", "ForLoop"],
238-
"Data Manipulation": ["Variable", "MathOperation", "ArrayOperation", "ObjectOperation", "JSONParse", "JSONStringify"],
288+
"Data Manipulation": ["Variable", "MathOperation", "ArrayOperation", "ObjectOperation", "JSONParse", "JSONStringify", "Random"],
239289
"Functions": ["Function"],
240290
"Input/Output": ["Log", "HttpRequest"],
241291
"Encoding": ["Base64Encode", "Base64Decode"]

0 commit comments

Comments
 (0)