62 lines
4.4 KiB
Python
62 lines
4.4 KiB
Python
import argparse
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
from . import __version__
|
|
from .kernel import build, operator_catalog, load_plugin, validate_bundle
|
|
from .project import Project, read_json, write_json
|
|
from .examples import demo_project, small_project
|
|
|
|
def main(argv=None):
|
|
p=argparse.ArgumentParser(prog='spatial-lab',description='Mathematical geometry workbench; author through commands, inspect in a browser, transfer exact meshes to Blender.')
|
|
p.add_argument('--version',action='version',version=__version__)
|
|
p.add_argument('--plugin',action='append',default=[],help='Load a trusted local Python operator module (repeatable)')
|
|
s=p.add_subparsers(dest='command',required=True)
|
|
q=s.add_parser('init',help='Create an editable project');q.add_argument('project');q.add_argument('--example',choices=['atrium','simple'],default='atrium')
|
|
q=s.add_parser('build',help='Evaluate and export the technical bundle');q.add_argument('project');q.add_argument('--out',required=True)
|
|
q=s.add_parser('inspect',help='Show evaluated nodes and mesh checks');q.add_argument('project');q.add_argument('--node')
|
|
q=s.add_parser('param',help='Change a global parameter transactionally');q.add_argument('project');q.add_argument('name');q.add_argument('value',help='JSON value, e.g. 12 or "2*pi"')
|
|
q=s.add_parser('patch',help='Apply an atomic list of graph changes');q.add_argument('project');q.add_argument('changes',help='JSON file containing an array of changes')
|
|
q=s.add_parser('node',help='Add or replace a node by stable ID');q.add_argument('project');q.add_argument('spec',help='JSON file containing one node')
|
|
q=s.add_parser('remove',help='Remove a node');q.add_argument('project');q.add_argument('node_id');q.add_argument('--cascade',action='store_true')
|
|
for name in ['undo','redo']:
|
|
q=s.add_parser(name);q.add_argument('project')
|
|
s.add_parser('ops',help='List installed operators and their versions')
|
|
q=s.add_parser('check',help='Check bundle integrity');q.add_argument('bundle')
|
|
q=s.add_parser('serve',help='Run the read-only, live geometry viewer');q.add_argument('project');q.add_argument('--port',type=int,default=8767)
|
|
a=p.parse_args(argv)
|
|
try:
|
|
for plugin in a.plugin:load_plugin(plugin)
|
|
if a.command=='ops':result=operator_catalog()
|
|
elif a.command=='init':
|
|
if Path(a.project).exists():raise ValueError('Project already exists; choose another path')
|
|
project=demo_project() if a.example=='atrium' else small_project();b=build(project);write_json(a.project,project);result={'project':str(Path(a.project).resolve()),**b['stats']}
|
|
elif a.command=='build':
|
|
b=Project(a.project).build();write_json(a.out,b);result={'bundle':str(Path(a.out).resolve()),'hash':b['bundle_hash'],**b['stats'],'warnings':b['validation']['warnings']}
|
|
elif a.command=='inspect':
|
|
b=Project(a.project).build()
|
|
if a.node:
|
|
result=next((n for n in b['nodes'] if n['id']==a.node),None)
|
|
if result is None:raise ValueError('No such node: '+a.node)
|
|
else:result={'name':b['name'],'parameters':b['recipe'].get('parameters',{}),'nodes':b['nodes'],'stats':b['stats'],'bounds':b['bounds'],'validation':b['validation']}
|
|
elif a.command=='check':
|
|
b=read_json(a.bundle);validate_bundle(b);result={'valid':True,'hash':b['bundle_hash'],'stats':b['stats']}
|
|
elif a.command=='serve':
|
|
from .server import serve
|
|
serve(a.project,a.port);return 0
|
|
else:
|
|
project=Project(a.project)
|
|
if a.command in ['undo','redo']:b=project.travel(a.command)
|
|
else:
|
|
if a.command=='patch':changes=read_json(a.changes)
|
|
elif a.command=='node':changes=[{'action':'upsert_node','node':read_json(a.spec)}]
|
|
elif a.command=='remove':changes=[{'action':'remove_node','id':a.node_id,'cascade':a.cascade}]
|
|
elif a.command=='param':changes=[{'action':'set_parameter','name':a.name,'value':json.loads(a.value)}]
|
|
b=project.apply(changes)
|
|
result={'action':a.command,'project_hash':b['project_hash'],**b['stats'],'warnings':b['validation']['warnings']}
|
|
print(json.dumps(result,indent=2,allow_nan=False));return 0
|
|
except (ValueError,OSError,KeyError,TypeError) as e:
|
|
print(json.dumps({'error':str(e)}),file=sys.stderr);return 2
|
|
|
|
if __name__=='__main__':raise SystemExit(main())
|