diff --git a/src/components/Customizer.tsx b/src/components/Customizer.tsx index a00456e..f3d10b9 100644 --- a/src/components/Customizer.tsx +++ b/src/components/Customizer.tsx @@ -2,6 +2,7 @@ import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown'; import Accordion from '@mui/material/Accordion'; import AccordionDetails from '@mui/material/AccordionDetails'; import AccordionSummary from '@mui/material/AccordionSummary'; +import Autocomplete from '@mui/material/Autocomplete'; import Checkbox from '@mui/material/Checkbox'; import FormControlLabel from '@mui/material/FormControlLabel'; import FormGroup from '@mui/material/FormGroup'; @@ -19,19 +20,21 @@ type Props = { }; export default function Customizer({ parameters, onChange }: Props) { - const handleParameterChange = - (isCheckbox) => (event: React.ChangeEvent) => { - const newParameters = parameters.map((parameter) => { - if (parameter.name === event.target.name) { - return { - ...parameter, - value: isCheckbox ? event.target.checked : event.target.value, - }; - } - return parameter; - }); - onChange(newParameters); - }; + const handleParameterChange = ( + event: React.ChangeEvent, + newValue? + ) => { + const newParameters = parameters.map((parameter) => { + if (parameter.name === event.target.name) { + return { + ...parameter, + value: newValue || event.target.value, + }; + } + return parameter; + }); + onChange(newParameters); + }; // Group parameters const groups = useMemo( @@ -74,7 +77,7 @@ export default function Customizer({ parameters, onChange }: Props) { fullWidth key={parameter.name} name={parameter.name} - onChange={handleParameterChange(false)} + onChange={handleParameterChange} value={parameter.value} sx={{ mt: 2, p: 1 }} > @@ -94,7 +97,7 @@ export default function Customizer({ parameters, onChange }: Props) { type={parameter.type} key={parameter.name} name={parameter.name} - onChange={handleParameterChange(false)} + onChange={handleParameterChange} value={parameter.value} InputProps={{ inputProps: { @@ -107,6 +110,41 @@ export default function Customizer({ parameters, onChange }: Props) { sx={{ mt: 2, p: 1 }} /> ); + } else if ( + parameter.type === 'number[]' || + parameter.type === 'string[]' + ) { + return ( + { + return ( + + ); + }} + /> + ); } else if (parameter.type === 'boolean') { return ( @@ -114,7 +152,7 @@ export default function Customizer({ parameters, onChange }: Props) { control={ } diff --git a/src/lib/openSCAD/parseParameter.ts b/src/lib/openSCAD/parseParameter.ts index 010b4ba..84b29e1 100644 --- a/src/lib/openSCAD/parseParameter.ts +++ b/src/lib/openSCAD/parseParameter.ts @@ -9,10 +9,18 @@ type ParameterRange = { step?: number; }; +type ParameterType = + | 'string' + | 'number' + | 'boolean' + | 'string[]' + | 'number[]' + | 'boolean[]'; + export type Parameter = { name: string; - type: 'string' | 'number' | 'boolean'; - value: string | boolean | number; + type: ParameterType; + value: string | boolean | number | string[] | number[] | boolean[]; description?: string; group?: string; range?: ParameterRange; @@ -28,7 +36,7 @@ export default function parseParameters(script: string): Parameter[] { const parameters: Record = {}; const parameterRegex = - /^([a-z0-9A-Z_$]+)\s*=\s*([^;]+);[\t\f\cK ]*(\/\/.*)?/gm; // TODO: Use AST parser instead of regex + /^([a-z0-9A-Z_$]+)\s*=\s*([^;]+);[\t\f\cK ]*(\/\/[^\n]*)?/gm; // TODO: Use AST parser instead of regex const groupRegex = /^\/\*\s*\[([^\]]+)\]\s*\*\//gm; const groupSections: { id: string; group: string; code: string }[] = []; @@ -139,12 +147,31 @@ export default function parseParameters(script: string): Parameter[] { function convertType(rawValue): { value: string | boolean | number; - type: 'string' | 'number' | 'boolean'; + type: ParameterType; } { if (/^\d+(\.\d+)?$/.test(rawValue)) { return { value: parseFloat(rawValue), type: 'number' }; } else if (rawValue === 'true' || rawValue === 'false') { return { value: rawValue === 'true', type: 'boolean' }; + } else if (rawValue.startsWith('[') && rawValue.endsWith(']')) { + // Array type + const arrayValue = rawValue + .slice(1, -1) + .split(',') + .map((item) => item.trim()); + if (arrayValue.every((item) => /^\d+(\.\d+)?$/.test(item))) { + return { + value: arrayValue.map((item) => parseFloat(item)), + type: 'number[]', + }; + } else if (arrayValue.every((item) => /^".*"$/.test(item))) { + return { + value: arrayValue.map((item) => item.slice(1, -1)), + type: 'string[]', + }; + } else { + return { value: arrayValue, type: 'string[]' }; + } } else { // Remove quotes rawValue = rawValue.replace(/^"(.*)"$/, '$1'); diff --git a/src/openSCADWorker.mts b/src/openSCADWorker.mts index 12d93b3..e1629f1 100644 --- a/src/openSCADWorker.mts +++ b/src/openSCADWorker.mts @@ -31,10 +31,15 @@ async function exportFile( params: OpenSCADWorkerInputMessage['params'], fileType = 'stl' ): Promise { - const parameters = params.map(({ name, value }) => { - if (typeof value === 'string') { + const parameters = params.map(({ name, type, value }) => { + if (type === 'string' && typeof value === 'string') { value = escapeShell(value); + } else if (type === 'number[]' && Array.isArray(value)) { + value = `[${value.join(',')}]`; + } else if (type === 'string[]' && Array.isArray(value)) { + value = `[${value.map((item) => escapeShell(item)).join(',')}]`; } + return `-D${name}=${value}`; }); @@ -53,9 +58,13 @@ async function preview( params: OpenSCADWorkerInputMessage['params'], fileType = 'stl' ): Promise { - const parameters = params.map(({ name, value }) => { - if (typeof value === 'string') { + const parameters = params.map(({ name, type, value }) => { + if (type === 'string' && typeof value === 'string') { value = escapeShell(value); + } else if (type === 'number[]' && Array.isArray(value)) { + value = `[${value.join(',')}]`; + } else if (type === 'string[]' && Array.isArray(value)) { + value = `[${value.map((item) => escapeShell(item)).join(',')}]`; } return `-D${name}=${value}`; }); diff --git a/tests/__snapshots__/openSCADparseParameters.test.ts.snap b/tests/__snapshots__/openSCADparseParameters.test.ts.snap index 4563487..a1b73d6 100644 --- a/tests/__snapshots__/openSCADparseParameters.test.ts.snap +++ b/tests/__snapshots__/openSCADparseParameters.test.ts.snap @@ -254,8 +254,15 @@ exports[`testing parameter parsing of openscad scripts testing example1.scad: ex "name": "Vector6", "options": undefined, "range": undefined, - "type": "string", - "value": "[12,34,44,43,23,23]", + "type": "number[]", + "value": [ + 12, + 34, + 44, + 43, + 23, + 23, + ], }, { "description": "Text box for string", @@ -276,8 +283,10 @@ exports[`testing parameter parsing of openscad scripts testing example1.scad: ex "min": 0, "step": 2, }, - "type": "string", - "value": "[12]", + "type": "number[]", + "value": [ + 12, + ], }, { "description": undefined, @@ -289,8 +298,11 @@ exports[`testing parameter parsing of openscad scripts testing example1.scad: ex "min": 0, "step": 2, }, - "type": "string", - "value": "[12,34]", + "type": "number[]", + "value": [ + 12, + 34, + ], }, { "description": undefined, @@ -302,8 +314,12 @@ exports[`testing parameter parsing of openscad scripts testing example1.scad: ex "min": 0, "step": 2, }, - "type": "string", - "value": "[12,34,46]", + "type": "number[]", + "value": [ + 12, + 34, + 46, + ], }, { "description": undefined, @@ -315,8 +331,13 @@ exports[`testing parameter parsing of openscad scripts testing example1.scad: ex "min": 0, "step": 2, }, - "type": "string", - "value": "[12,34,46,24]", + "type": "number[]", + "value": [ + 12, + 34, + 46, + 24, + ], }, ] `; @@ -332,8 +353,14 @@ exports[`testing parameter parsing of openscad scripts testing printables-513382 "max": 2, "min": 0, }, - "type": "string", - "value": "[35, 35, 35, 35, 35]", + "type": "number[]", + "value": [ + 35, + 35, + 35, + 35, + 35, + ], }, { "description": "How wide should each segment be?", @@ -341,8 +368,15 @@ exports[`testing parameter parsing of openscad scripts testing printables-513382 "name": "segment_diameters", "options": undefined, "range": undefined, - "type": "string", - "value": "[140, 100, 120, 90, 110, 90]", + "type": "number[]", + "value": [ + 140, + 100, + 120, + 90, + 110, + 90, + ], }, { "description": "The shape of the twig", @@ -372,8 +406,14 @@ exports[`testing parameter parsing of openscad scripts testing printables-513382 "name": "twig_twist", "options": undefined, "range": undefined, - "type": "string", - "value": "[25, 25, 25, 0, 25]", + "type": "number[]", + "value": [ + 25, + 25, + 25, + 0, + 25, + ], }, { "description": "How much should the twigs in the counter direction be twisted?", @@ -381,8 +421,14 @@ exports[`testing parameter parsing of openscad scripts testing printables-513382 "name": "twig_counter_twist", "options": undefined, "range": undefined, - "type": "string", - "value": "[25, 25, 25, 0, 25]", + "type": "number[]", + "value": [ + 25, + 25, + 25, + 0, + 25, + ], }, { "description": "How thick should a twig be?", @@ -493,8 +539,11 @@ exports[`testing parameter parsing of openscad scripts testing printables-513382 "name": "min_max_segment_count", "options": undefined, "range": undefined, - "type": "string", - "value": "[2, 5]", + "type": "number[]", + "value": [ + 2, + 5, + ], }, { "description": "The height of each segment is between those two numbers.", @@ -502,8 +551,11 @@ exports[`testing parameter parsing of openscad scripts testing printables-513382 "name": "min_max_heights", "options": undefined, "range": undefined, - "type": "string", - "value": "[0, 180]", + "type": "number[]", + "value": [ + 0, + 180, + ], }, { "description": "Min/max of all the diameters.", @@ -511,8 +563,11 @@ exports[`testing parameter parsing of openscad scripts testing printables-513382 "name": "min_max_diameters", "options": undefined, "range": undefined, - "type": "string", - "value": "[100, 180]", + "type": "number[]", + "value": [ + 100, + 180, + ], }, { "description": "The amount of twig twist will be between those two numbers.", @@ -520,8 +575,11 @@ exports[`testing parameter parsing of openscad scripts testing printables-513382 "name": "min_max_twig_twist", "options": undefined, "range": undefined, - "type": "string", - "value": "[0, 180]", + "type": "number[]", + "value": [ + 0, + 180, + ], }, { "description": "Min/max of the twig diameter.", @@ -529,8 +587,11 @@ exports[`testing parameter parsing of openscad scripts testing printables-513382 "name": "min_max_twig_diameter", "options": undefined, "range": undefined, - "type": "string", - "value": "[2, 20]", + "type": "number[]", + "value": [ + 2, + 20, + ], }, { "description": "The twig count of both twig directions will be between those two numbers.", @@ -538,8 +599,11 @@ exports[`testing parameter parsing of openscad scripts testing printables-513382 "name": "min_max_twig_count", "options": undefined, "range": undefined, - "type": "string", - "value": "[5, 15]", + "type": "number[]", + "value": [ + 5, + 15, + ], }, { "description": undefined, @@ -814,8 +878,11 @@ exports[`testing parameter parsing of openscad scripts testing specialVector.sca "name": "Vector2", "options": undefined, "range": undefined, - "type": "string", - "value": "[12,34]", + "type": "number[]", + "value": [ + 12, + 34, + ], }, { "description": undefined, @@ -823,8 +890,12 @@ exports[`testing parameter parsing of openscad scripts testing specialVector.sca "name": "Vector3", "options": undefined, "range": undefined, - "type": "string", - "value": "[12,34,45]", + "type": "number[]", + "value": [ + 12, + 34, + 45, + ], }, { "description": undefined, @@ -832,8 +903,13 @@ exports[`testing parameter parsing of openscad scripts testing specialVector.sca "name": "Vector4", "options": undefined, "range": undefined, - "type": "string", - "value": "[12,34,45,23]", + "type": "number[]", + "value": [ + 12, + 34, + 45, + 23, + ], }, { "description": undefined, @@ -845,8 +921,12 @@ exports[`testing parameter parsing of openscad scripts testing specialVector.sca "min": 1, "step": 2, }, - "type": "string", - "value": "[12,34,46]", + "type": "number[]", + "value": [ + 12, + 34, + 46, + ], }, { "description": undefined, @@ -857,8 +937,13 @@ exports[`testing parameter parsing of openscad scripts testing specialVector.sca "max": 50, "min": 1, }, - "type": "string", - "value": "[12,34,45,23]", + "type": "number[]", + "value": [ + 12, + 34, + 45, + 23, + ], }, ] `; @@ -887,8 +972,15 @@ exports[`testing parameter parsing of openscad scripts testing splitAfterFunctio "name": "Vector6", "options": undefined, "range": undefined, - "type": "string", - "value": "[12,34,44,43,23,23]", + "type": "number[]", + "value": [ + 12, + 34, + 44, + 43, + 23, + 23, + ], }, { "description": "Text box for string with length 8", @@ -912,8 +1004,15 @@ exports[`testing parameter parsing of openscad scripts testing testbox.scad: tes "name": "Vector6", "options": undefined, "range": undefined, - "type": "string", - "value": "[12,34,44,43,23,23]", + "type": "number[]", + "value": [ + 12, + 34, + 44, + 43, + 23, + 23, + ], }, { "description": "Text box for string with length 8",