diff --git a/.gitignore b/.gitignore
index ea4a0dade..b97c00845 100644
--- a/.gitignore
+++ b/.gitignore
@@ -50,6 +50,9 @@
# /plugins_src/autouv/
/plugins_src/autouv/autouv_en.lang
+# /plugins_src/scripting/docs/generated/
+/plugins_src/scripting/docs/generated/*
+
# /plugins_src/fbx/
/plugins_src/fbx/*.o
/plugins_src/fbx/*.obj
diff --git a/plugins/scripting/README b/plugins/scripting/README
new file mode 100644
index 000000000..61eaea1b1
--- /dev/null
+++ b/plugins/scripting/README
@@ -0,0 +1 @@
+This directory contains the scripting plugin.
diff --git a/plugins/scripting/wpc_scripting_shapes_init/README b/plugins/scripting/wpc_scripting_shapes_init/README
new file mode 100644
index 000000000..a5d1aa3b3
--- /dev/null
+++ b/plugins/scripting/wpc_scripting_shapes_init/README
@@ -0,0 +1 @@
+This directory contains files for the scripting plugin.
diff --git a/plugins/scripting/wpc_scripting_shapes_init/py/README b/plugins/scripting/wpc_scripting_shapes_init/py/README
new file mode 100644
index 000000000..06724e6c5
--- /dev/null
+++ b/plugins/scripting/wpc_scripting_shapes_init/py/README
@@ -0,0 +1 @@
+This directory contains the python files for scripting.
diff --git a/plugins/scripting/wpc_scripting_shapes_init/scm/README b/plugins/scripting/wpc_scripting_shapes_init/scm/README
new file mode 100644
index 000000000..cedf160c8
--- /dev/null
+++ b/plugins/scripting/wpc_scripting_shapes_init/scm/README
@@ -0,0 +1 @@
+This directory contains the scheme files for scripting.
diff --git a/plugins_src/Makefile b/plugins_src/Makefile
index e94ad5933..03ac52f0f 100644
--- a/plugins_src/Makefile
+++ b/plugins_src/Makefile
@@ -51,6 +51,7 @@ subdirs:
(cd primitives; $(MAKE))
(cd commands; $(MAKE))
(cd autouv; $(MAKE))
+ (cd scripting; $(MAKE))
template: opt
$(ERL) -pa $(WINGS_INTL) -noinput -run tools generate_template_files $(EBIN)
@@ -64,6 +65,7 @@ subdirs_lang:
(cd primitives; $(MAKE) lang)
(cd commands; $(MAKE) lang)
(cd autouv; $(MAKE) lang)
+ (cd scripting; $(MAKE) lang)
clean: subdirs_clean
rm -f $(TARGET_FILES)
@@ -74,6 +76,7 @@ subdirs_clean:
(cd primitives; $(MAKE) clean)
(cd commands; $(MAKE) clean)
(cd autouv; $(MAKE) clean)
+ (cd scripting; $(MAKE) clean)
$(EBIN)/%.beam: $(ESRC)/%.erl
$(ERLC) $(ERL_COMPILE_FLAGS) -o$(EBIN) $<
diff --git a/plugins_src/scripting/Makefile b/plugins_src/scripting/Makefile
new file mode 100644
index 000000000..68bf5dc12
--- /dev/null
+++ b/plugins_src/scripting/Makefile
@@ -0,0 +1,125 @@
+#
+# Makefile --
+#
+# Makefile for building plug-ins for scripting.
+#
+# Copyright (c) 2001-2013 Bjorn Gustavsson
+#
+# See the file "license.terms" for information on usage and redistribution
+# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+#
+# $Id: Makefile,v 1.14 2006/08/02 22:44:40 antoneos Exp $
+#
+include ../../erl.mk
+
+.SUFFIXES: .erl .jam .beam .yrl .xrl .bin .mib .hrl .sgml .html .ps .3 .1 \
+ .fig .dvi .tex .class .java .pdf .psframe .pscrop
+
+ESRC=.
+WINGS_INTL=../../intl_tools
+EBIN=../../plugins/scripting
+WINGS_TOP=../../..
+WINGS_E3D=../../e3d
+
+ifeq ($(TYPE),debug)
+TYPE_FLAGS=-DDEBUG
+else
+TYPE_FLAGS=
+endif
+
+MODULES= \
+ wpc_scripting_shapes \
+ scripting_engines
+
+INIT_SCRIPT_SRC=$(ESRC)/init_scripts
+INIT_SCRIPT_EBIN=$(EBIN)/wpc_scripting_shapes_init
+INIT_SCRIPT_PY_SRC=$(INIT_SCRIPT_SRC)/py
+INIT_SCRIPT_PY_EBIN=$(INIT_SCRIPT_EBIN)/py
+INIT_SCRIPT_SCM_SRC=$(INIT_SCRIPT_SRC)/scm
+INIT_SCRIPT_SCM_EBIN=$(INIT_SCRIPT_EBIN)/scm
+
+TARGET_FILES= $(MODULES:%=$(EBIN)/%.beam)
+
+
+# ----------------------------------------------------
+# FLAGS
+# ----------------------------------------------------
+ERL_COMPILE_FLAGS += -Werror +nowarn_match_float_zero -I $(WINGS_TOP) \
+ $(TYPE_FLAGS) -pa $(WINGS_INTL) +debug_info
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+
+opt debug:
+ $(MAKE) TYPE=$@ common
+
+template: opt
+ $(ERL) -pa $(WINGS_INTL) -noinput -run tools generate_template_files $(EBIN)
+
+lang: template
+ cp *.lang $(EBIN)
+ $(ERL) -pa $(WINGS_INTL) -noinput -run tools diff_lang_files $(EBIN)
+
+common: $(TARGET_FILES) $(INIT_SCRIPT_EBIN) subdirs
+
+subdirs:
+ (cd docs; $(MAKE))
+
+clean:
+ rm -f $(TARGET_FILES)
+ rm -f core
+ (cd docs; $(MAKE) clean)
+
+SH?=sh
+
+$(EBIN)/%.beam: $(ESRC)/%.erl
+ $(ERLC) $(ERL_COMPILE_FLAGS) -o$(EBIN) $<
+
+PY_FILES= init w3d_e3d w3d_int w3d_newshape
+PY_FILES_1=$(PY_FILES:%=$(INIT_SCRIPT_PY_EBIN)/%.py)
+
+SCM_FILES= init_env_csi init_env_gauche
+SCM_FILES_1=$(SCM_FILES:%=$(INIT_SCRIPT_SCM_EBIN)/%.scm)
+
+$(INIT_SCRIPT_EBIN): $(INIT_SCRIPT_EBIN)/callable.conf \
+ $(INIT_SCRIPT_EBIN)/defaults.conf \
+ $(INIT_SCRIPT_EBIN)/python.script-init-conf \
+ $(INIT_SCRIPT_EBIN)/scheme.script-init-conf \
+ $(INIT_SCRIPT_PY_EBIN)/w3d_we.py \
+ $(INIT_SCRIPT_SCM_EBIN)/init.scm \
+ $(PY_FILES_1) \
+ $(SCM_FILES_1)
+
+
+$(INIT_SCRIPT_EBIN)/%.conf: $(INIT_SCRIPT_SRC)/%.conf
+ cp $< $(INIT_SCRIPT_EBIN)/
+$(INIT_SCRIPT_EBIN)/%.script-init-conf: $(INIT_SCRIPT_SRC)/%.script-init-conf
+ cp $< $(INIT_SCRIPT_EBIN)/
+$(INIT_SCRIPT_PY_EBIN)/%.py: $(INIT_SCRIPT_PY_SRC)/%.py
+ cp $< $(INIT_SCRIPT_PY_EBIN)/
+$(INIT_SCRIPT_SCM_EBIN)/%.scm: $(INIT_SCRIPT_SCM_SRC)/%.scm
+ cp $< $(INIT_SCRIPT_SCM_EBIN)/
+
+# Generate w3d_we.py file from callable.conf
+$(INIT_SCRIPT_PY_EBIN)/w3d_we.py: $(INIT_SCRIPT_PY_SRC)/w3d_we.1.py \
+ $(INIT_SCRIPT_SRC)/callable.conf \
+ $(INIT_SCRIPT_SRC)/py-modnames
+ $(SH) ./tools/gen-init-we-script.sh \
+ "$(INIT_SCRIPT_PY_SRC)/w3d_we.1.py" \
+ - \
+ "$(INIT_SCRIPT_SRC)/callable.conf" \
+ "$(INIT_SCRIPT_SRC)/py-modnames" py \
+ > "$(INIT_SCRIPT_PY_EBIN)/w3d_we.py"
+
+# Generate init.scm file from callable.conf
+$(INIT_SCRIPT_SCM_EBIN)/init.scm: $(INIT_SCRIPT_SCM_SRC)/init.1.scm \
+ $(INIT_SCRIPT_SRC)/callable.conf \
+ $(INIT_SCRIPT_SRC)/scm-modnames \
+ $(INIT_SCRIPT_SCM_SRC)/init.2.scm
+ $(SH) ./tools/gen-init-we-script.sh \
+ "$(INIT_SCRIPT_SCM_SRC)/init.1.scm" \
+ "$(INIT_SCRIPT_SCM_SRC)/init.2.scm" \
+ "$(INIT_SCRIPT_SRC)/callable.conf" \
+ "$(INIT_SCRIPT_SRC)/scm-modnames" scm \
+ > "$(INIT_SCRIPT_SCM_EBIN)/init.scm"
+
diff --git a/plugins_src/scripting/docs/Makefile b/plugins_src/scripting/docs/Makefile
new file mode 100644
index 000000000..b6466d6d4
--- /dev/null
+++ b/plugins_src/scripting/docs/Makefile
@@ -0,0 +1,108 @@
+#
+# Makefile --
+#
+# Makefile for building scripting documentation
+#
+# Copyright (c) 2025 Edward Blake
+#
+# See the file "license.terms" for information on usage and redistribution
+# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+#
+
+AWK?=awk
+MKDIR?=mkdir -p
+PACK_MANUAL?=../../../tools/pack_manual
+ESCRIPT?=escript
+
+FILES= \
+ en/INDEX \
+ en/README.txt \
+ en/wscr/intro.txt \
+ en/wscr/langfiles.txt \
+ en/wscr/pluginscripts.txt \
+ en/wscr/query.txt \
+ en/wscr/scriptfolders.txt \
+ en/wscr/directives/type.txt \
+ en/wscr/directives/name.txt \
+ en/wscr/directives/mode.txt \
+ en/wscr/directives/desc.txt \
+ en/wscr/directives/include.txt \
+ en/wscr/directives/params_title.txt \
+ en/wscr/directives/params_templates.txt \
+ en/wscr/directives/template.txt \
+ en/wscr/directives/params.txt \
+ en/wscr/directives/param.txt \
+ en/wscr/directives/extensions.txt \
+ en/wscr/directives/params_init.txt \
+ en/wscr/directives/params_set.txt \
+ en/wscr/directives/export_param.txt \
+ en/wscr/directives/import_param.txt \
+ en/wscr/directives/adv_param.txt \
+ en/wscr/directives/params_preview.txt \
+ en/wscr/directives/command_changes.txt \
+ en/wscr/directives/command_inputs.txt \
+ en/wscr/directives/extra_file.txt \
+ en/wscr/directives/script_param.txt \
+ en/wscr/directives/hradio.txt \
+ en/wscr/directives/menu.txt \
+ en/wscr/directives/browse.txt \
+ en/wscr/directives/checkbox.txt \
+ en/wscr/directives/vradio.txt \
+ en/scheme/command_scripts.txt \
+ en/scheme/exporter_scripts.txt \
+ en/scheme/importer_scripts.txt \
+ en/scheme/intro.txt \
+ en/scheme/langres.txt \
+ en/scheme/new_shape_scripts.txt \
+ en/scheme/functions/e3d_face.txt \
+ en/scheme/functions/e3d_file.txt \
+ en/scheme/functions/e3d_image.txt \
+ en/scheme/functions/e3d_mesh.txt \
+ en/scheme/functions/e3d_object.txt \
+ en/python/command_scripts.txt \
+ en/python/exporter_scripts.txt \
+ en/python/importer_scripts.txt \
+ en/python/intro.txt \
+ en/python/langres.txt \
+ en/python/new_shape_scripts.txt \
+ en/python/functions/e3d_face.txt \
+ en/python/functions/e3d_file.txt \
+ en/python/functions/e3d_image.txt \
+ en/python/functions/e3d_mesh.txt \
+ en/python/functions/e3d_object.txt \
+ en/python/functions/list_of_arrays.txt \
+ en/python/functions/list_of_tuples.txt \
+ en/python/functions/material.txt \
+ en/python/functions/material_maps.txt \
+ en/python/functions/material_opengl.txt \
+ en/python/functions/new_shape.txt \
+ en/python/functions/output_list.txt
+
+SCRIPTINITDIR=../../../plugins/scripting/wpc_scripting_shapes_init
+
+all: ${SCRIPTINITDIR}/scripting-reference-en.manual
+
+clean:
+ rm generated/en/scm-we.txt
+ rm generated/en/py-we.txt
+ rmdir generated/en
+ rmdir generated
+
+
+generated/en:
+ $(MKDIR) generated/en
+
+generated/en/scm-we.txt: generated/en funs
+ $(AWK) -v type=scm -f ../tools/gen-funs-doc.awk < funs > generated/en/scm-we.txt
+
+generated/en/py-we.txt: generated/en funs
+ $(AWK) -v type=py -f ../tools/gen-funs-doc.awk < funs > generated/en/py-we.txt
+
+${SCRIPTINITDIR}/scripting-reference-en.manual: $(FILES) \
+ generated/en/scm-we.txt \
+ generated/en/py-we.txt
+ $(ESCRIPT) $(PACK_MANUAL) en \
+ --out ${SCRIPTINITDIR}/scripting-reference-en.manual
+
+
+
diff --git a/plugins_src/scripting/docs/en/INDEX b/plugins_src/scripting/docs/en/INDEX
new file mode 100644
index 000000000..0bcb3942d
--- /dev/null
+++ b/plugins_src/scripting/docs/en/INDEX
@@ -0,0 +1,94 @@
+{info,[
+ {title, "Scripting Reference"},
+ {language, "en"}
+]}.
+{txt, "README", "README.txt"}.
+{"Scripting Information",[
+ {"Introduction",[
+ {txt, "Introduction", "wscr/intro.txt"},
+ {txt, "Script Folders", "wscr/scriptfolders.txt"},
+ {txt, "Plugin Scripts", "wscr/pluginscripts.txt"},
+ {txt, "Language files", "wscr/langfiles.txt"},
+ {txt, "Query Mini Language", "wscr/query.txt"}
+ ]},
+ {"Directive Reference",[
+ {txt, "type Directive", "wscr/directives/type.txt"},
+ {txt, "name Directive", "wscr/directives/name.txt"},
+ {txt, "mode Directive", "wscr/directives/mode.txt"},
+ {txt, "desc Directive", "wscr/directives/desc.txt"},
+ {txt, "include Directive", "wscr/directives/include.txt"},
+ {txt, "params_title Directive", "wscr/directives/params_title.txt"},
+ {txt, "params_templates Directive", "wscr/directives/params_templates.txt"},
+ {txt, "template Directive", "wscr/directives/template.txt"},
+ {txt, "params Directive", "wscr/directives/params.txt"},
+ {txt, "param Directive", "wscr/directives/param.txt"},
+ {txt, "extensions Directive", "wscr/directives/extensions.txt"},
+ {txt, "params_init Directive", "wscr/directives/params_init.txt"},
+ {txt, "params_set Directive", "wscr/directives/params_set.txt"},
+ {txt, "export_param Directive", "wscr/directives/export_param.txt"},
+ {txt, "import_param Directive", "wscr/directives/import_param.txt"},
+ {txt, "adv_param Directive", "wscr/directives/adv_param.txt"},
+ {txt, "params_preview Directive", "wscr/directives/params_preview.txt"},
+ {txt, "script_param Directive", "wscr/directives/script_param.txt"},
+ {txt, "extra_file Directive", "wscr/directives/extra_file.txt"},
+ {txt, "command_inputs Directive", "wscr/directives/command_inputs.txt"},
+ {txt, "command_changes Directive", "wscr/directives/command_changes.txt"},
+ {txt, "hradio Directive", "wscr/directives/hradio.txt"},
+ {txt, "menu Directive", "wscr/directives/menu.txt"},
+ {txt, "browse Directive", "wscr/directives/browse.txt"},
+ {txt, "checkbox Directive", "wscr/directives/checkbox.txt"},
+ {txt, "vradio Directive", "wscr/directives/vradio.txt"}
+ ]}
+]}.
+{"Python",[
+ {"Introduction",[
+ {txt, "Online Resources for the Python Language", "python/langres.txt"},
+ {txt, "Python Intro", "python/intro.txt"},
+ {txt, "New Shape Scripts", "python/new_shape_scripts.txt"},
+ {txt, "Command Scripts", "python/command_scripts.txt"},
+ {txt, "Importer Scripts", "python/importer_scripts.txt"},
+ {txt, "Exporter Scripts", "python/exporter_scripts.txt"}
+ ]},
+ {"OutputList Function Reference",[
+ {txt, "OutputList", "python/functions/output_list.txt"}
+ ]},
+ {"NewShape (w3d_newshape) Function Reference",[
+ {txt, "ListOfArrays", "python/functions/list_of_arrays.txt"},
+ {txt, "ListOfTuples", "python/functions/list_of_tuples.txt"},
+ {txt, "NewShape", "python/functions/new_shape.txt"}
+ ]},
+ {"E3D (w3d_e3d) Function Reference",[
+ {txt, "E3DFace", "python/functions/e3d_face.txt"},
+ {txt, "E3DMesh", "python/functions/e3d_mesh.txt"},
+ {txt, "E3DObject", "python/functions/e3d_object.txt"},
+ {txt, "E3DImage", "python/functions/e3d_image.txt"},
+ {txt, "MaterialMaps", "python/functions/material_maps.txt"},
+ {txt, "MaterialOpenGLAttributes", "python/functions/material_opengl.txt"},
+ {txt, "Material", "python/functions/material.txt"},
+ {txt, "E3DFile", "python/functions/e3d_file.txt"}
+ ]},
+ {"We (w3d_we) Function Reference",[
+ {txt, "we", "../generated/en/py-we.txt"}
+ ]}
+]}.
+{"Scheme",[
+ {"Introduction",[
+ {txt, "Online Resources for the Scheme Language", "scheme/langres.txt"},
+ {txt, "Scheme Intro", "scheme/intro.txt"},
+ {txt, "New Shape Scripts", "scheme/new_shape_scripts.txt"},
+ {txt, "Command Scripts", "scheme/command_scripts.txt"},
+ {txt, "Importer Scripts", "scheme/importer_scripts.txt"},
+ {txt, "Exporter Scripts", "scheme/exporter_scripts.txt"}
+ ]},
+ {"E3D Function Reference",[
+ {txt, "e3d_face", "scheme/functions/e3d_face.txt"},
+ {txt, "e3d_mesh", "scheme/functions/e3d_mesh.txt"},
+ {txt, "e3d_object", "scheme/functions/e3d_object.txt"},
+ {txt, "e3d_file", "scheme/functions/e3d_file.txt"},
+ {txt, "e3d_image", "scheme/functions/e3d_image.txt"}
+ ]},
+ {"We Function Reference",[
+ {txt, "we", "../generated/en/scm-we.txt"}
+ ]}
+]}.
+
diff --git a/plugins_src/scripting/docs/en/README.txt b/plugins_src/scripting/docs/en/README.txt
new file mode 100644
index 000000000..4761a2a52
--- /dev/null
+++ b/plugins_src/scripting/docs/en/README.txt
@@ -0,0 +1,18 @@
+Scripting Plugin for Wings3D
+
+This reference contains three sections:
+
+Script Information (.wscr)
+
+Scheme Scripting
+
+Python Scripting
+
+
+CONTRIBUTORS FOR DOCUMENTATION
+
+Edward Blake has written the documentation for the first
+release of the scripting plugin.
+
+
+
diff --git a/plugins_src/scripting/docs/en/python/command_scripts.txt b/plugins_src/scripting/docs/en/python/command_scripts.txt
new file mode 100644
index 000000000..cc1bb05a9
--- /dev/null
+++ b/plugins_src/scripting/docs/en/python/command_scripts.txt
@@ -0,0 +1,230 @@
+Command Scripts
+
+Command scripts are available when right clicking on a selected object.
+
+The "points" extra parameter is needed to access the list of vertices, which come as a vertice number and a 3 dimensional position.
+
+
+
+Don't forget to assign the 'main' function to 'w3d_main_function'.
+
+The following script shifts all the vertice positions of a selected object by 0.5 in all axis:
+
+
+import w3d_int
+
+def main(params, params_by_key, extra_params):
+ points = w3d_int.Points()
+ points.load_from(extra_params["points"])
+
+ ##
+ ## ...
+ ##
+
+ ## Return new points positions
+ return [w3d_int.SetPoints(points)]
+
+w3d_main_function = main
+
+
+The op variable contains which mode the command script is in, such as "vertex", "edge", "face" and "body". The modes the script runs in is set with the mode directive in the .wscr file.
+
+An .wscr file to use with the script:
+
+
+import w3d_int
+
+def command(params, params_by_key, extra_params):
+ op = extra_params["op"]
+ points = w3d_int.Points()
+ points.load_from(extra_params["points"])
+
+ newpoints = []
+ for pair in points.list:
+ x = pair[1][0] + 0.5
+ y = pair[1][1] + 0.5
+ z = pair[1][2] + 0.5
+ newpoints.append((pair[0], (x,y,z)))
+ return [w3d_int.SetPoints(newpoints)]
+
+w3d_main_function = main
+
+
+The .wscr and .scm file should have the same name before the extension (script.scm and script.wscr).
+
+To use parameters with the command script, such as shifting the points by an adjustable value:
+
+
+type "py"
+name ?__(1,"Example Simple Script")
+desc ?__(2,"Example Simple Script")
+mode "body"
+params_title ?__(3,"Example Simple Script")
+command_inputs "points"
+command_changes "points"
+params {
+ param "Unused" 0.0
+}
+
+
+In the .wscr file, rename the "Unused" parameter to "Move":
+
+
+import w3d_int
+
+def command(params, params_by_key, extra_params):
+ op = extra_params["op"]
+ val = params[0]
+ points = w3d_int.Points()
+ points.load_from(extra_params["points"])
+
+ newpoints = []
+ for pair in points.list:
+ x = pair[1][0] + val
+ y = pair[1][1] + val
+ z = pair[1][2] + val
+ newpoints.append((pair[0], (x,y,z)))
+ return [w3d_int.SetPoints(newpoints)]
+
+w3d_main_function = main
+
+
+
+
+Changes
+
+Depending on the kind of changes you are making, you add to the list:
+SetPoints
+SetFaceUVs
+SetFaceColors
+SetE3dMesh
+
+You also need to add to the command_inputs and command_changes directives the data needed, and what data will be changed. For the script using set_points, the following is needed in the .wscr file:
+
+type "py"
+name ?__(1,"Example Simple Script")
+desc ?__(2,"Example Simple Script")
+mode "body"
+params_title ?__(3,"Example Simple Script")
+command_inputs "points"
+command_changes "points"
+params {
+ param "Move" 0.5
+}
+
+
+
+Advanced Commands
+
+Simple command scripts can be used to change the point coordinates, the UV values and colors. For more complicated tasks where the mesh needs more complicated changes, there are two options: E3DMesh scripts and We function based scripts.
+
+The E3DMesh script option the .wscr needs to specify the following options:
+
+command_inputs "points"
+command_changes "points"
+
+
+With this option, the script gets a E3DMesh object, and the E3DMesh can be changed, and then returned. Then the object's winged edge structure is recreated. This option is most useful when the script is a wrapper for external tools that expect meshes as list of faces and vertex array such as in a .obj file. A caveat of this option is the mesh will have to be turned into a list of faces and vertices, and then rebuilt back to a winged edge structure, so faces, edges and vertices will be renumbered.
+
+An example command that just returns the E3DMesh unchanged:
+
+
+command_inputs "e3d_mesh"
+command_changes "e3d_mesh"
+
+
+An example of moving the object by 0.5 in all axis:
+
+
+from w3d_e3d import E3DMesh, SetE3DMesh
+
+def command(params, params_by_key, extra_params):
+ op = extra_params["op"]
+ mesh = E3DMesh()
+ mesh.load_from(extra_params["e3d_mesh"])
+
+ return [SetE3DMesh(mesh)]
+
+w3d_main_function = command
+
+
+An .wscr file to use with the script:
+
+
+from w3d_e3d import E3DMesh, SetE3DMesh
+
+def command(params, params_by_key, extra_params):
+ op = extra_params["op"]
+ mesh = E3DMesh()
+ mesh.load_from(extra_params["e3d_mesh"])
+
+ points = mesh.vs
+
+ newpoints = []
+ for (x,y,z) in points:
+ newpoints.append((x + 0.5, y + 0.5, z + 0.5))
+
+ mesh.vs = newpoints
+
+ return [SetE3DMesh(mesh)]
+
+w3d_main_function = command
+
+
+
+Advanced We Function Scripts
+
+Scripts can use the we functions from Wings3D directly. When using this option, the command script does not need to use the command_inputs and command_changes directives, and the return from the script can be an empty list. Instead the script contains a sequence of we function calls that modifies the we object.
+
+
+An example of moving the object by 0.5 in all axis using a we function, in this case, matrix transformation of the vertices:
+
+
+type "py"
+name ?__(1,"Example E3D Mesh Script")
+desc ?__(2,"Example E3D Mesh Script")
+mode "body"
+params_title ?__(3,"Example E3D Mesh Script")
+command_inputs "e3d_mesh"
+command_changes "e3d_mesh"
+params {
+ param "Unused" 0.0
+}
+
+
+A list of all the available callable we functions is available in the w3d_we function references.
+
+An .wscr file to use for the we function command script:
+
+
+import w3d_we
+
+def command(params, params_by_key, extra_params):
+ op = extra_params["op"]
+
+ move_x = 0.5
+ move_y = 0.5
+ move_z = 0.5
+ w3d_we.we__transform_vs([
+ 1.0, 0.0, 0.0,
+ 0.0, 1.0, 0.0,
+ 0.0, 0.0, 1.0,
+ move_x, move_y, move_z])
+
+ return []
+
+w3d_main_function = command
+
+
+
+
diff --git a/plugins_src/scripting/docs/en/python/exporter_scripts.txt b/plugins_src/scripting/docs/en/python/exporter_scripts.txt
new file mode 100644
index 000000000..0185b1092
--- /dev/null
+++ b/plugins_src/scripting/docs/en/python/exporter_scripts.txt
@@ -0,0 +1,105 @@
+Exporter Scripts
+
+Exporter scripts are available from the "File" > "Export" and "File" > "Export Selected" menus.
+
+Read E3DFile
+
+The "filename" and "content" extra parameters are needed, the file name is chosen by the user to save the model, and the content contains an e3d_file data structure.
+
+
+type "py"
+name ?__(1,"Example We Script")
+desc "Example We Script"
+mode "body"
+params_title ?__(2,"Example We Script")
+params_preview 1
+params {
+ param "Unused" 0.0
+}
+
+
+Don't forget to assign the 'main' function to 'w3d_main_function'.
+
+Exporters take in both a filename and the e3d_file content which has to be loaded with E3DFile.load_from(...). After writing the file to disk, create an "ok" tuple and write its output list out:
+
+
+def main(params, params_by_key, extra_params):
+ filename = extra_params["filename"]
+ content = w3d_e3d.E3DFile()
+ content.load_from(extra_params["content"])
+
+ ##
+ ## ...
+ ##
+
+ ## Return Okay() if the file was written successfully.
+ return Okay()
+
+w3d_main_function = main
+
+
+
+A minimal exporter that writes vertices to a file:
+
+
+ return Okay()
+
+
+
+A .wscr file for the exporter:
+
+
+import w3d_e3d
+from os import path
+
+def export_fun(attr, filename, content):
+ objs = content.objs
+ with open(filename, "w", encoding="utf-8") as fp:
+ # Number of objects
+ fp.write(str(len(objs)) + "\n")
+ for obj in objs:
+ mesh = obj.obj
+ vs = mesh.vs
+ faces = mesh.fs
+ # Object name
+ fp.write(str(obj.name) + "\n")
+ # Number of vertices
+ fp.write(str(len(vs)) + "\n")
+ for v in vs:
+ x = v[0]
+ y = v[1]
+ z = v[2]
+ # Vertice coordinate
+ fp.write(str(x) + " " + str(y) + " " + str(z) + "\n")
+ # Number of faces
+ fp.write(str(len(faces)) + "\n")
+ for face in faces:
+ vlist = face.vs
+ for v in vlist:
+ fp.write(str(v) + " ")
+ fp.write("\n")
+
+ return Okay()
+
+def exporter(params, params_by_key, extra_params):
+ filename = extra_params["filename"]
+ content = w3d_e3d.E3DFile()
+ content.load_from(extra_params["content"])
+ return export_fun({}, filename, content)
+
+w3d_main_function = exporter
+
+
+
+The .wscr and .scm file should have the same name before the extension (script.scm and script.wscr).
+
+
+
diff --git a/plugins_src/scripting/docs/en/python/functions/e3d_face.txt b/plugins_src/scripting/docs/en/python/functions/e3d_face.txt
new file mode 100644
index 000000000..a7b350884
--- /dev/null
+++ b/plugins_src/scripting/docs/en/python/functions/e3d_face.txt
@@ -0,0 +1,17 @@
+class E3DFace
+
+Visible attributes vs, vc, tx, ns, mat, sg, vis
+
+E3DFace()
+
+Creates a new E3DFace instance
+
+face.as_output_list()
+
+Outputs the whole E3DFace instance and all its values into an output list, not usually used directly.
+
+face.load_from(expr)
+
+Load contents into a E3DFace instance, not usually used directly.
+
+
diff --git a/plugins_src/scripting/docs/en/python/functions/e3d_file.txt b/plugins_src/scripting/docs/en/python/functions/e3d_file.txt
new file mode 100644
index 000000000..539e7360d
--- /dev/null
+++ b/plugins_src/scripting/docs/en/python/functions/e3d_file.txt
@@ -0,0 +1,31 @@
+class E3DFile
+
+Visible attributes objs, mat, creator, dir
+
+E3DFile()
+
+Creates a new E3DFile instance
+
+e3df.as_output_list()
+
+Outputs the whole E3DFile instance and all its values into an output list which is then to be written to stdout
+
+
+type "py"
+name ?__(1,"Example Exporter (.txt)")
+mode "export"
+params_title ?__(3,"Example Exporter Settings")
+params_templates {
+ template export include_normals,include_uvs,include_colors
+}
+params_set {
+ export_param include_uvs %[bool(params/include_uvs)]
+ export_param subdivisions %[params/subdivisions]
+ export_param include_normals %[bool(params/include_normals)]
+ export_param include_hard_edges 'true'
+ export_param script_texture_convert 'user'
+}
+extensions {
+ ext .txt ?__(5,"Text Mesh")
+}
+
+
+e3df.load_from(expr)
+
+Load contents into a E3DFile instance
+
+
+b = w3d_e3d.E3DFile()
+b.objs.append(E3DObject())
+o_ok = OutputList()
+o_ok.add_symbol("ok")
+o_ok.add_list(b.as_output_list())
+o_ok.write_list_out(sys.stdout)
+
+
+
diff --git a/plugins_src/scripting/docs/en/python/functions/e3d_image.txt b/plugins_src/scripting/docs/en/python/functions/e3d_image.txt
new file mode 100644
index 000000000..38e573c89
--- /dev/null
+++ b/plugins_src/scripting/docs/en/python/functions/e3d_image.txt
@@ -0,0 +1,17 @@
+class E3DImage
+
+Visible attributes type, bytes_pp, alignment, order, width, height, image, filename, name, extra
+
+E3DImage()
+
+Creates a new E3DImage instance
+
+image.as_output_list()
+
+Outputs the whole E3DImage instance and all its values into an output list, not usually used directly.
+
+image.load_from(expr)
+
+Load contents into a E3DImage instance, not usually used directly.
+
+
diff --git a/plugins_src/scripting/docs/en/python/functions/e3d_mesh.txt b/plugins_src/scripting/docs/en/python/functions/e3d_mesh.txt
new file mode 100644
index 000000000..c64f8784a
--- /dev/null
+++ b/plugins_src/scripting/docs/en/python/functions/e3d_mesh.txt
@@ -0,0 +1,17 @@
+class E3DMesh
+
+Visible attributes: type, vs, vc, tx, ns, fs, he, matrix
+
+E3DMesh()
+
+Creates a new E3DMesh instance
+
+mesh.as_output_list()
+
+Outputs the whole E3DMesh instance and all its values into an output list, not usually used directly.
+
+mesh.load_from(expr)
+
+Load contents into a E3DMesh instance, not usually used directly.
+
+
diff --git a/plugins_src/scripting/docs/en/python/functions/e3d_object.txt b/plugins_src/scripting/docs/en/python/functions/e3d_object.txt
new file mode 100644
index 000000000..9f9cf170f
--- /dev/null
+++ b/plugins_src/scripting/docs/en/python/functions/e3d_object.txt
@@ -0,0 +1,17 @@
+class E3DObject
+
+Visible attributes name, obj, mat, attr
+
+E3DObject()
+
+Creates a new E3DObject instance
+
+obj.as_output_list()
+
+Outputs the whole E3DObject instance and all its values into an output list, not usually used directly.
+
+obj.load_from(expr)
+
+Load contents into a E3DObject instance, not usually used directly.
+
+
diff --git a/plugins_src/scripting/docs/en/python/functions/list_of_arrays.txt b/plugins_src/scripting/docs/en/python/functions/list_of_arrays.txt
new file mode 100644
index 000000000..f2537c182
--- /dev/null
+++ b/plugins_src/scripting/docs/en/python/functions/list_of_arrays.txt
@@ -0,0 +1,13 @@
+class ListOfArrays
+
+Visible attributes: l
+
+ListOfArrays()
+
+Create a new empty ListOfArrays instance
+
+list.as_output_list()
+
+Return an OutputList instance
+
+
diff --git a/plugins_src/scripting/docs/en/python/functions/list_of_tuples.txt b/plugins_src/scripting/docs/en/python/functions/list_of_tuples.txt
new file mode 100644
index 000000000..ea9bb4848
--- /dev/null
+++ b/plugins_src/scripting/docs/en/python/functions/list_of_tuples.txt
@@ -0,0 +1,14 @@
+class ListOfTuples
+
+Visible attributes: l
+
+ListOfTuples()
+
+Create a new empty ListOfTuples instance
+
+list.as_output_list()
+
+Return an OutputList instance
+
+
+
diff --git a/plugins_src/scripting/docs/en/python/functions/material.txt b/plugins_src/scripting/docs/en/python/functions/material.txt
new file mode 100644
index 000000000..fcbd2b033
--- /dev/null
+++ b/plugins_src/scripting/docs/en/python/functions/material.txt
@@ -0,0 +1,18 @@
+class Material
+
+Visible attributes name, attrs
+
+Material()
+
+Creates a new Material instance
+
+mat.as_output_list()
+
+Outputs the whole Material instance and all its values into an output list,
+ not usually used directly.
+
+mat.load_from(expr)
+
+Load contents into a Material instance, not usually used directly.
+
+
diff --git a/plugins_src/scripting/docs/en/python/functions/material_maps.txt b/plugins_src/scripting/docs/en/python/functions/material_maps.txt
new file mode 100644
index 000000000..8ca61d150
--- /dev/null
+++ b/plugins_src/scripting/docs/en/python/functions/material_maps.txt
@@ -0,0 +1,17 @@
+class MaterialMaps
+
+Visible attributes maps
+
+MaterialMaps()
+
+Create a new MaterialMaps instance
+
+maps.as_output_list()
+
+Outputs the whole MaterialMaps instance and all its values into an output list, not usually used directly.
+
+maps.load_from(tlist)
+
+Load contents into a MaterialMaps instance, not usually used directly.
+
+
diff --git a/plugins_src/scripting/docs/en/python/functions/material_opengl.txt b/plugins_src/scripting/docs/en/python/functions/material_opengl.txt
new file mode 100644
index 000000000..b5e3d6d35
--- /dev/null
+++ b/plugins_src/scripting/docs/en/python/functions/material_opengl.txt
@@ -0,0 +1,17 @@
+class MaterialOpenGLAttributes
+
+Visible attributes ambient, specular, shininess, diffuse, emission, metallic, roughness, vertex_colors
+
+MaterialOpenGLAttributes()
+
+Creates a new MaterialOpenGLAttributes instance
+
+opengl.as_output_list()
+
+Outputs the whole MaterialOpenGLAttributes instance and all its values into an output list, not usually used directly.
+
+opengl.load_from(tlist)
+
+Load contents into a MaterialOpenGLAttributes instance, not usually used directly.
+
+
diff --git a/plugins_src/scripting/docs/en/python/functions/new_shape.txt b/plugins_src/scripting/docs/en/python/functions/new_shape.txt
new file mode 100644
index 000000000..639d7ba02
--- /dev/null
+++ b/plugins_src/scripting/docs/en/python/functions/new_shape.txt
@@ -0,0 +1,22 @@
+class NewShape
+
+Visible attributes prefix, fs, vs, obj, mat
+
+NewShape()
+
+Create a NewShape instance
+
+shape.as_output_list()
+
+Return an OutputList instance, the kind of tuple that is returned is different depending on if obj is set, if it is set the tuple contains obj and mat. If it is not set, it returns a tuple with fs and vs.
+
+
+content = w3d_e3d.E3DFile()
+content.load_from(extra_params["content"])
+
+
diff --git a/plugins_src/scripting/docs/en/python/functions/output_list.txt b/plugins_src/scripting/docs/en/python/functions/output_list.txt
new file mode 100644
index 000000000..e33bdd1b6
--- /dev/null
+++ b/plugins_src/scripting/docs/en/python/functions/output_list.txt
@@ -0,0 +1,60 @@
+class OutputList
+
+Visible attributes lst_cont, lst_type
+
+Python is somewhat different from Erlang and Scheme for their data structures, so to serialize objects for input and output with the script plugin, a helper class called OutputList is used. Other helper classes also use OutputList to be able to load and write data to and from Wings3D.
+
+
+OutputList()
+
+Create a new empty OutputList instance
+
+list.add_symbol(a)
+
+Add a symbol (interned string) to the list
+
+list.add_str(a)
+
+Add a string to the list
+
+list.add_number(a)
+
+Add a number to the list
+
+list.add_numbers(alst)
+
+Add several numbers to the list, it does not add a sublist
+
+list.add_integer(a)
+
+Add an integer to the list
+
+list.add_integers(alst)
+
+Add several integers to the list, it does not add a sublist
+
+list.add_float(a)
+
+Add a floating point number to the list
+
+list.add_floats(alst)
+
+Add several floating point numbers to the list, it does not add a sublist
+
+list.add_list(a)
+
+Add a sublist to the list
+
+list.add_vector(a)
+
+Add a vector (tuple) to the list
+
+list.add(a, typ)
+
+Add an item with type number to the list, this method shouldn't be used directly.
+
+list.write_list_out(ost)
+
+Write the list to the stream ost
+
+
diff --git a/plugins_src/scripting/docs/en/python/importer_scripts.txt b/plugins_src/scripting/docs/en/python/importer_scripts.txt
new file mode 100644
index 000000000..5890f3572
--- /dev/null
+++ b/plugins_src/scripting/docs/en/python/importer_scripts.txt
@@ -0,0 +1,120 @@
+Importer Scripts
+
+Importer scripts are available from the "File" > "Import" menu.
+
+The "filename" extra parameter is needed which contain the file path of the file to read:
+
+
+e3d_o = w3d_e3d.E3DObject()
+e3d_o.obj = mesh
+shp = w3d_newshape.NewShape()
+shp.prefix = "shape"
+shp.obj = e3d_o
+o = shp.as_output_list()
+o.write_list_out(sys.stdout)
+
+
+Don't forget to assign the 'main' function to 'w3d_main_function'.
+
+Importers take the filename to read the file needed, construct a E3DObject, add it to an E3DFile and return the output list as part of an "ok" tuple.
+
+Constructing E3D objects involves instancing E3DFace, E3DObject, E3DFile, etc.
+
+
+def main(params, params_by_key, extra_params):
+ filename = extra_params["filename"]
+
+ ##
+ ## ...
+ ##
+
+ e3df = E3DFile()
+
+ ## After reading the file, return e3df
+ return Okay(ed3f)
+
+w3d_main_function = main
+
+
+
+A minimal importer that reads vertices to a file:
+
+
+ e3df = w3d_e3d.E3DFile()
+ e3df.objs = [obj] # obj is an E3DObject
+ return Okay(e3df)
+
+
+
+A .wscr file for the importer:
+
+
+import w3d_e3d
+from os import path
+
+def import_fun(attr, filename):
+ with open(filename, "r", encoding="utf-8") as fp:
+ # Number of objects
+ num_objs = int(fp.readline().rstrip())
+ objs = []
+ # Read each object
+ for i in range(0, num_objs):
+ # Object name
+ objname = fp.readline().rstrip()
+ # Number of vertices
+ num_vs = int(fp.readline().rstrip())
+ vs = []
+ for i2 in range(0, num_vs):
+ v = fp.readline().rstrip().split(" ")
+ vs.append((float(v[0]),float(v[1]),float(v[2])))
+ # Number of faces
+ num_faces = int(fp.readline().rstrip())
+ faces = []
+ for i2 in range(0, num_faces):
+ # Vertex indices
+ vf0 = fp.readline().rstrip().split(" ")
+ vf = []
+ for v in vf0:
+ vf.append(int(v))
+ face = w3d_e3d.E3DFace()
+ face.vs = vf
+ face.vc = []
+ face.tx = []
+ face.ns = []
+ faces.append(face)
+
+ mesh = w3d_e3d.E3DMesh()
+ mesh.type="polygon"
+ mesh.vs=vs
+ mesh.fs=faces
+ mesh.vc=[]
+ mesh.tx=[]
+ mesh.ns=[]
+ mesh.he=[]
+ obj = w3d_e3d.E3DObject()
+ obj.name = objname
+ obj.obj = mesh
+ objs.append(obj)
+
+ e3df = w3d_e3d.E3DFile()
+ e3df.objs = objs
+ return Okay(e3df)
+
+def importer(params, params_by_key, extra_params):
+ filename = extra_params["filename"]
+ return import_fun({}, filename)
+
+w3d_main_function = importer
+
+
+
+The .wscr and .scm file should have the same name before the extension (script.scm and script.wscr).
+
+
+
diff --git a/plugins_src/scripting/docs/en/python/intro.txt b/plugins_src/scripting/docs/en/python/intro.txt
new file mode 100644
index 000000000..f5612bcbf
--- /dev/null
+++ b/plugins_src/scripting/docs/en/python/intro.txt
@@ -0,0 +1,99 @@
+Introduction
+
+
+type "py"
+name ?__(1,"Example Importer (.txt)")
+mode "import"
+params_title ?__(3,"Example Importer Settings")
+params_templates {
+ template import
+}
+params_set {
+ import_param script_texture_convert 'auto'
+}
+extensions {
+ ext .txt ?__(5,"Text Mesh")
+}
+
+ w3d_int.wings_set_var("var1", [1, 2, 3, 4])
+
+
+The return value should be ["ok"]
+
+
+Get value of variable:
+
+
+ w3d_int.wings_get_var("var1")
+
+
+The reply should be [[1, 2, 3, 4]]
+
+
+Query:
+
+
+ w3d_int.wings_query("lists:reverse(<$'var1')")
+
+
+The reply should be [[4, 3, 2, 1]]
+
+
+import w3d_newshape
+
+import w3d_e3d
+
+import w3d_we
+
+
+
+
+class Examp:
+ def run(self):
+ sum = 0
+ for i in range(0, 100000000):
+ sum = sum + i
+
+print("Begin")
+lrp = LongRunningProcess()
+lrp.run(Examp())
+print("Done")
+
+
+
+
diff --git a/plugins_src/scripting/docs/en/python/langres.txt b/plugins_src/scripting/docs/en/python/langres.txt
new file mode 100644
index 000000000..cf57aa075
--- /dev/null
+++ b/plugins_src/scripting/docs/en/python/langres.txt
@@ -0,0 +1,13 @@
+Online Resources for the Python language
+
+A non-exhaustive list of resources:
+
+Python Tutorial
+https://docs.python.org/3/tutorial/
+
+Python Language Reference
+https://docs.python.org/3/reference/
+
+
+
+
diff --git a/plugins_src/scripting/docs/en/python/new_shape_scripts.txt b/plugins_src/scripting/docs/en/python/new_shape_scripts.txt
new file mode 100644
index 000000000..c3e3f8896
--- /dev/null
+++ b/plugins_src/scripting/docs/en/python/new_shape_scripts.txt
@@ -0,0 +1,123 @@
+New Shape Scripts
+
+New shape scripts are available when right clicking with no selection.
+
+A complete example of a Python script to create a new shape:
+
+
+import w3d_newshape
+import sys
+
+def main(params, params_by_key, extra_params):
+ nshp = w3d_newshape.NewShape()
+ nshp.fs = w3d_newshape.ListOfArrays([
+ [0,1,3,2],
+ [6,7,5,4],
+ [1,0,4,5],
+ [2,3,7,6],
+ [0,2,6,4],
+ [3,1,5,7]
+ ])
+ nshp.vs = w3d_newshape.ListOfTuples([
+ (-2.0, 0.5,-2.0),
+ (-2.0, 0.5, 2.0),
+ ( 2.0, 0.5,-2.0),
+ ( 2.0, 0.5, 2.0),
+ (-2.0,-0.5,-2.0),
+ (-2.0,-0.5, 2.0),
+ ( 2.0,-0.5,-2.0),
+ ( 2.0,-0.5, 2.0)
+ ])
+ return nshp
+
+w3d_main_function = main
+
+
+Don't forget to assign the 'main' function to 'w3d_main_function'.
+
+An .wscr to use with the script:
+
+
+type "py"
+name ?__(1,"Example New Shape")
+params_title ?__(3,"Example New Shape")
+params {
+param "Unused" 0.0
+}
+
+
+The .wscr and .py file should have the same name before the extension (script.py and script.wscr).
+
+To use parameters with the script, such as shifting the points by an adjustable value:
+
+
+import w3d_newshape
+import sys
+
+def main(params, params_by_key, extra_params):
+ xshift = params[0]
+ yshift = params[1]
+ zshift = params[2]
+ nshp = w3d_newshape.NewShape()
+ nshp.fs = w3d_newshape.ListOfArrays([
+ [0,1,3,2],
+ [6,7,5,4],
+ [1,0,4,5],
+ [2,3,7,6],
+ [0,2,6,4],
+ [3,1,5,7]
+ ])
+ nshp.vs = w3d_newshape.ListOfTuples([
+ (-2.0 + xshift, 0.5 + yshift, -2.0 + zshift),
+ (-2.0 + xshift, 0.5 + yshift, 2.0 + zshift),
+ ( 2.0 + xshift, 0.5 + yshift, -2.0 + zshift),
+ ( 2.0 + xshift, 0.5 + yshift, 2.0 + zshift),
+ (-2.0 + xshift, -0.5 + yshift, -2.0 + zshift),
+ (-2.0 + xshift, -0.5 + yshift, 2.0 + zshift),
+ ( 2.0 + xshift, -0.5 + yshift, -2.0 + zshift),
+ ( 2.0 + xshift, -0.5 + yshift, 2.0 + zshift)
+ ])
+ return nshp
+
+w3d_main_function = main
+
+
+In the .wscr file, replace the "Unused" parameter with "X", "Y" and "Z" parameters:
+
+
+type "py"
+name ?__(1,"Example New Shape")
+params_title ?__(3,"Example New Shape")
+params {
+param "X" 0.0
+param "Y" 0.0
+param "Z" 0.0
+}
+
+
+
+
+Another way to create a new shape where you might want to add colors, UVs, materials and hard edges is to make a new shape using a E3DObject. To create a shape, create a geometry with E3DMesh and E3DObject, and assign E3DObject to the obj attribute of a NewShape object.
+
+
+import w3d_newshape
+import w3d_e3d
+import sys
+
+def main(params, params_by_key, extra_params):
+ mesh = w3d_e3d.E3DMesh()
+ ...
+ e3d_o = w3d_e3d.E3DObject()
+ e3d_o.obj = mesh
+ nshp = w3d_newshape.NewShape()
+ nshp.prefix = "shape"
+ nshp.obj = e3d_o
+ return nshp
+
+w3d_main_function = main
+
+
+
+
+
+
diff --git a/plugins_src/scripting/docs/en/scheme/command_scripts.txt b/plugins_src/scripting/docs/en/scheme/command_scripts.txt
new file mode 100644
index 000000000..177756206
--- /dev/null
+++ b/plugins_src/scripting/docs/en/scheme/command_scripts.txt
@@ -0,0 +1,247 @@
+Command Scripts
+
+Command scripts are available when right clicking on a selected object
+
+The "points" extra parameter is needed to access the list of vertices, which come as a vertice number and a 3 dimensional position.
+
+
+(main-function
+ (lambda (*params* *extra-params*)
+ ;; The mode of the command: 'body 'face 'vertex 'edge
+ (define op (list-ref
+ (assoc "op" *extra-params*) 1))
+ ;; The list of selected items (if op is 'face , then sel
+ ;; contains a list of face numbers.
+ (define sel (list-ref
+ (assoc "sel" *extra-params*) 1))
+ ;; A "points" list is available if the command_inputs
+ ;; has "points" in its comma-separated list.
+ (define points (list-ref
+ (assoc "points" *extra-params*) 1))
+
+ ;; ...
+
+ ;; Return new positions of vertices
+ (list (list 'set_points points))
+ ))
+
+
+
+The following script shifts all the vertice positions of a selected object by 0.5 in all axis:
+
+
+(main-function
+ (lambda (*params* *extra-params*)
+ (define op (list-ref
+ (assoc "op" *extra-params*) 1))
+ (define points (list-ref
+ (assoc "points" *extra-params*) 1))
+ (define newpoints
+ (map (lambda (p)
+ (define a (vector-ref p 0))
+ (define b (vector-ref p 1))
+ (define x (vector-ref b 0))
+ (define y (vector-ref b 1))
+ (define z (vector-ref b 2))
+ (vector a (vector
+ (+ 0.5 x)
+ (+ 0.5 y)
+ (+ 0.5 z)))
+ ) points))
+ (list (list 'set_points newpoints ))
+ ))
+
+
+The op variable contains which mode the command script is in, such as "vertex", "edge", "face" and "body". The modes the script runs in is set with the mode directive in the .wscr file.
+
+An .wscr file to use with the script:
+
+
+type "scm"
+name ?__(1,"Example Simple Script")
+desc ?__(2,"Example Simple Script")
+mode "body"
+params_title ?__(3,"Example Simple Script")
+command_inputs "points"
+command_changes "points"
+params {
+ param "Unused" 0.0
+}
+
+
+The .wscr and .scm file should have the same name before the extension (script.scm and script.wscr).
+
+To use parameters with the command script, such as shifting the points by an adjustable value:
+
+
+(main-function
+ (lambda (*params* *extra-params*)
+ (define op (list-ref
+ (assoc "op" *extra-params*) 1))
+ (define points (list-ref
+ (assoc "points" *extra-params*) 1))
+ (define val (list-ref *params* 0))
+ (define newpoints
+ (map (lambda (p)
+ (define a (vector-ref p 0))
+ (define b (vector-ref p 1))
+ (define x (vector-ref b 0))
+ (define y (vector-ref b 1))
+ (define z (vector-ref b 2))
+ (vector a (vector
+ (+ val x)
+ (+ val y)
+ (+ val z)))
+ ) points))
+ (list (list 'set_points newpoints ))
+ ))
+
+
+In the .wscr file, rename the "Unused" parameter to "Move":
+
+
+type "scm"
+name ?__(1,"Example Simple Script")
+desc ?__(2,"Example Simple Script")
+mode "body"
+params_title ?__(3,"Example Simple Script")
+command_inputs "points"
+command_changes "points"
+params {
+ param "Move" 0.5
+}
+
+
+
+
+Changes
+
+Depending on the kind of changes you are making, you add to the list:
+'set_points
+'set_face_uvs
+'set_face_colors
+'set_e3d_mesh
+
+You also need to add to the command_inputs and command_changes directives the data needed, and what data will be changed. For the script using set_points, the following is needed in the .wscr file:
+
+command_inputs "points"
+command_changes "points"
+
+
+
+Advanced Commands
+
+Simple command scripts can be used to change the point coordinates, the UV values and colors. For more complicated tasks where the mesh needs more complicated changes, there are two options: E3DMesh scripts and We function based scripts.
+
+The E3DMesh script option the .wscr needs to specify the following options:
+
+command_inputs "e3d_mesh"
+command_changes "e3d_mesh"
+
+
+With this option, the script gets a E3DMesh object, and the E3DMesh can be changed, and then returned. Then the object's winged edge structure is recreated. This option is most useful when the script is a wrapper for external tools that expect meshes as list of faces and vertex array such as in a .obj file. A caveat of this option is the mesh will have to be turned into a list of faces and vertices, and then rebuilt back to a winged edge structure, so faces, edges and vertices will be renumbered.
+
+An example command that just returns the E3DMesh unchanged:
+
+
+(main-function command)
+ (lambda (*params* *extra-params*)
+ (define op (list-ref (assoc "op" *extra-params*) 1))
+ (define mesh (list-ref (assoc "e3d_mesh" *extra-params*) 1))
+
+ (list (list 'set_e3d_mesh mesh))
+ ))
+
+
+An example of moving the object by 0.5 in all axis:
+
+
+(main-function command)
+ (lambda (*params* *extra-params*)
+ (define op (list-ref (assoc "op" *extra-params*) 1))
+ (define mesh (list-ref (assoc "e3d_mesh" *extra-params*) 1))
+
+ (define points (e3d_mesh-vs mesh))
+
+ (define newpoints #f)
+ (define newmesh #f)
+
+ (set! newpoints
+ (map (lambda (b)
+ (let ((x (vector-ref b 0))
+ (y (vector-ref b 1))
+ (z (vector-ref b 2)))
+ (vector
+ (+ 0.5 x)
+ (+ 0.5 y)
+ (+ 0.5 z)))
+ ) points))
+
+ (set! newmesh
+ (**loader-construct-from `((vs ,newpoints)) mesh (list 'type 'vs 'vc 'tx 'ns 'fs 'he 'matrix))
+ )
+ (list (list 'set_e3d_mesh newmesh))
+ ))
+
+
+An .wscr file to use with the script:
+
+
+type "scm"
+name ?__(1,"Example E3D Mesh Script")
+desc ?__(2,"Example E3D Mesh Script")
+mode "body"
+params_title ?__(3,"Example E3D Mesh Script")
+command_inputs "e3d_mesh"
+command_changes "e3d_mesh"
+params {
+ param "Unused" 0.0
+}
+
+
+
+Advanced We Function Scripts
+
+Scripts can use the we functions from Wings3D directly. When using this option, the command script does not need to use the command_inputs and command_changes directives, and the return from the script can be an empty list. Instead the script contains a sequence of we function calls that modifies the we object.
+
+
+An example of moving the object by 0.5 in all axis using a we function, in this case, matrix transformation of the vertices:
+
+
+(main-function command)
+ (lambda (*params* *extra-params*)
+ (define op (list-ref (assoc "op" *extra-params*) 1))
+
+ (define MoveX 0.5)
+ (define MoveY 0.5)
+ (define MoveZ 0.5)
+
+ (we:we:transform_vs!
+ (list 1.0 0.0 0.0
+ 0.0 1.0 0.0
+ 0.0 0.0 1.0
+ MoveX MoveY MoveZ))
+
+ (list)
+ ))
+
+
+A list of all the available callable we functions is available in the w3d_we function references.
+
+An .wscr file to use for the we function command script:
+
+
+type "scm"
+name ?__(1,"Example We Script")
+desc "Example We Script"
+mode "body"
+params_title ?__(2,"Example We Script")
+params_preview 1
+params {
+ param "Unused" 0.0
+}
+
+
+
+
+
diff --git a/plugins_src/scripting/docs/en/scheme/exporter_scripts.txt b/plugins_src/scripting/docs/en/scheme/exporter_scripts.txt
new file mode 100644
index 000000000..163211c75
--- /dev/null
+++ b/plugins_src/scripting/docs/en/scheme/exporter_scripts.txt
@@ -0,0 +1,120 @@
+Exporter Scripts
+
+Exporter scripts are available from the "File" > "Export" and "File" > "Export Selected" menus.
+
+Read e3d_file
+
+The "filename" and "content" extra parameters are needed, the file name is chosen by the user to save the model, and the content contains an e3d_file data structure.
+
+
+(main-function
+ (lambda (*params* *extra-params*)
+ ;; The filename to write to
+ (define filename
+ (list-ref
+ (assoc "filename" *extra-params*) 1))
+ ;; The e3d_file contents
+ (define content
+ (list-ref
+ (assoc "content" *extra-params*) 1))
+
+ ;;
+ ;; ...
+ ;;
+
+ ;; Return '(ok) after successfully writing the file
+ '(ok)
+ ))
+
+
+After writing the content to a file, the exporter returns a list with only 'ok' to Wings3D:
+
+'(ok)
+
+
+A minimal exporter that writes vertices to a file:
+
+
+(use srfi-1)
+(use srfi-13)
+
+(define (export_fun Attr Filename Contents)
+ (define Objs (e3d_file-objs Contents))
+ (call-with-output-file Filename (lambda (Fp)
+ ;; Number of objects
+ (display (format "~w\n" (length Objs)) Fp)
+ (for-each (lambda (Obj)
+ (define Mesh (e3d_object-obj Obj))
+ (define Name (e3d_object-name Obj))
+ (define Vs (e3d_mesh-vs Mesh))
+ (define Faces (e3d_mesh-fs Mesh))
+ ;; Name of object
+ (display (format "~a\n" Name) Fp)
+ ;; Number of vertices
+ (display (format "~w\n" (length Vs)) Fp)
+ (for-each (lambda (V)
+ (define X (vector-ref V 0))
+ (define Y (vector-ref V 1))
+ (define Z (vector-ref V 2))
+ ;; Vertex coordinates
+ (display (format "~f ~f ~f\n" X Y Z) Fp)
+ ) Vs)
+ ;; Number of faces
+ (display (format "~w\n" (length Faces)) Fp)
+ (for-each (lambda (Face)
+ (define VList (e3d_face-vs Face))
+ (display (format "DEBUG:~w\n" VList))
+ (for-each (lambda (V)
+ ;; Index
+ (display (format "~w " V) Fp)
+ ) VList)
+ (display (format "\n") Fp)
+ ) Faces)
+ ) Objs)
+ '(ok)
+ ) :encoding 'utf8)
+ )
+
+(define (exporter *params* *extra-params*)
+ ;; e3d_file tuple is found in "content" of extra parameters, and the
+ ;; filename to export to is in filename.
+ ;;
+ (define Filename (list-ref (assoc "filename" *extra-params*) 1))
+ (define Content (list-ref (assoc "content" *extra-params*) 1))
+ (newline)
+ (export_fun '() Filename Content)
+ )
+
+(main-function exporter)
+
+
+
+A .wscr file for the exporter:
+
+
+type "scm"
+name ?__(1,"Example Exporter (.txt)")
+mode "export"
+params_title ?__(3,"Example Exporter Settings")
+params_init {
+ do %[1>$'subdivisions']
+}
+params_templates {
+ template export include_normals,include_uvs,include_colors
+}
+params_set {
+ export_param include_uvs %[bool(params/include_uvs)]
+ export_param subdivisions %[params/subdivisions]
+ export_param include_hard_edges 'true'
+ export_param script_texture_convert 'user'
+}
+extensions {
+ ext .txt ?__(5,"Text Mesh")
+}
+
+
+
+The .wscr and .scm file should have the same name before the extension (script.scm and script.wscr).
+
+
+
diff --git a/plugins_src/scripting/docs/en/scheme/functions/e3d_face.txt b/plugins_src/scripting/docs/en/scheme/functions/e3d_face.txt
new file mode 100644
index 000000000..a6c37c547
--- /dev/null
+++ b/plugins_src/scripting/docs/en/scheme/functions/e3d_face.txt
@@ -0,0 +1,37 @@
+e3d_face
+
+(make-e3d_face default
+ .
+ sc)
+
+Make a e3d_face list structure with a constructor list, available items are 'vs 'vc 'tx 'ns 'mat 'sg 'vis
+
+(e3d_face?
+ l)
+
+Is the list structure an e3d_face?
+
+(e3d_face-vs
+ l)
+
+(e3d_face-vc
+ l)
+
+(e3d_face-tx
+ l)
+
+(e3d_face-ns
+ l)
+
+(e3d_face-mat
+ l)
+
+(e3d_face-sg
+ l)
+
+(e3d_face-vis
+ l)
+
+Get the value of a given field in e3d_face.
+
+
diff --git a/plugins_src/scripting/docs/en/scheme/functions/e3d_file.txt b/plugins_src/scripting/docs/en/scheme/functions/e3d_file.txt
new file mode 100644
index 000000000..cec06c45e
--- /dev/null
+++ b/plugins_src/scripting/docs/en/scheme/functions/e3d_file.txt
@@ -0,0 +1,28 @@
+e3d_file
+
+(make-e3d_file
+ .
+ sc)
+
+Make a e3d_file list structure with a constructor list, available items are 'objs 'mat 'creator 'dir
+
+(e3d_file?
+ l)
+
+Is the list structure an e3d_file?
+
+(e3d_file-objs
+ l)
+
+(e3d_file-mat
+ l)
+
+(e3d_file-creator
+ l)
+
+(e3d_file-dir
+ l)
+
+Get the value of a given field in e3d_file
+
+
diff --git a/plugins_src/scripting/docs/en/scheme/functions/e3d_image.txt b/plugins_src/scripting/docs/en/scheme/functions/e3d_image.txt
new file mode 100644
index 000000000..2e7a466b5
--- /dev/null
+++ b/plugins_src/scripting/docs/en/scheme/functions/e3d_image.txt
@@ -0,0 +1,48 @@
+e3d_image
+
+(make-e3d_image
+ .
+ sc)
+
+Make a e3d_image list structure with a constructor list, available items are 'type 'bytes_pp 'alignment 'order 'width 'height 'image 'filename 'name 'extra
+
+(e3d_image?
+ l)
+
+Is the list structure an e3d_image?
+
+(e3d_image-type
+ l)
+
+(e3d_image-bytes_pp
+ l)
+
+(e3d_image-alignment
+ l)
+
+(e3d_image-order
+ l)
+
+(e3d_image-width
+ l)
+
+(e3d_image-height
+ l)
+
+(e3d_image-image
+ l)
+
+(e3d_image-filename
+ l)
+
+(e3d_image-name
+ l)
+
+(e3d_image-extra
+ l)
+
+Get the value of a given field in e3d_image
+
+
+
+
diff --git a/plugins_src/scripting/docs/en/scheme/functions/e3d_mesh.txt b/plugins_src/scripting/docs/en/scheme/functions/e3d_mesh.txt
new file mode 100644
index 000000000..3c03b8518
--- /dev/null
+++ b/plugins_src/scripting/docs/en/scheme/functions/e3d_mesh.txt
@@ -0,0 +1,40 @@
+e3d_mesh
+
+(make-e3d_mesh
+ .
+ sc)
+
+Make a e3d_mesh list structure with a constructor list, available items are 'type 'vs 'vc 'tx 'ns 'fs 'he 'matrix
+
+(e3d_mesh?
+ l)
+
+Is the list structure an e3d_mesh?
+
+(e3d_mesh-type
+ l)
+
+(e3d_mesh-vs
+ l)
+
+(e3d_mesh-vc
+ l)
+
+(e3d_mesh-tx
+ l)
+
+(e3d_mesh-ns
+ l)
+
+(e3d_mesh-fs
+ l)
+
+(e3d_mesh-he
+ l)
+
+(e3d_mesh-matrix
+ l)
+
+Get the value of a given field in e3d_mesh
+
+
diff --git a/plugins_src/scripting/docs/en/scheme/functions/e3d_object.txt b/plugins_src/scripting/docs/en/scheme/functions/e3d_object.txt
new file mode 100644
index 000000000..4ec21d7b0
--- /dev/null
+++ b/plugins_src/scripting/docs/en/scheme/functions/e3d_object.txt
@@ -0,0 +1,29 @@
+e3d_object
+
+(make-e3d_object
+ .
+ sc)
+
+Make a e3d_object list structure with a constructor list, available items
+ are 'name 'obj 'mat 'attr
+
+(e3d_object?
+ l)
+
+Is the list structure an e3d_object?
+
+(e3d_object-name
+ l)
+
+(e3d_object-obj
+ l)
+
+(e3d_object-mat
+ l)
+
+(e3d_object-attr
+ l)
+
+Get the value of a given field in e3d_object
+
+
diff --git a/plugins_src/scripting/docs/en/scheme/importer_scripts.txt b/plugins_src/scripting/docs/en/scheme/importer_scripts.txt
new file mode 100644
index 000000000..8c1014941
--- /dev/null
+++ b/plugins_src/scripting/docs/en/scheme/importer_scripts.txt
@@ -0,0 +1,126 @@
+Importer Scripts
+
+Importer scripts are available from the "File" > "Import" menu.
+
+Importer scripts are scripts that open a file, and with the mesh information in the file, creates an e3d_file data structure and returns it.
+
+The "filename" extra parameter is needed which contain the file path of the file to read:
+
+
+(main-function
+ (lambda (*params* *extra-params*)
+ (define filename
+ (list-ref
+ (assoc "filename" *extra-params*) 1))
+
+ ;;
+ ;; ...
+ ;;
+
+ ;; Return an e3d_file in a variable called e3df
+ (list 'ok e3df)
+ ))
+
+
+Constructing e3d objects involves using
+make-e3d_face,
+make-e3d_object,
+ make-e3dfile, etc.
+
+After parsing the file, the importer returns the contents of e3d_file back to Wings3D:
+
+(list 'ok (make-e3d_file `(objs ,(list Obj))))
+
+
+A minimal importer that reads vertices from a file:
+
+
+(use srfi-1)
+(use srfi-13) ; For string-tokenize
+
+(define (read-for-each Fp F)
+ (define NumVals (string->number (read-line Fp))) ; Number of values
+ (let loop ((I 0) (Vals (list)))
+ (if (< I NumVals)
+ (let ((Ret (F)))
+ (loop (+ I 1) (cons Ret Vals))
+ )
+ (reverse Vals))
+ )
+ )
+
+(define (import_fun Attr Filename)
+ (call-with-input-file Filename (lambda (Fp)
+ ; Read each object
+ (define Objs (read-for-each Fp
+ (lambda ()
+ (define ObjName (read-line Fp))
+ ; Read each vertex
+ (define Vs (read-for-each Fp
+ (lambda ()
+ (define Vtx0 (string-tokenize (read-line Fp)))
+ ; Vertex coordinate
+ (define Vtx (map exact->inexact (map string->number Vtx0)))
+ (list->vector Vtx))))
+ ; Read each face
+ (define Faces (read-for-each Fp
+ (lambda ()
+ ; Vertex indices
+ (define VList0 (string-tokenize (read-line Fp)))
+ (define VList (map string->number VList0))
+ (make-e3d_face
+ `(vs ,VList)
+ `(vc ,(list))
+ `(tx ,(list))
+ `(ns ,(list))
+ )
+ )))
+ (define Mesh (make-e3d_mesh
+ `(type polygon)
+ `(vs ,Vs)
+ `(fs ,Faces)
+ `(vc ,(list))
+ `(tx ,(list))
+ `(ns ,(list))
+ `(he ,(list))
+ ))
+ (make-e3d_object `(name ,ObjName) `(obj ,Mesh))
+ )))
+ (list 'ok (make-e3d_file `(objs ,Objs)))
+ ) :encoding 'utf8)
+ )
+
+(define (importer *params* *extra-params*)
+ ;; the filename to import from is in "filename" of extra parameters.
+ ;;
+ (define Filename (list-ref (assoc "filename" *extra-params*) 1))
+ (import_fun '() Filename)
+ )
+
+(main-function importer)
+
+
+
+A .wscr file for the importer:
+
+
+type "scm"
+name ?__(1,"Example Importer (.txt)")
+mode "import"
+params_title ?__(3,"Example Importer Settings")
+params_templates {
+ template import
+}
+params_set {
+ import_param script_texture_convert 'auto'
+}
+extensions {
+ ext .txt ?__(5,"Text Mesh")
+}
+
+
+
+The .wscr and .scm file should have the same name before the extension (script.scm and script.wscr).
+
+
+
diff --git a/plugins_src/scripting/docs/en/scheme/intro.txt b/plugins_src/scripting/docs/en/scheme/intro.txt
new file mode 100644
index 000000000..d9258eb0a
--- /dev/null
+++ b/plugins_src/scripting/docs/en/scheme/intro.txt
@@ -0,0 +1,83 @@
+Introduction
+
+ (list 'ok '(new_shape ... ))
+
+
+ (wings-set-variable! "var1" '(1 2 3 4))
+
+
+The return should be 'ok
.
+
+
+Get value of variable:
+
+
+ (wings-get-variable "var1")
+
+
+The return would be '(1 2 3 4)
.
+
+
+Query:
+
+
+ (wings-query "lists:reverse(<$'var1')")
+
+
+The return would be '(4 3 2 1)
.
+
+
+
+
+(display "Started")(newline)
+(long-running-process
+ (lambda ()
+ (let loop ((N 100000000))
+ (if (> N 0)
+ (loop (- N 1))
+ 'ok))
+ ))
+(display "Done")(newline)
+
+
+
+
+
+
+
diff --git a/plugins_src/scripting/docs/en/scheme/langres.txt b/plugins_src/scripting/docs/en/scheme/langres.txt
new file mode 100644
index 000000000..2e837890b
--- /dev/null
+++ b/plugins_src/scripting/docs/en/scheme/langres.txt
@@ -0,0 +1,15 @@
+Online Resources for the Scheme Language
+
+A very non exhaustive list of resources:
+
+Scheme Language Tutorials and Books
+https://docs.scheme.org/
+
+Gauche reference
+http://practical-scheme.net/gauche/man/gauche-refe/index.html
+
+R5RS (reference for functions found in nearly every Scheme to write portable code)
+https://conservatory.scheme.org/schemers/Documents/Standards/R5RS/
+
+
+
diff --git a/plugins_src/scripting/docs/en/scheme/new_shape_scripts.txt b/plugins_src/scripting/docs/en/scheme/new_shape_scripts.txt
new file mode 100644
index 000000000..e41d9fcbd
--- /dev/null
+++ b/plugins_src/scripting/docs/en/scheme/new_shape_scripts.txt
@@ -0,0 +1,109 @@
+New Shape Scripts
+
+New shape scripts are available when right clicking with no selection.
+
+An example of a complete Scheme script to create a shape:
+
+
+(main-function
+ (lambda (*params* *extra-params*)
+ (list 'new_shape "MyShape"
+ '(
+ (0 1 3 2)
+ (6 7 5 4)
+ (1 0 4 5)
+ (2 3 7 6)
+ (0 2 6 4)
+ (3 1 5 7)
+ ) '(
+ #(-2.0 0.5 -2.0)
+ #(-2.0 0.5 2.0)
+ #(2.0 0.5 -2.0)
+ #(2.0 0.5 2.0)
+ #(-2.0 -0.5 -2.0)
+ #(-2.0 -0.5 2.0)
+ #(2.0 -0.5 -2.0)
+ #(2.0 -0.5 2.0)
+ ))
+ ))
+
+
+An .wscr file to use with the script:
+
+
+type "scm"
+name ?__(1,"Example New Shape")
+params_title ?__(3,"Example New Shape")
+params {
+param "Unused" 0.0
+}
+
+
+The .wscr and .scm file should have the same name before the extension (script.scm and script.wscr).
+
+To use parameters with the script, such as shifting the points by an adjustable value:
+
+
+(main-function
+ (lambda (*params* *extra-params*)
+ (define xshift (list-ref *params* 0))
+ (define yshift (list-ref *params* 1))
+ (define zshift (list-ref *params* 2))
+ (list 'new_shape "MyShape"
+ '(
+ (0 1 3 2)
+ (6 7 5 4)
+ (1 0 4 5)
+ (2 3 7 6)
+ (0 2 6 4)
+ (3 1 5 7)
+ ) '(
+ (vector (+ val -2.0) (+ yshift 0.5) (+ zshift -2.0))
+ (vector (+ val -2.0) (+ yshift 0.5) (+ zshift 2.0))
+ (vector (+ val 2.0) (+ yshift 0.5) (+ zshift -2.0))
+ (vector (+ val 2.0) (+ yshift 0.5) (+ zshift 2.0))
+ (vector (+ val -2.0) (+ yshift -0.5) (+ zshift -2.0))
+ (vector (+ val -2.0) (+ yshift -0.5) (+ zshift 2.0))
+ (vector (+ val 2.0) (+ yshift -0.5) (+ zshift -2.0))
+ (vector (+ val 2.0) (+ yshift -0.5) (+ zshift 2.0))
+ ))
+ ))
+
+
+In the .wscr file, replace the "Unused" parameter with "X", "Y" and "Z" parameters:
+
+
+type "scm"
+name ?__(1,"Example New Shape")
+params_title ?__(3,"Example New Shape")
+params {
+param "X" 0.0
+param "Y" 0.0
+param "Z" 0.0
+}
+
+
+
+
+Another way to create a new shape where you might want to add colors, UVs, materials and hard edges is to make a new shape using a e3d_object.
+
+
+(main-function
+ (lambda (*params* *extra-params*)
+ (define Fs ...) ; List of indices into vertice list
+ (define Vs ...) ; Vertice list
+ (define Efs (map (lambda (Indices)
+ (make-e3d_face `(vs ,Indices))) Fs))
+ (define Mesh (make-e3d_mesh
+ `(type polygon)
+ `(vs ,Vs)
+ `(fs ,Efs)
+ ))
+ (define Obj (make-e3d_object `(obj ,Mesh)))
+ (define Mat '())
+ (list 'new_shape "MyShape" Obj Mat)
+ ))
+
+
+
+
diff --git a/plugins_src/scripting/docs/en/wscr/directives/adv_param.txt b/plugins_src/scripting/docs/en/wscr/directives/adv_param.txt
new file mode 100644
index 000000000..2c87120b4
--- /dev/null
+++ b/plugins_src/scripting/docs/en/wscr/directives/adv_param.txt
@@ -0,0 +1,29 @@
+adv_param Directive
+
+adv_params begin
+ {vframe, [
+ {hframe,[
+ {label,?__(3,"Path:")},
+ {text,"testing2",[{key,path}]}]},
+ panel,
+ separator,
+ {?__(2,"Enable"), 'true', [{key,enable}]}
+ ]}
+end.
+
+
+Adds parameters in the form of erlang term syntax, which allows
+scripts to have more control over the user interface layout. The
+erlang term syntax begins on the line after begin until
+the line end.. NOTE that there must be a dot (.) immediately
+after end on the same line, and end. must be on its own line.
+
+The erlang term itself should not have a dot after the
+term itself.
+
+Locale strings ?__(1, "Text") and query values %[...] are available
+to include inside the erlang term.
+
+An adv_param directive should be inside a
+params { ... } section.
+
diff --git a/plugins_src/scripting/docs/en/wscr/directives/browse.txt b/plugins_src/scripting/docs/en/wscr/directives/browse.txt
new file mode 100644
index 000000000..d5a905bbf
--- /dev/null
+++ b/plugins_src/scripting/docs/en/wscr/directives/browse.txt
@@ -0,0 +1,11 @@
+browse Directive
+
+browse "Click to browse" "filename.txt"
+
+
+A text box with a button to browse for files.
+
+A browse directive should be inside a
+params { ... } section.
+
+
diff --git a/plugins_src/scripting/docs/en/wscr/directives/checkbox.txt b/plugins_src/scripting/docs/en/wscr/directives/checkbox.txt
new file mode 100644
index 000000000..079f3b674
--- /dev/null
+++ b/plugins_src/scripting/docs/en/wscr/directives/checkbox.txt
@@ -0,0 +1,10 @@
+checkbox Directive
+
+checkbox "Enabled" 'true'
+
+
+Show a checkbox
+
+A checkbox directive should be inside a
+params { ... } section.
+
diff --git a/plugins_src/scripting/docs/en/wscr/directives/command_changes.txt b/plugins_src/scripting/docs/en/wscr/directives/command_changes.txt
new file mode 100644
index 000000000..b93bedd20
--- /dev/null
+++ b/plugins_src/scripting/docs/en/wscr/directives/command_changes.txt
@@ -0,0 +1,26 @@
+command_changes Directive
+
+command_changes "points"
+
+
+For command scripts, sets a comma separated list of possible information
+that will be changed by the script when making modifications to data from
+command_inputs. This directive is not needed if the script uses we commands
+to modify the shape directly.
+
+This directive is only used with command scripts, and only optionally as an
+alternative to using the we commands directly.
+
+The e3d_mesh option provides an e3d mesh of the shape, which can be modified
+by the script and then is recreated as a we object when returned back to wings.
+Since the we object is changed to a e3d mesh and then back, it is possible that
+all the numbering of vertices, edges and faces will be completely changed and any
+special data that uses those numberings won't match anymore.
+
+Items can be:
+ points,
+ face_colors,
+ face_uvs,
+ e3d_mesh
+
+
diff --git a/plugins_src/scripting/docs/en/wscr/directives/command_inputs.txt b/plugins_src/scripting/docs/en/wscr/directives/command_inputs.txt
new file mode 100644
index 000000000..b06f27b68
--- /dev/null
+++ b/plugins_src/scripting/docs/en/wscr/directives/command_inputs.txt
@@ -0,0 +1,24 @@
+command_inputs Directive
+
+command_inputs "points,faces"
+
+
+For command scripts, sets a comma separated list of input information that
+ will be sent to the script as part of the
+extra_params variable in Python, or the
+*extra-params* vaiable in Scheme.
+ For example, if faces is specified, an item entry with the key faces will
+ be available in extra params. This directive is not needed if the script
+uses the we commands directly.
+
+This directive is only used with command scripts, and only optionally as an
+alternative to using the we commands directly.
+
+Items can be:
+ points,
+ faces,
+ face_colors,
+ face_uvs,
+ e3d_mesh
+
+
diff --git a/plugins_src/scripting/docs/en/wscr/directives/desc.txt b/plugins_src/scripting/docs/en/wscr/directives/desc.txt
new file mode 100644
index 000000000..7f8b70428
--- /dev/null
+++ b/plugins_src/scripting/docs/en/wscr/directives/desc.txt
@@ -0,0 +1,11 @@
+desc Directive
+
+desc ?__(2,"This script does something")
+
+
+A short description can be added to provide more information about what
+ the script does.
+
+
+
+
diff --git a/plugins_src/scripting/docs/en/wscr/directives/export_param.txt b/plugins_src/scripting/docs/en/wscr/directives/export_param.txt
new file mode 100644
index 000000000..fb13a2510
--- /dev/null
+++ b/plugins_src/scripting/docs/en/wscr/directives/export_param.txt
@@ -0,0 +1,21 @@
+export_param Instruction
+
+export_param "subdivisions" %[params/subdivisions]
+
+
+Sets an export parameter for the wpa:export
+ function or some scripting export functions on the scripting plugin side.
+ For "script_texture_convert", the recommended option is
+'user' with the export template for the texture handling.
+
+This directive is only used with exporter scripts.
+
+An export_param directive should be inside a
+params_set { ... } section.
+
+The second argument can be a string, an atom or a query value enclosed
+in %[...]
+
+Refer to the Query Mini Language about query values.
+
+
diff --git a/plugins_src/scripting/docs/en/wscr/directives/extensions.txt b/plugins_src/scripting/docs/en/wscr/directives/extensions.txt
new file mode 100644
index 000000000..5909371f0
--- /dev/null
+++ b/plugins_src/scripting/docs/en/wscr/directives/extensions.txt
@@ -0,0 +1,14 @@
+extensions Section
+
+extensions {
+ ext ".tres" ?__(5,"Godot Mesh Library")
+}
+
+
+The extensions section indicates the extensions that are supported by the
+ importer or exporter script.
+ Each line within the section should be a ext directive, with its first
+ argument the extension including the dot, and the second argument is a
+ description of the file format.
+
+
diff --git a/plugins_src/scripting/docs/en/wscr/directives/extra_file.txt b/plugins_src/scripting/docs/en/wscr/directives/extra_file.txt
new file mode 100644
index 000000000..298ee91b5
--- /dev/null
+++ b/plugins_src/scripting/docs/en/wscr/directives/extra_file.txt
@@ -0,0 +1,17 @@
+extra_file Section
+
+extra_file "icon" {
+ title "Choose small image"
+ extensions {
+ ...
+ }
+}
+
+
+An extra input file which can be used by any of the script modes.
+ The first argument should be the name that will be set in
+extra_params for the script.
+ The title and the list of extensions need to be set for it.
+
+
+
diff --git a/plugins_src/scripting/docs/en/wscr/directives/hradio.txt b/plugins_src/scripting/docs/en/wscr/directives/hradio.txt
new file mode 100644
index 000000000..eb4f29ba6
--- /dev/null
+++ b/plugins_src/scripting/docs/en/wscr/directives/hradio.txt
@@ -0,0 +1,19 @@
+hradio Directive
+
+hradio "Color" 'blue' {
+ item "Red" 'red'
+ item "Green" 'green'
+ item "Blue" 'blue'
+}
+
+
+Shows a horizontal row of radio button choices.
+
+The list of values each start with item, followed
+by the string showed to user, followed by an atom that
+will be used by the script.
+
+A hradio directive should be inside a
+params { ... } section.
+
+
diff --git a/plugins_src/scripting/docs/en/wscr/directives/import_param.txt b/plugins_src/scripting/docs/en/wscr/directives/import_param.txt
new file mode 100644
index 000000000..4de2f9e53
--- /dev/null
+++ b/plugins_src/scripting/docs/en/wscr/directives/import_param.txt
@@ -0,0 +1,20 @@
+import_param Instruction
+
+import_param "script_texture_convert" 'auto'
+
+
+Sets an import parameter for the wpa:import
+ function or some scripting import functions on the scripting plugin side.
+ For "script_texture_convert", the recommended option is
+'auto' for the texture handling.
+
+This directive is only used with importer scripts.
+
+An import_param directive should be inside a
+params_set { ... } section.
+
+The second argument can be a string, an atom or a query value enclosed
+in %[...]
+
+Refer to the Query Mini Language about query values.
+
diff --git a/plugins_src/scripting/docs/en/wscr/directives/include.txt b/plugins_src/scripting/docs/en/wscr/directives/include.txt
new file mode 100644
index 000000000..a8d43e88b
--- /dev/null
+++ b/plugins_src/scripting/docs/en/wscr/directives/include.txt
@@ -0,0 +1,22 @@
+include Directives
+
+include "(%)_dialog.inclr"
+
+
+WSCR files can include other files, it is recommended to move all the parameter
+ related configuration to another file and add an include directive towards
+ it so as to keep the main wscr file small.
+ This is important as each time the user opens a script chooser dialog,
+ every directory is traversed for every wscr file, and each wscr file found
+ is read for it's information to display in the script chooser browser.
+
+The script chooser traversal process reads every wscr file, but it does
+ not read the include directives.
+ When a script is invoked, the include directives are used to read the rest
+ of the configuration.
+ A parenthesis wrapped percent symbol "(%)"
+
+ auto expands to the name of the .wscr file before the extension.
+
+
+
diff --git a/plugins_src/scripting/docs/en/wscr/directives/menu.txt b/plugins_src/scripting/docs/en/wscr/directives/menu.txt
new file mode 100644
index 000000000..58f4047c0
--- /dev/null
+++ b/plugins_src/scripting/docs/en/wscr/directives/menu.txt
@@ -0,0 +1,19 @@
+menu Directive
+
+menu "Color" 'blue' {
+ item "Red" 'red'
+ item "Green" 'green'
+ item "Blue" 'blue'
+}
+
+
+Shows a drop down menu to select choices.
+
+The list of values each start with item, followed
+by the string showed to user, followed by an atom that
+will be used by the script.
+
+A menu directive should be inside a
+params { ... } section.
+
+
diff --git a/plugins_src/scripting/docs/en/wscr/directives/mode.txt b/plugins_src/scripting/docs/en/wscr/directives/mode.txt
new file mode 100644
index 000000000..ce83b45fd
--- /dev/null
+++ b/plugins_src/scripting/docs/en/wscr/directives/mode.txt
@@ -0,0 +1,33 @@
+mode Directive
+
+mode "import"
+
+
+The mode indicates what kind of script it is and which menu it should
+appear in. For example, if a script has "export" for mode, it will only
+show up when choosing "Script-based exporters" in the
+"File" > "Export" menu.
+
+Table of values for mode:
+
+Kind of script mode
+-------------- ----
+New shapes (empty)
+Importers import
+Exporters export
+Commands command
+
+
+New shapes scripts create a new shape.
+
+Importer scripts are Found in File > Import.
+ Returns an E3D file
+
+Exporter scripts are Found in File > Export.
+ Takes an E3D file.
+
+Command scripts changes existing shapes.
+
+
+
+
diff --git a/plugins_src/scripting/docs/en/wscr/directives/name.txt b/plugins_src/scripting/docs/en/wscr/directives/name.txt
new file mode 100644
index 000000000..8ca5e4e5f
--- /dev/null
+++ b/plugins_src/scripting/docs/en/wscr/directives/name.txt
@@ -0,0 +1,7 @@
+name Directive
+
+name ?__(1,"Raw Triangles (.raw)")
+
+
+A name for the script, which appears in the script chooser dialog.
+
diff --git a/plugins_src/scripting/docs/en/wscr/directives/param.txt b/plugins_src/scripting/docs/en/wscr/directives/param.txt
new file mode 100644
index 000000000..90f5d5372
--- /dev/null
+++ b/plugins_src/scripting/docs/en/wscr/directives/param.txt
@@ -0,0 +1,10 @@
+param Directive
+
+param "Size" "2.0"
+
+
+A param directive specifies one field for user input
+
+A param directive should be inside a
+params { ... } section.
+
diff --git a/plugins_src/scripting/docs/en/wscr/directives/params.txt b/plugins_src/scripting/docs/en/wscr/directives/params.txt
new file mode 100644
index 000000000..714b7e0de
--- /dev/null
+++ b/plugins_src/scripting/docs/en/wscr/directives/params.txt
@@ -0,0 +1,11 @@
+params Section
+
+params {
+ param "Size" "2.0"
+ ...
+}
+
+
+The params section contains param directives.
+
+
diff --git a/plugins_src/scripting/docs/en/wscr/directives/params_init.txt b/plugins_src/scripting/docs/en/wscr/directives/params_init.txt
new file mode 100644
index 000000000..c462ff647
--- /dev/null
+++ b/plugins_src/scripting/docs/en/wscr/directives/params_init.txt
@@ -0,0 +1,16 @@
+params_init Instructions Section
+
+params_init {
+ do %[1>$'subdivisions']
+}
+
+
+The params_init section behaves as a subroutine, with each directive being
+evaluated sequentially. This section is used to load values into temporary
+variables to pass to the params and params_set sections.
+
+The state of temporary variables do not carry over after the params section.
+The argument of a do should be a query value enclosed in %[...]
+
+Refer to the Query Mini Language about query values.
+
diff --git a/plugins_src/scripting/docs/en/wscr/directives/params_preview.txt b/plugins_src/scripting/docs/en/wscr/directives/params_preview.txt
new file mode 100644
index 000000000..ce0401a8b
--- /dev/null
+++ b/plugins_src/scripting/docs/en/wscr/directives/params_preview.txt
@@ -0,0 +1,9 @@
+params_preview Directive
+
+params_preview 1
+
+
+Specifies that preview should be enabled on the dialog box so changes
+are seen when when changing parameters.
+
+
diff --git a/plugins_src/scripting/docs/en/wscr/directives/params_set.txt b/plugins_src/scripting/docs/en/wscr/directives/params_set.txt
new file mode 100644
index 000000000..1d9afc7d9
--- /dev/null
+++ b/plugins_src/scripting/docs/en/wscr/directives/params_set.txt
@@ -0,0 +1,16 @@
+params_set Instructions Section
+
+params_set {
+ export_param "include_uvs" %[bool(params/include_uvs)]
+ ...
+}
+
+
+The params_set section sets special parameters with directives such as
+export_param, import_param and script_param. With each directive within
+assigning a value to a parameter. These are different from the params
+section which are for normal parameters shown in a dialog box.
+
+Query values are enclosed in %[...] brackets, refer to the
+Query Mini Language about query values.
+
diff --git a/plugins_src/scripting/docs/en/wscr/directives/params_templates.txt b/plugins_src/scripting/docs/en/wscr/directives/params_templates.txt
new file mode 100644
index 000000000..d763510af
--- /dev/null
+++ b/plugins_src/scripting/docs/en/wscr/directives/params_templates.txt
@@ -0,0 +1,16 @@
+params_templates Section
+
+params_templates {
+ template "export" ""
+}
+
+
+Param templates are standard groups of parameters that are used by most
+ plugins in Wings3D for consistency.
+ For example, importers might use the import template to show the flip axis
+ and scale parameters.
+ When a template is used, the corresponding code that interprets the standard
+ parameters to automatically modify the E3D tuple will also be handled by
+ the scripting interface.
+
+
diff --git a/plugins_src/scripting/docs/en/wscr/directives/params_title.txt b/plugins_src/scripting/docs/en/wscr/directives/params_title.txt
new file mode 100644
index 000000000..dfa034128
--- /dev/null
+++ b/plugins_src/scripting/docs/en/wscr/directives/params_title.txt
@@ -0,0 +1,9 @@
+params_title Directive
+
+params_title ?__(3,"Command Settings")
+
+
+The params_title directive indicates the titlebar string of the dialog that
+ is shown for user input.
+
+
diff --git a/plugins_src/scripting/docs/en/wscr/directives/script_param.txt b/plugins_src/scripting/docs/en/wscr/directives/script_param.txt
new file mode 100644
index 000000000..21eff6078
--- /dev/null
+++ b/plugins_src/scripting/docs/en/wscr/directives/script_param.txt
@@ -0,0 +1,17 @@
+script_param Instruction
+
+script_param "test" "1"
+
+
+Sets an extra parameter for the script, which has to be accessed through the
+extra_params variable in Python, or the
+*extra-params* variable in Scheme.
+
+An script_param directive should be inside a
+params_set { ... } section.
+
+The second argument can be a string, an atom or a query value enclosed
+in %[...]
+
+Refer to the Query Mini Language about query values.
+
diff --git a/plugins_src/scripting/docs/en/wscr/directives/template.txt b/plugins_src/scripting/docs/en/wscr/directives/template.txt
new file mode 100644
index 000000000..fc548a406
--- /dev/null
+++ b/plugins_src/scripting/docs/en/wscr/directives/template.txt
@@ -0,0 +1,8 @@
+template Directive
+
+template "export" ""
+
+
+A template in the params_template section.
+
+
diff --git a/plugins_src/scripting/docs/en/wscr/directives/type.txt b/plugins_src/scripting/docs/en/wscr/directives/type.txt
new file mode 100644
index 000000000..d97f6dcc6
--- /dev/null
+++ b/plugins_src/scripting/docs/en/wscr/directives/type.txt
@@ -0,0 +1,7 @@
+type Directive
+
+type "py"
+
+
+This is either "scm" or "py", depending on if the script is a Scheme or Python file.
+
diff --git a/plugins_src/scripting/docs/en/wscr/directives/vradio.txt b/plugins_src/scripting/docs/en/wscr/directives/vradio.txt
new file mode 100644
index 000000000..2b4dc7067
--- /dev/null
+++ b/plugins_src/scripting/docs/en/wscr/directives/vradio.txt
@@ -0,0 +1,19 @@
+vradio Directive
+
+vradio "Color" 'blue' {
+ item "Red" 'red'
+ item "Green" 'green'
+ item "Blue" 'blue'
+}
+
+
+Shows a vertical list of radio button choices.
+
+The list of values each start with item, followed
+by the string showed to user, followed by an atom that
+will be used by the script.
+
+A vradio directive should be inside a
+params { ... } section.
+
+
diff --git a/plugins_src/scripting/docs/en/wscr/intro.txt b/plugins_src/scripting/docs/en/wscr/intro.txt
new file mode 100644
index 000000000..47975f516
--- /dev/null
+++ b/plugins_src/scripting/docs/en/wscr/intro.txt
@@ -0,0 +1,13 @@
+What are .wscr files?
+
+Wings SCRipt files provide information so that scripts can be used like normal plugins in wings, WSCR files specify:
+
+* Name and description of the script.
+* If the script is a import, export, shape or command script.
+* Parameters to ask the user before starting the script.
+
+When a user opens a script chooser dialog, the script search path is traversed for all wscr files, the name in the wscr file is used for the name shown in the script chooser dialog.
+
+
+
+
diff --git a/plugins_src/scripting/docs/en/wscr/langfiles.txt b/plugins_src/scripting/docs/en/wscr/langfiles.txt
new file mode 100644
index 000000000..b0de5a53a
--- /dev/null
+++ b/plugins_src/scripting/docs/en/wscr/langfiles.txt
@@ -0,0 +1,22 @@
+Language files
+
+Like normal wings3d plugins, wscr files can use language files to localize the user interface text into multiple languages. The lang file needs to be in the same directory as the wscr file, and begin with the same base file name without the extension, with a underscore and a language code following it, followed by the ".lang" file extension. The format of the language file is the same Erlang term format as wings3d lang files. To localize a string in the wscr file, enclose the string in the same enclosing syntax used in Erlang wings3d source files.
+
+
+name ?__(1,"Color")
+
+
+For a script called "script_name.wscr", the lang file for French should be named "script_name_fr.lang", and the content resembles other Wings3D lang files but the outer name is script and the inner name is wscr:
+
+
+{script,
+ [
+ {wscr,
+ [
+ {1,"Couleur"}
+ ]}
+ ]}.
+
+
+
+
diff --git a/plugins_src/scripting/docs/en/wscr/pluginscripts.txt b/plugins_src/scripting/docs/en/wscr/pluginscripts.txt
new file mode 100644
index 000000000..cb95e0c40
--- /dev/null
+++ b/plugins_src/scripting/docs/en/wscr/pluginscripts.txt
@@ -0,0 +1,67 @@
+Plugin Scripts
+
+Plugin scripts are scripts that get added into wing's menu system so they can be accessed the same way as plugins, this is done by having a config file that points to the wscr file, and some information of the menu to create for the script.
+
+Each plugin script config file is written in erlang term syntax, similar to the syntax used for Wings3D language files.
+
+
+%% Syntax of conf file, comment lines start with %
+
+{<Name>, [
+ {wscr, <WSCRFile>},
+ {menu, [
+ {<MenuPath>, <Option>}
+ ]}
+]}.
+
+{strings, [
+ {plugin, <Name>},
+ {<LangCode>, [
+ {{plugin,<MenuPathItem>}, <ActualName>},
+ {{menu,<MenuPathItem>}, <ActualName>},
+ ...
+ {{info,<MenuPathItem>}, <Text>}
+ ]}
+]}.
+
+
+<Name> is a double quoted string of a unique name, for example "com.example.author-name.plugin-script-name" or "author-name.plugin-script-name".
+
+<WSCRFile> is a double quoted path relative to the current config file, that points to the script's .wscr file.
+
+<MenuPath> is a list of <MenuPathItem> enclosed in [...] brackets, with comma separated words of the menu identifiers that the plugin's menu item is located. The first item is the top menu, subsequent items before the last item are submenus and the last item is the name for the plugin itself.
+
+<MenuPathItem> is each individual menu path item, names should not have quotes, double quotes, special characters, and just use a-z and _, numbers cannot be the first character in the name.
+
+<Option> is currently an empty list []
+
+<LangCode> is a 2 or 5 character language code, same as the ones available in Wings 3D.
+
+<ActualName> is a double quote enclosed text of the actual display name for the plugin or sub menu item that will be displayed in the menu.
+
+<Text> is a double quote enclosed text of text to show in some contexts.
+
+The config file is placed in the "plugin_scripts" folder inside the wings user data folder, in the same directory as the plugins and patches folders.
+
+
+%% Example plugin script .conf file:
+
+{"com.example.my.plugin.1", [
+ {wscr, "my-plugin.wscr"},
+ {menu, [
+ {[tools, newsubmenu, newnewsubmenu, myplugin], []}
+ ]}
+]}.
+
+{strings, [
+ {plugin, "com.example.my.plugin.1"},
+ {"en", [
+ {{plugin,name}, "My Plugin"},
+ {{menu,newsubmenu}, "New Submenu"},
+ {{menu,newnewsubmenu}, "New new Submenu"},
+ {{menu,myplugin}, "My Plugin"},
+ {{info,myplugin}, "My Plugin menu tooltip"}
+ ]}
+]}.
+
+
diff --git a/plugins_src/scripting/docs/en/wscr/query.txt b/plugins_src/scripting/docs/en/wscr/query.txt
new file mode 100644
index 000000000..0d5dfcfdb
--- /dev/null
+++ b/plugins_src/scripting/docs/en/wscr/query.txt
@@ -0,0 +1,246 @@
+Query Mini Language
+
+The query language is designed to be able to navigate the Erlang terms used by Wings3D with a simple path-like syntax.
+
+When the user enters parameter settings, before the script is invoked, there will be a need to set some settings for wpa:export or wpa:import through export_param and import_param instructions. The query language is used for referencing parameter values in the script information file as well as for communicating between the script and plugin.
+
+Query values are enclosed in %[...] brackets in the .wscr file instead of double quotes.
+
+
+ 'triangulated'
+
+
+
+
+ "Sample text"
+
+
+
+
+
+ 1
+ 1.2
+
+
+
+
+ variable.shapes
+
+
+With some records such as 'st' and 'we', fields of those records can be accessed using a dot notation. There is no need to specify the record type itself as done in Erlang because the record is inspected at runtime to match the record type for a field name.
+
+However, only a few record types have their fields registered in the plugin to use the dot notation. Alternatively, record fields can be accessed with the tuple index operator, where {0} is the atom of the record and {1} is the first field.
+
+e.g.
+
+
+ st.shapes
+
+
+
+
+ variable/key
+
+
+Associative lists can be navigated by their keys with the "slash" (/) character. If the associated lists contain more associated lists, the slash can be chained like a path to access deeper elements.
+
+e.g.
+
+
+ params/include_uvs
+
+
+
+
+ variable{0}
+
+
+The entries of a tuple can be accessed by a zero-based index by enclosing a number inside curly brackets, the first entry is {0}. Indexing operators can be chained as necessary to access deeper elements.
+
+e.g.
+
+
+ tuple(1,2,3,4){2}
+
+
+
+
+
+
+ variable[0]
+
+
+The entries of a list can be accessed by a zero-based index by enclosing a number inside square brackets, the first entry is [0] Indexing operators can be chained as necessary to access deeper elements.
+
+e.g.
+
+
+ list(1,2,3,4)[1]
+
+
+
+
+
+
+ >$'variable'
+
+
+Store the current value into a temporary variable for later retrieval, the syntax is a greater than symbol, a dollar sign and a integer or atom literal. This operator returns the same value that it is storing so it can be added at the end of a query chain without affecting the return value.
+
+e.g.
+
+
+ params/include_uvs>$'include_uvs'
+
+
+
+
+
+
+ <$'variable'
+
+
+Get the value stored in a temporary variable, the syntax is a lesser than symbol, a dollar sign and a integer or atom literal, this can appear at the beginning of the query.
+
+e.g.
+
+
+ <$'settings'/group1/setting
+
+
+
+
+ module:function( ... )
+
+
+Calls a function from a module, with the given arguments. A function call can appear at the beginning of the query.
+
+e.g.
+
+
+ lists:reverse(list(1,2,3,4))
+
+
+
+
+
+ list(1,'two',3)
+
+
+Construct a list with given values. A list constructor can appear at the beginning of the query. It is usually more convenient to use %setvar with temporary variables to input list data structures from the script to the plugin than to use inline constructors.
+
+
+
+
+
+ tuple(1,2,'three')
+
+
+Construct a tuple with given values. A tuple constructor can appear at the beginning of the query. It is usually more convenient to use %setvar with temporary variables to input tuple data structures from the script to the plugin than to use inline constructors.
+
+
+
+
+
+ bool(1)
+
+
+Get either the atom 'true' or 'false' from another value.
+
+
+
+
+
+ int("1")
+
+
+Get the integer value from another value
+
+
+
+
+
+ float("1.0")
+
+
+Get the float value from another value.
+
+
+
+
+ ok_test(orddict:find(key, $<'variable'), "not found")
+
+
+Tests if the return of the first argument is a tuple with the first element being 'ok'. If it is an 'ok' tuple, the tuple's value is returned. If the first argument is not an 'ok' tuple, the second argument is evaluated and returned instead.
+
+
+
+
+ value_test('value', gb_trees:lookup(key, $<'variable'), "not found")
+
+
+A general version of ok_test that tests if the return of the second argument is a tuple with the first element being equal to the first argument. If the second argument does not match, the third argument is evaluated and returned instead.
+
+
+
+
+ if(bool(<$'cond'), "on", "off")
+
+
+Tests the first argument if it evaluates to the atom 'true', then the second argument (the "then" argument) is evaluated, otherwise the third (the "else" argument) is evaluated.
+
+
+
diff --git a/plugins_src/scripting/docs/en/wscr/scriptfolders.txt b/plugins_src/scripting/docs/en/wscr/scriptfolders.txt
new file mode 100644
index 000000000..d43b65a93
--- /dev/null
+++ b/plugins_src/scripting/docs/en/wscr/scriptfolders.txt
@@ -0,0 +1,37 @@
+Script Folders
+
+Script folder files are config files in the user data area where other software or the user can add new paths to find scripts.
+
+Each script folder config file is written in erlang term syntax, similar to the syntax used for Wings3D language files.
+
+
+%% Syntax of config file, comment lines start with %
+
+{<Name>,[
+ {type,<Type>},
+ {path,<Paths>}
+]}.
+
+
+<Name> is a double quoted string of a unique name, for example "com.example.author.script-folders.name" or "author-name.script-folders.name".
+
+<Type> is a list enclosed in [...] of: vertex, edge, face, body, shape
+
+<Paths> is a list of double quoted string paths enclosed in [...]. So a list of one path is ["folder/"]. Paths can be absolute paths, or relative to your operating system user folder.
+
+The config file is placed in the "script_folders" folder inside the wings user data folder, in the same directory as the plugins and patches folders.
+
+
+%% Example script folder .conf file:
+
+{"com.example.wings-scripts-1",[
+ {type,[shape]},
+ {path,["a1/","a2/"]}
+]}.
+
+{"com.example.scripts.2",[
+ {type,[face,body]},
+ {path,["a/"]}
+]}.
+
+
diff --git a/plugins_src/scripting/docs/funs b/plugins_src/scripting/docs/funs
new file mode 100644
index 000000000..1962de825
--- /dev/null
+++ b/plugins_src/scripting/docs/funs
@@ -0,0 +1,301 @@
+| Winged Edge Function Reference
+we | collapse | collapse_edge | ! | EdgeNumber | KeepVertexNumber | |
+we | collapse | collapse_edges | ! | ListOfEdges | | |
+we | collapse | collapse_faces | ! | ListOfFaces | | |
+we | collapse | collapse_vertices | ! | ListOfVertices | | |
+we | collapse | fast_collapse_edge | ! | EdgeNumber | | |
+we | dissolve | complement | ! | ListOfFaces | | |
+we | dissolve | faces | ! | ListOfFaces | | |
+we | dissolve | outer_edge_partition | | ListOfFaces | | list
+
+we | edge | cut | ! | EdgeNumber Count | | number
+we | edge | dissolve_edge | ! | EdgeNumber | | number
+
+we | edge | dissolve_edges | ! | ListOfEdgeNumbers | | _
+we | edge | dissolve_edges | ! | ListOfEdgeNumbers ListOfFaceNumbers | | list
+
+we | edge | dissolve_isolated_vs | ! | ListOfVertexNumbers | | |
+we | edge | fast_cut | ! | EdgeNumber VertexPositionVec3 | | vertex number
+we | edge | from_faces | | ListOfFaceNumbers | | list of edge numbers
+we | edge | from_vs | | ListOfVertexNumbers | | list of edge numbers
+we | edge | length | | EdgeNumber | | float
+we | edge | reachable_faces | | ListOfFaceNumbers ListOfEdgeNumbers | | list of face numbers
+we | edge | screaming_cut | ! | EdgeNumber VertexPositionVec3 | | vertex number
+we | edge | select_region | | ListOfEdgeNumbers | | list of face numbers
+we | edge | to_vertices | | ListOfEdges | | list of vertex numbers
+
+we | edge_cmd | loop_cut_partition | | ListOfEdgeNumbers | | list of lists of face numbers
+
+we | edge_loop | edge_links | | ListOfEdgeNumbers | | list of tuples $(TUPLE EdgeNumber VertexNumberStart VertexNumberEnd)
+we | edge_loop | edge_loop_vertices | | ListOfEdgeNumbers | | list of lists of vertex numbers
+we | edge_loop | partition_edges | | ListOfEdgeNumbers | | list of lists of edge numbers
+we | edge_loop | select_loop | | ListOfEdgeNumbers | | list of edge numbers
+
+we | extrude_edge | extrude_edges | ! | ListOfEdgeNumbers DistanceFloat | | list of vertices, list of new vertices, list of forbidden faces
+we | extrude_face | faces | ! | ListOfFaceNumbers | | |
+we | extrude_face | regions | ! | ListOfListOfFaceNumbers | | |
+
+we | face | are_neighbors | | FaceNumberA FaceNumberB | | boolean
+we | face | area | | FaceNumber | | float
+we | face | center | | FaceNumber | | vec3
+we | face | delete_bad_faces | ! | ListOfFaceNumbers | |
+we | face | extend_border | | ListOfFaceNumbers | | list of face numbers
+we | face | face_normal_ccw | | ListOfVertexNumbers | | vec3
+we | face | face_normal_cw | | ListOfVertexNumbers | | vec3
+we | face | from_edges | | ListOfEdgeNumbers | | list
+we | face | from_vs | | ListOfVertexNumbers | | list
+we | face | good_normal | | FaceNumber | | boolean
+we | face | inner_edges | | ListOfFaceNumbers | | list of inner edges
+we | face | inner_outer_edges | | ListOfFaceNumbers | | list of inner edges, list of outer edges
+we | face | is_planar | | ToleranceFloat FaceNumber | | boolean
+we | face | is_thin | | FaceNumber | | boolean
+we | face | mirror_matrix | | FaceNumber | | matrix
+we | face | normal | | FaceNumber | EdgeNumber | vec3
+we | face | outer_edges | | ListOfFaceNumbers | | list of edge numbers
+we | face | to_edges | | ListOfFaceNumbers | | list of edge numbers
+we | face | to_vertices | | ListOfFaceNumbers | | list of vertex numbers
+we | face | vertex_positions | | FaceNumber | EdgeNumber | list
+we | face | vertices | | FaceNumber | | number
+we | face | vertices_ccw | | FaceNumber | EdgeNumber | list of vertex numbers
+we | face | vertices_cw | | FaceNumber | EdgeNumber | list of vertex numbers
+
+we | face_cmd | force_bridge | ! | FaceNumberA VertexA FaceNumberB VertexB | | |
+we | face_cmd | mirror_faces | ! | ListOfFaceNumbers | | |
+
+we | facemat | all | | | | list
+we | facemat | any_interesting_materials | | | | boolean
+we | facemat | assign | ! | ListOfTuples | | |
+we | facemat | assign | ! | Material FaceNumber | | |
+we | facemat | delete_face | ! | FaceNumber | | |
+we | facemat | delete_faces | ! | ListOfFaceNumbers | | |
+we | facemat | face | | FaceNumber | | atom string
+we | facemat | gc | ! | | | |
+we | facemat | hide_faces | ! | | | |
+we | facemat | is_material_used | | Material | | boolean
+we | facemat | keep_faces | ! | ListOfFaceNumbers | |
+we | facemat | mat_faces | | ListOfPairsFaceInfo | | list of tuples $(TUPLE Material ListOfPairsFaceInfo)
+we | facemat | show_faces | ! | ListOfFaces | | |
+we | facemat | used_materials | | | | list
+
+we | subdiv | get_proxy_info | | DynListOfVertexNumbers UpdateListOfVertexNumbers | | list of face positions, list of edge split, list of smooth new vertices, list of original vertices
+we | subdiv | smooth | ! | | | |
+we | subdiv | smooth | ! | ListOfFaces ListOfVertices ListOfEdges ListOfHardEdges | |
+we | subdiv | smooth_faces_htab | | | | list of faces and list of hard edges
+we | subdiv | subdiv | ! | | | |
+we | subdiv | subdiv | ! | ListOfFaces ListOfVertices ListOfEdges ListOfHardEdges | |
+
+we | tesselation | quadrangulate | ! | | ListOfFaceNumbers | |
+we | tesselation | triangulate | ! | | ListOfFaceNumbers | |
+
+we | util | add_vpos | | ListOfVertexNumbers | | list of tuple $(TUPLE VertexNumber Vec3)
+we | util | update_vpos | | ListOfTuple | | list of tuple $(TUPLE VertexNumber Vec3)
+| Each element in $(ARG ListOfTuple) is $(TUPLE VertexNumber Vec3)
+
+we | va | all | | WhatAtom | | list
+we | va | any_attributes | | | | boolean
+we | va | any_colors | | | | boolean
+we | va | any_uvs | | | | boolean
+
+we | va | del_edge_attrs | ! | EdgeNumber | |
+
+we | va | edge_attrs | | EdgeNumber Side | WeightFloat | Attribute value
+we | va | face_attr | | WhatSymbol FaceNumber | EdgeNumber | list
+| $(ARG WhatSymbol) can be $(ATOM uv) or $(ATOM color)
+
+we | va | face_mixed_attrs | | FaceNumber | | list
+we | va | face_pos_attr | | WhatAtom FaceNumber EdgeNumber | | list, list
+we | va | gc | ! | | | |
+we | va | remove | ! | WhatSymbol | ListOfFaceNumbers |
+| $(ARG WhatSymbol) can be $(ATOM all) $(ATOM uv) $(ATOM color)
+
+we | va | renumber | ! | ListOfPairs | | |
+we | va | set_body_color | ! | Vec3 | | |
+we | va | set_both_edge_attrs | ! | EdgeNumber LeftOpaqueAttribute RightOpaqueAttribute | | |
+we | va | set_edge_attrs | ! | ListOfTuple | | |
+| Each element in $(ARG ListOfTuple) is $(TUPLE EdgeNumber LeftUV RightUV LeftVC RightVC)
+
+we | va | set_edge_attrs | ! | EdgeNumber Side AttrValue | |
+we | va | set_edge_color | ! | ListOfEdges ColorVec3 | |
+we | va | set_edge_color | ! | EdgeNumber LeftColorVec3 RightColorVec3 | |
+we | va | set_edge_colors | ! | ListOfTuple | |
+| Each element in $(ARG ListOfTuple) is $(TUPLE EdgeNumber LeftColor RightColor)
+
+we | va | set_edge_uvs | ! | ListOfTuple | |
+| Each element in $(ARG ListOfTuple) is $(TUPLE EdgeNumber LeftUV RightUV)
+
+we | va | set_face_attr_vs | ! | WhatAtom FaceNumber ListOfNewValues | |
+we | va | set_face_attrs | ! | FaceNumber OpaqueAttr | |
+we | va | set_face_color | ! | ListOfFaceNumbers Vec3 | |
+we | va | set_vertex_color | ! | ListOfVertexNumbers Vec2 | |
+we | va | set_vtx_face_uvs | ! | VertexNumber ListOfFaceNumbers Vec2 | |
+we | va | vtx_attrs | | VertexNumber | FaceNumber | Attribute value
+
+we | vertex | bounding_box | | | ListOfVertices BoundingBoxVec3 | vec3
+we | vertex | center | | | ListOfVertices | vec3
+we | vertex | connect | ! | FaceNumber ListOfVertexNumbers | |
+we | vertex | connect_cut | ! | VertexNumberA VertexNumberB | | list of edge numbers
+we | vertex | dissolve_isolated | ! | ListOfVertexNumbers | |
+we | vertex | edge_through | | VertexNumberA VertexNumberB | FaceNumber | $(ARG EdgeNumber)
+we | vertex | flatten | ! | ListOfVertices PlaneNormalVec3 | CenterVec3 |
+we | vertex | force_connect | ! | VertexNumberStart VertexNumberEnd FaceNumber | | |
+we | vertex | from_edges | | ListOfEdgeNumbers | | list
+we | vertex | from_faces | | ListOfFaceNumbers | | list
+we | vertex | isolated | | | | list of vertex numbers
+we | vertex | normal | | VertexNumber | | vec3
+we | vertex | outer_vertices_ccw | | ListOfFaceNumbers | | list of vertex numbers or none
+we | vertex | per_face | | ListOfVertexNumbers | | list of tuples $(TUPLE Face ListOfVertexNumbers)
+we | vertex | pos | | VertexNumber | | vec3
+we | vertex | reachable | | ListOfVertexNumbers | | list
+
+we | vertex_cmd | bevel_vertex | ! | VertexNumber | |
+we | vertex_cmd | connect | ! | ListOfVertices | |
+
+we | we | all_hidden | | | | boolean
+we | we | break_mirror | ! | | | |
+we | we | centroid | | | | vec3
+we | we | create_holes | ! | ListOfFaceNumbers | |
+we | we | create_mirror | ! | FaceNumber | |
+we | we | fast_rebuild | ! | | | |
+we | we | freeze_mirror | ! | | | |
+we | we | fully_visible_edges | | List | | list
+we | we | hide_faces | ! | ListOfFaces | | list
+we | we | invert_normals | ! | | | |
+we | we | is_consistent | | | | boolean
+we | we | is_face_consistent | | FaceNumber | | boolean
+we | we | is_open | | | | boolean
+we | we | mirror_projection | | | | matrix
+we | we | new_id | ! | | | number
+we | we | new_ids | ! | Number | |
+we | we | new_wrap_range | ! | Number Number | | tuple
+we | we | normals | | FaceNormals MatrixOrNone | | list
+we | we | num_hidden | | | | number
+we | we | perimeter | | | | float
+we | we | rebuild | ! | | | |
+we | we | renumber | ! | StartNumber | RootSet |
+we | we | separate | ! | | | list
+we | we | show_faces | ! | | ListOfFaces |
+we | we | surface_area | | | | float
+we | we | transform_vs | ! | Matrix | |
+we | we | uv_mapped_faces | | | | list
+we | we | uv_to_color | ! | | | |
+we | we | validate_mirror | ! | | | |
+we | we | visible | | | List | list
+| $(ARG List) is either list of face numbers, or list of tuples $(TUPLE FaceNumber EdgeNumber)
+
+we | we | visible_edges | | | ListOfEdges | list
+we | we | visible_vs | | | ListOfVertices | list
+we | we | volume | | | | float
+
+| e3d_mat | identity | | | | matrix
+| e3d_mat | is_identity | | Matrix | | bool
+| e3d_mat | determinant | | Matrix | | float
+| e3d_mat | print | | Matrix | | atom
+| e3d_mat | compress | | Matrix | | matrix
+| e3d_mat | expand | | Matrix | | matrix
+
+| e3d_mat | translate | | Vec3 | | _
+| e3d_mat | translate | | Float Float Float | | matrix
+
+| e3d_mat | scale | | Vec3OrFloat | | _
+| e3d_mat | scale | | Float Float Float Float | | matrix
+
+| e3d_mat | rotate | | Float Vec3 | | matrix
+
+| e3d_mat | rotate_from_euler_rad | | Vec3 | |
+| e3d_mat | rotate_from_euler_rad | | Float Float Float | | matrix
+
+| e3d_mat | rotate_to_z | | Vec3 | | matrix
+| e3d_mat | rotate_s_to_t | | Vec3 Vec3 | | matrix
+| e3d_mat | project_to_plane | | Vec3 | | matrix
+| e3d_mat | transpose | | Matrix | | matrix
+| e3d_mat | invert | | Matrix | | matrix
+| e3d_mat | add | | Matrix Matrix | | matrix
+
+| e3d_mat | mul | | List | | _
+| e3d_mat | mul | | Matrix MatrixOrFloat | | matrix
+
+| e3d_mat | mul_point | | Matrix Vec3 | | vec3
+| e3d_mat | mul_vector | | Matrix Vec3 | | vec3
+| e3d_mat | eigenv3 | | Matrix | | vec3 matrix
+
+
+| e3d_vec | zero | | | | vec3
+| e3d_vec | is_zero | | Vec3 | | bool
+
+| e3d_vec | add | | List | | _
+| e3d_vec | add | | Vec3 Vec3 | | vec3
+
+| e3d_vec | add_prod | | Vec3 Vec3 Float | | vec3
+
+| e3d_vec | sub | | List | | _
+| e3d_vec | sub | | Vec3 Vec3 | | vec3
+
+| e3d_vec | lerp | | Vec3 Vec3 Float | | vec3
+| e3d_vec | norm_sub | | Vec3 Vec3 | | vec3
+| e3d_vec | mul | | Vec3 Float | | vec3
+| e3d_vec | divide | | Vec3 Float | | vec3
+| e3d_vec | neg | | Vec3 | | vec3
+| e3d_vec | dot | | Vec3 Vec3 | | float
+
+| e3d_vec | cross | | Vec3 Vec3 | | vec3
+| e3d_vec | len | | Vec3 | | float
+| e3d_vec | len_sqr | | Vec3 | | float
+| e3d_vec | dist | | Vec3 Vec3 | | float
+| e3d_vec | dist_sqr | | Vec3 Vec3 | | float
+
+| e3d_vec | norm | | Vec3 | | _
+| e3d_vec | norm | | Float Float Float | | vec3
+
+| e3d_vec | normal | | List | | _
+| e3d_vec | normal | | Vec3 Vec3 Vec3 | | vec3
+
+| e3d_vec | average | | List | | _
+| e3d_vec | average | | Vec3 Vec3 | | _
+| e3d_vec | average | | Vec3 Vec3 Vec3 Vec3 | | vec3
+
+| e3d_vec | area | | Vec3 Vec3 Vec3 | | float
+| e3d_vec | degrees | | Vec3 Vec3 | | float
+
+| e3d_vec | plane | | List | | _
+| e3d_vec | plane | | Vec3 Vec3 | | _
+| e3d_vec | plane | | Vec3 Vec3 Vec3 | | tuple
+
+| e3d_vec | plane_side | | Vec3 Tuple | | integer
+| e3d_vec | plane_dist | | Vec3 Tuple | | float
+| e3d_vec | line_dist | | Vec3 Vec3 Vec3 | | float
+| e3d_vec | line_dist_sqr | | Vec3 Vec3 Vec3 | | float
+| e3d_vec | line_line_intersect | | Vec3 Vec3 Vec3 Vec3 | | any
+| e3d_vec | project_2d | | Vec3 Atom | | vec3
+| Atom is $(ATOM x), $(ATOM y) or $(ATOM z)
+
+| e3d_vec | largest_dir | | Vec3 | | atom
+
+
+| e3d_bv | box | | | | _
+| e3d_bv | box | | List | | _
+| e3d_bv | box | | Any Any | | _
+| e3d_bv | box | | Vec3 Vec3 Float | | tuple
+
+| e3d_bv | union | | Tuple Tuple | | tuple
+| e3d_bv | dist | | Tuple Tuple | | float_or_false
+| e3d_bv | intersect | | Tuple Tuple | | bool
+| e3d_bv | center | | Tuple | | vec3
+| e3d_bv | max_extent | | Tuple | | any
+| e3d_bv | surface_area | | Tuple | | float
+| e3d_bv | volume | | Tuple | | float
+| e3d_bv | sphere | | Tuple | | tuple
+| e3d_bv | inside | | Vec3 Tuple | | bool
+
+| e3d_bv | hit | | Ray BBox | | _
+| e3d_bv | hit | | Ray Tuple BBox | | bool
+
+| e3d_bv | inv_sign | | Vec3 | | tuple
+| e3d_bv | eigen_vecs | | List | | tuple
+| e3d_bv | quickhull | | List | | tuple
+| e3d_bv | covariance_matrix | | List | | tuple
+
+
+
+
+
+
diff --git a/plugins_src/scripting/init_scripts/callable.conf b/plugins_src/scripting/init_scripts/callable.conf
new file mode 100644
index 000000000..c02383c84
--- /dev/null
+++ b/plugins_src/scripting/init_scripts/callable.conf
@@ -0,0 +1,272 @@
+
+%%
+%% Scripting for Shapes
+%%
+%% Copyright 2025 Edward Blake
+%%
+%% See the file "license.terms" for information on usage and redistribution
+%% of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+%%
+%% $Id$
+%%
+
+%% List of callable wings functions
+[
+{{wings_collapse,collapse_edge,1},{[int,we],we}},
+{{wings_collapse,collapse_edge,2},{[int,int,we],we}},
+{{wings_collapse,collapse_edges,1},{[list,we],we}},
+{{wings_collapse,collapse_faces,1},{[gbset,we],[we,gbset]}},
+{{wings_collapse,collapse_vertices,1},{[list,we],we}},
+{{wings_collapse,fast_collapse_edge,1},{[int,we],we}},
+{{wings_dissolve,complement,1},{[list,we],we}},
+{{wings_dissolve,faces,1},{[ordset,we],we}},
+{{wings_dissolve,outer_edge_partition,1},{[ordset,we],list}},
+{{wings_edge,cut,2},{[int,int,we],[we,int]}},
+{{wings_edge,dissolve_edge,1},{[int,we],we}},
+{{wings_edge,dissolve_edges,1},{[list,we],we}},
+{{wings_edge,dissolve_edges,2},{[list,list,we],[we,list]}},
+{{wings_edge,dissolve_isolated_vs,1},{[list,we],we}},
+{{wings_edge,fast_cut,2},{[int,vec3_or_atom,we],[we,int]}},
+{{wings_edge,from_faces,1},{[list,we],gbset}},
+{{wings_edge,from_vs,1},{[list,we],list}},
+{{wings_edge,length,1},{[int,we],float}},
+{{wings_edge,reachable_faces,2},{[gbset,gbset,we],list}},
+{{wings_edge,screaming_cut,2},{[int,vec3,we],[we,int]}},
+{{wings_edge,select_region,1},{[gbset,we],gbset}},
+{{wings_edge,to_vertices,1},{[list,we],list}},
+{{wings_edge_cmd,loop_cut_partition,1},{[list,we],list}},
+{{wings_edge_loop,edge_links,1},{[gbset,we],list}},
+{{wings_edge_loop,edge_loop_vertices,1},{[gbset,we],list}},
+{{wings_edge_loop,partition_edges,1},{[gbset,we],list}},
+{{wings_edge_loop,select_loop,1},{[ordset,we],list}},
+{{wings_extrude_edge,extrude_edges,2},{[list,float,we],[we,list,gbset,gbset]}},
+{{wings_extrude_face,faces,1},{[list,we],we}},
+{{wings_extrude_face,regions,1},{[list,we],we}},
+{{wings_face,are_neighbors,2},{[int,int,we],bool}},
+{{wings_face,area,1},{[int,we],float}},
+{{wings_face,center,1},{[int,we],vec3}},
+{{wings_face,delete_bad_faces,1},{[list,we],we}},
+{{wings_face,extend_border,1},{[gbset,we],gbset}},
+{{wings_face,face_normal_ccw,1},{[list,we],vec3}},
+{{wings_face,face_normal_cw,1},{[list,we],vec3}},
+{{wings_face,from_edges,1},{[list,we],gbset}},
+{{wings_face,from_vs,1},{[list,we],list}},
+{{wings_face,good_normal,1},{[int,we],bool}},
+{{wings_face,inner_edges,1},{[list,we],list}},
+{{wings_face,inner_outer_edges,1},{[list,we],[list,list]}},
+{{wings_face,is_planar,2},{[float,int,we],bool}},
+{{wings_face,is_thin,1},{[int,we],bool}},
+{{wings_face,mirror_matrix,1},{[int,we],matrix}},
+{{wings_face,normal,1},{[int,we],vec3}},
+{{wings_face,normal,2},{[int,int,we],vec3}},
+{{wings_face,outer_edges,1},{[list,we],list}},
+{{wings_face,to_edges,1},{[list,we],list}},
+{{wings_face,to_vertices,1},{[list,we],list}},
+{{wings_face,vertex_positions,1},{[int,we],list}},
+{{wings_face,vertex_positions,2},{[int,int,we],list}},
+{{wings_face,vertices,1},{[int,we],int}},
+{{wings_face,vertices_ccw,1},{[int,we],list}},
+{{wings_face,vertices_ccw,2},{[int,int,we],list}},
+{{wings_face,vertices_cw,1},{[int,we],list}},
+{{wings_face,vertices_cw,2},{[int,int,we],list}},
+{{wings_face_cmd,force_bridge,4},{[int,int,int,int,we],we}},
+{{wings_face_cmd,mirror_faces,1},{[list,we],we}},
+{{wings_facemat,all,0},{[we],list}},
+{{wings_facemat,any_interesting_materials,0},{[we],bool}},
+{{wings_facemat,assign,1},{[list,we],we}},
+{{wings_facemat,assign,2},{[val,list,we],we}},
+{{wings_facemat,delete_face,1},{[int,we],we}},
+{{wings_facemat,delete_faces,1},{[list,we],we}},
+{{wings_facemat,face,1},{[int,we],atom}},
+{{wings_facemat,gc,0},{[we],we}},
+{{wings_facemat,hide_faces,0},{[we],we}},
+{{wings_facemat,is_material_used,1},{[val,we],bool}},
+{{wings_facemat,keep_faces,1},{[list,we],we}},
+{{wings_facemat,mat_faces,1},{[list,we],list}},
+{{wings_facemat,show_faces,1},{[list,we],we}},
+{{wings_facemat,used_materials,0},{[we],list}},
+{{wings_subdiv,get_proxy_info,2},{[list,list,we],[list,list,list,list]}},
+{{wings_subdiv,smooth,0},{[we],we}},
+{{wings_subdiv,smooth,4},{[list,list,list,list,we],we}},
+{{wings_subdiv,smooth_faces_htab,0},{[we],[list,gbset]}},
+{{wings_subdiv,subdiv,0},{[we],we}},
+{{wings_subdiv,subdiv,4},{[list,list,list,list,we],we}},
+{{wings_tesselation,quadrangulate,0},{[we],we}},
+{{wings_tesselation,quadrangulate,1},{[list,we],we}},
+{{wings_tesselation,triangulate,0},{[we],we}},
+{{wings_tesselation,triangulate,1},{[gbset,we],we}},
+{{wings_util,add_vpos,1},{[list,we],list}},
+{{wings_util,update_vpos,1},{[list,we],list}},
+{{wings_va,all,1},{[atom,we],list}},
+{{wings_va,any_attributes,0},{[we],bool}},
+{{wings_va,any_colors,0},{[we],bool}},
+{{wings_va,any_uvs,0},{[we],bool}},
+{{wings_va,del_edge_attrs,1},{[int,we],we}},
+{{wings_va,edge_attrs,2},{[int,atom,we],attr}},
+{{wings_va,edge_attrs,3},{[int,atom,float,we],attr}},
+{{wings_va,face_attr,2},{[atom,int,we],list}},
+{{wings_va,face_attr,3},{[atom,int,int,we],list}},
+{{wings_va,face_mixed_attrs,1},{[int,we],list}},
+{{wings_va,face_pos_attr,3},{[atom,int,int,we],[list,list]}},
+{{wings_va,gc,0},{[we],we}},
+{{wings_va,remove,1},{[atom,we],we}},
+{{wings_va,remove,2},{[atom,list,we],we}},
+{{wings_va,renumber,1},{[gbtree,we],we}},
+{{wings_va,set_body_color,1},{[vec3,we],we}},
+{{wings_va,set_both_edge_attrs,3},{[int,val,val,we],we}},
+{{wings_va,set_edge_attrs,1},{[list,we],we}},
+{{wings_va,set_edge_attrs,3},{[int,atom,val,we],we}},
+{{wings_va,set_edge_color,2},{[gbset,vec3,we],we}},
+{{wings_va,set_edge_color,3},{[int,vec3,vec3,we],we}},
+{{wings_va,set_edge_colors,1},{[list,we],we}},
+{{wings_va,set_edge_uvs,1},{[list,we],we}},
+{{wings_va,set_face_attr_vs,3},{[atom,int,list,we],we}},
+{{wings_va,set_face_attrs,2},{[int,val,we],we}},
+{{wings_va,set_face_color,2},{[gbset,vec3,we],we}},
+{{wings_va,set_vertex_color,2},{[gbset,vec3,we],we}},
+{{wings_va,set_vtx_face_uvs,3},{[int,list,vec2,we],we}},
+{{wings_va,vtx_attrs,1},{[int,we],attr}},
+{{wings_va,vtx_attrs,2},{[int,int,we],attr}},
+{{wings_vertex,bounding_box,0},{[we],vec3}},
+{{wings_vertex,bounding_box,1},{[we,val],vec3}},
+{{wings_vertex,bounding_box,2},{[list,we,val],vec3}},
+{{wings_vertex,center,0},{[we],vec3}},
+{{wings_vertex,center,1},{[gbset,we],vec3}},
+{{wings_vertex,connect,2},{[int,list,we],we}},
+{{wings_vertex,connect_cut,2},{[int,int,we],[we,gbset]}},
+{{wings_vertex,dissolve_isolated,1},{[list,we],we}},
+{{wings_vertex,edge_through,2},{[int,int,we],int}},
+{{wings_vertex,edge_through,3},{[int,int,int,we],int}},
+{{wings_vertex,flatten,2},{[list,vec3,we],we}},
+{{wings_vertex,flatten,3},{[list,vec3,vec3,we],we}},
+{{wings_vertex,force_connect,3},{[int,int,int,we],we}},
+{{wings_vertex,from_edges,1},{[gbset,we],gbset}},
+{{wings_vertex,from_faces,1},{[gbset,we],list}},
+{{wings_vertex,isolated,0},{[we],gbset}},
+{{wings_vertex,normal,1},{[int,we],vec3}},
+{{wings_vertex,outer_vertices_ccw,1},{[list,we],list}},
+{{wings_vertex,per_face,1},{[list,we],list}},
+{{wings_vertex,pos,1},{[int,we],vec3}},
+{{wings_vertex,reachable,1},{[list,we],list}},
+{{wings_vertex_cmd,bevel_vertex,1},{[int,we],we}},
+{{wings_vertex_cmd,connect,1},{[list,we],we}},
+{{wings_we,all_hidden,0},{[we],bool}},
+{{wings_we,break_mirror,0},{[we],we}},
+{{wings_we,centroid,0},{[we],vec3}},
+{{wings_we,create_holes,1},{[list,we],we}},
+{{wings_we,create_mirror,1},{[int,we],we}},
+{{wings_we,fast_rebuild,0},{[we],we}},
+{{wings_we,freeze_mirror,0},{[we],we}},
+{{wings_we,fully_visible_edges,1},{[ordset,we],list}},
+{{wings_we,hide_faces,1},{[gbset,we],we}},
+{{wings_we,invert_normals,0},{[we],we}},
+{{wings_we,is_consistent,0},{[we],bool}},
+{{wings_we,is_face_consistent,1},{[int,we],bool}},
+{{wings_we,is_open,0},{[we],bool}},
+{{wings_we,mirror_projection,0},{[we],matrix}},
+{{wings_we,new_id,0},{[we],[int,we]}},
+{{wings_we,new_ids,1},{[int,we],[int,we]}},
+{{wings_we,new_wrap_range,2},{[int,int,we],[tuple,we]}},
+{{wings_we,normals,2},{[list,we,matrix],list}},
+{{wings_we,num_hidden,0},{[we],int}},
+{{wings_we,perimeter,0},{[we],float}},
+{{wings_we,rebuild,0},{[we],we}},
+{{wings_we,renumber,1},{[we,int],we}},
+{{wings_we,renumber,2},{[we,int,list],we}},
+{{wings_we,separate,0},{[we],list}},
+{{wings_we,show_faces,0},{[we],we}},
+{{wings_we,show_faces,1},{[list,we],we}},
+{{wings_we,surface_area,0},{[we],float}},
+{{wings_we,transform_vs,1},{[matrix,we],we}},
+{{wings_we,uv_mapped_faces,0},{[we],list}},
+{{wings_we,uv_to_color,0},{[we,st],we}},
+{{wings_we,validate_mirror,0},{[we],we}},
+{{wings_we,visible,0},{[we],list}},
+{{wings_we,visible,1},{[list,we],list}},
+{{wings_we,visible_edges,0},{[we],list}},
+{{wings_we,visible_edges,1},{[gbset,we],list}},
+{{wings_we,visible_vs,0},{[we],list}},
+{{wings_we,visible_vs,1},{[list,we],list}},
+{{wings_we,volume,0},{[we],float}},
+{{e3d_mat,identity,0},{[],matrix}},
+{{e3d_mat,is_identity,1},{[matrix],bool}},
+{{e3d_mat,determinant,1},{[matrix],float}},
+{{e3d_mat,print,1},{[matrix],atom}},
+{{e3d_mat,compress,1},{[matrix],matrix}},
+{{e3d_mat,expand,1},{[matrix],matrix}},
+{{e3d_mat,translate,1},{[vec3],matrix}},
+{{e3d_mat,translate,3},{[float,float,float],matrix}},
+{{e3d_mat,scale,1},{[vec3_or_float],matrix}},
+{{e3d_mat,scale,3},{[float,float,float],matrix}},
+{{e3d_mat,rotate,2},{[float,vec3],matrix}},
+{{e3d_mat,rotate_from_euler_rad,1},{[vec3],matrix}},
+{{e3d_mat,rotate_from_euler_rad,3},{[float,float,float],matrix}},
+{{e3d_mat,rotate_to_z,1},{[vec3],matrix}},
+{{e3d_mat,rotate_s_to_t,2},{[vec3,vec3],matrix}},
+{{e3d_mat,project_to_plane,1},{[vec3],matrix}},
+{{e3d_mat,transpose,1},{[matrix],matrix}},
+{{e3d_mat,invert,1},{[matrix],matrix}},
+{{e3d_mat,add,2},{[matrix,matrix],matrix}},
+{{e3d_mat,mul,1},{[list],matrix}},
+{{e3d_mat,mul,2},{[matrix,matrix_or_float],matrix}},
+{{e3d_mat,mul_point,2},{[matrix,vec3],vec3}},
+{{e3d_mat,mul_vector,2},{[matrix,vec3],vec3}},
+{{e3d_mat,eigenv3,1},{[matrix],[vec3,matrix]}},
+{{e3d_vec,zero,0},{[],vec3}},
+{{e3d_vec,is_zero,1},{[vec3],bool}},
+{{e3d_vec,add,1},{[list],vec3}},
+{{e3d_vec,add,2},{[vec3,vec3],vec3}},
+{{e3d_vec,add_prod,3},{[vec3,vec3,float],vec3}},
+{{e3d_vec,sub,1},{[list],vec3}},
+{{e3d_vec,sub,2},{[vec3,vec3],vec3}},
+{{e3d_vec,lerp,3},{[vec3,vec3,float],vec3}},
+{{e3d_vec,norm_sub,2},{[vec3,vec3],vec3}},
+{{e3d_vec,mul,2},{[vec3,float],vec3}},
+{{e3d_vec,divide,2},{[vec3,float],vec3}},
+{{e3d_vec,neg,1},{[vec3],vec3}},
+{{e3d_vec,dot,2},{[vec3,vec3],float}},
+{{e3d_vec,cross,2},{[vec3,vec3],vec3}},
+{{e3d_vec,len,1},{[vec3],float}},
+{{e3d_vec,len_sqr,1},{[vec3],float}},
+{{e3d_vec,dist,2},{[vec3,vec3],float}},
+{{e3d_vec,dist_sqr,2},{[vec3,vec3],float}},
+{{e3d_vec,norm,1},{[vec3],vec3}},
+{{e3d_vec,norm,3},{[float,float,float],vec3}},
+{{e3d_vec,normal,3},{[vec3,vec3,vec3],vec3}},
+{{e3d_vec,normal,1},{[list],vec3}},
+{{e3d_vec,average,1},{[list],vec3}},
+{{e3d_vec,average,2},{[vec3,vec3],vec3}},
+{{e3d_vec,average,4},{[vec3,vec3,vec3,vec3],vec3}},
+{{e3d_vec,area,3},{[vec3,vec3,vec3],float}},
+{{e3d_vec,degrees,2},{[vec3,vec3],float}},
+{{e3d_vec,plane,1},{[list],tuple}},
+{{e3d_vec,plane,2},{[vec3,vec3],tuple}},
+{{e3d_vec,plane,3},{[vec3,vec3,vec3],tuple}},
+{{e3d_vec,plane_side,2},{[vec3,tuple],integer}},
+{{e3d_vec,plane_dist,2},{[vec3,tuple],float}},
+{{e3d_vec,line_dist,3},{[vec3,vec3,vec3],float}},
+{{e3d_vec,line_dist_sqr,3},{[vec3,vec3,vec3],float}},
+{{e3d_vec,line_line_intersect,4},{[vec3,vec3,vec3,vec3],any}},
+{{e3d_vec,project_2d,2},{[vec3,atom],vec3}},
+{{e3d_vec,largest_dir,1},{[vec3],atom}},
+{{e3d_bv,box,0},{[],tuple}},
+{{e3d_bv,box,1},{[list],tuple}},
+{{e3d_bv,box,2},{[any,any],tuple}},
+{{e3d_bv,box,3},{[vec3,vec3,float],tuple}},
+{{e3d_bv,union,2},{[tuple,tuple],tuple}},
+{{e3d_bv,dist,2},{[tuple,tuple],float_or_false}},
+{{e3d_bv,intersect,2},{[tuple,tuple],bool}},
+{{e3d_bv,center,1},{[tuple],vec3}},
+{{e3d_bv,max_extent,1},{[tuple],any}},
+{{e3d_bv,surface_area,1},{[tuple],float}},
+{{e3d_bv,volume,1},{[tuple],float}},
+{{e3d_bv,sphere,1},{[tuple],tuple}},
+{{e3d_bv,inside,2},{[vec3,tuple],bool}},
+{{e3d_bv,hit,2},{[tuple,tuple],bool}},
+{{e3d_bv,hit,3},{[tuple,tuple,tuple],bool}},
+{{e3d_bv,inv_sign,1},{[vec3],tuple}},
+{{e3d_bv,eigen_vecs,1},{[list],tuple}},
+{{e3d_bv,quickhull,1},{[list],tuple}},
+{{e3d_bv,covariance_matrix,1},{[list],tuple}}
+].
+
diff --git a/plugins_src/scripting/init_scripts/defaults.conf b/plugins_src/scripting/init_scripts/defaults.conf
new file mode 100644
index 000000000..a6b53fb61
--- /dev/null
+++ b/plugins_src/scripting/init_scripts/defaults.conf
@@ -0,0 +1,24 @@
+
+%%
+%% Scripting for Shapes
+%%
+%% Copyright 2025 Edward Blake
+%%
+%% See the file "license.terms" for information on usage and redistribution
+%% of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+%%
+%% $Id$
+%%
+
+%%
+%% Set defaults before the user specific preferences are set.
+
+%%
+%% To set the python binary filename, see python.script-init-conf
+%% To set the gauche binary filename, see scheme.script-init-conf
+%%
+
+{setting_enable_commands, true}.
+{setting_enable_import, true}.
+{setting_enable_export, true}.
+
diff --git a/plugins_src/scripting/init_scripts/py-modnames b/plugins_src/scripting/init_scripts/py-modnames
new file mode 100644
index 000000000..5b83fec88
--- /dev/null
+++ b/plugins_src/scripting/init_scripts/py-modnames
@@ -0,0 +1,36 @@
+
+%%
+%% Scripting for Shapes
+%%
+%% Copyright 2025 Edward Blake
+%%
+%% See the file "license.terms" for information on usage and redistribution
+%% of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+%%
+%% $Id$
+%%
+
+%% How modules are named in the generated python script files.
+[
+{wings_collapse, [collapse]},
+{wings_dissolve, [dissolve]},
+{wings_edge, [edge]},
+{wings_edge_cmd, [edge_cmd]},
+{wings_edge_loop, [edge_loop]},
+{wings_extrude_edge, [extrude_edge]},
+{wings_extrude_face, [extrude_face]},
+{wings_face, [face]},
+{wings_face_cmd, [face_cmd]},
+{wings_facemat, [facemat]},
+{wings_subdiv, [subdiv]},
+{wings_tesselation, [tesselation]},
+{wings_util, [util]},
+{wings_va, [va]},
+{wings_vertex, [vertex]},
+{wings_vertex_cmd, [vertex_cmd]},
+{wings_we, [we]},
+{e3d_mat, [e3d_mat]},
+{e3d_vec, [e3d_vec]},
+{e3d_bv, [e3d_bv]}
+].
+
diff --git a/plugins_src/scripting/init_scripts/py/init.py b/plugins_src/scripting/init_scripts/py/init.py
new file mode 100644
index 000000000..1bab1fc8c
--- /dev/null
+++ b/plugins_src/scripting/init_scripts/py/init.py
@@ -0,0 +1,128 @@
+
+##
+## Scripting for Shapes (Scheme and Python)
+##
+## Copyright 2023-2025 Edward Blake
+##
+## See the file "license.terms" for information on usage and redistribution
+## of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+##
+## $Id$
+##
+
+##
+## This python code acts as a boot strap to define some useful functions
+## and then load the actual script that is sent as a symbolic expression through
+## standard input by the erlang plugin.
+##
+
+import w3d_int
+import sys
+import time
+from os import path
+from runpy import run_path
+
+
+sys.stdin.reconfigure(encoding="utf-8")
+sys.stdout.reconfigure(encoding="utf-8")
+sys.stderr.reconfigure(encoding="utf-8")
+
+scr_langstrs = {}
+
+def set_lang_strings(l):
+ global scr_langstrs
+ for a in l:
+ scr_langstrs[a[0]] = a[1]
+
+def __t(id, str):
+ global scr_langstrs
+ if id in scr_langstrs:
+ return scr_langstrs[id]
+ else:
+ return str
+
+def script_loader_loop():
+ # Main function to call, can be called many times
+ scr_main_fun = None
+ while True:
+ while True:
+ line = sys.stdin.readline()
+ if (line == ""):
+ pass
+ cmd_0, _ = w3d_int.scm_parse(line)
+ cmd = cmd_0[0]
+ break
+
+ cmd_atom = cmd[0]
+ if cmd_atom == "run_init":
+ g = {}
+ g["scm_parse"] = w3d_int.scm_parse
+ g["OutputList"] = w3d_int.OutputList
+ g["Okay"] = w3d_int.Okay
+ ## Use __t(20, "Text") for localized text
+ g["__t"] = __t
+ script_file = cmd[1]
+ set_lang_strings(cmd[2])
+ sys.path.insert(0, path.dirname(script_file))
+ g2 = run_path(script_file, g)
+ scr_main_fun = None
+ if "w3d_main_function" in g2:
+ scr_main_fun = g2["w3d_main_function"]
+ if scr_main_fun == None:
+ sys.stderr.write("ERROR: main function not set\n")
+ exit(1)
+ print("(ok)")
+ sys.stdout.flush()
+
+ elif cmd_atom == "run":
+ ## Parameters being passed to the script from the script's
+ ## parameter options window.
+ params = []
+
+ ## Access parameters by key, for those that used an atom key
+ params_by_key = {}
+
+ ## Extra parameters passed as the third argument
+ ## These are parameters that are not set via a parameter
+ ## window and may be auxiliary variables dependent on the
+ ## type of script (e.g. "content" contains an e3d_file tuple
+ ## for exporter plugins, parameters set with "script_params"
+ ## also show up in here).
+ extra_params = {}
+
+ params = cmd[2]
+
+ ## Add to params_by_key parameters that are keyed
+ for p in params:
+ if hasattr(p, "__len__"):
+ if len(p) == 2:
+ pv = p[1]
+ params_by_key[p[0]] = pv
+
+ ## Grab the extra params
+ extra_params_1 = cmd[3]
+ for p in extra_params_1:
+ extra_params[p[0]] = p[1]
+ returned = scr_main_fun(params, params_by_key, extra_params)
+ if returned != None:
+ print("")
+ o = None
+ if hasattr(returned,"as_output_list"):
+ o = returned.as_output_list()
+ elif hasattr(returned,"__len__"):
+ o = w3d_int.OutputList()
+ for r in returned:
+ o.add_value(r)
+ if o != None:
+ o.write_list_out(sys.stdout)
+ print("")
+ sys.stdout.flush()
+ print("(%ok)")
+ sys.stdout.flush()
+
+ else:
+ print("ERROR: Not run")
+ exit(1)
+
+script_loader_loop()
+
diff --git a/plugins_src/scripting/init_scripts/py/w3d_e3d.py b/plugins_src/scripting/init_scripts/py/w3d_e3d.py
new file mode 100644
index 000000000..09468c753
--- /dev/null
+++ b/plugins_src/scripting/init_scripts/py/w3d_e3d.py
@@ -0,0 +1,538 @@
+
+##
+## Scripting for Shapes (Scheme and Python)
+##
+## Copyright 2023-2025 Edward Blake
+##
+## See the file "license.terms" for information on usage and redistribution
+## of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+##
+## $Id$
+##
+
+##
+## Helper classes for Scripting to Wings 3D
+##
+
+from w3d_int import OutputList
+import sys
+
+E3D_INFINITY = 3.402823e+38 ## 32 bits float max
+
+def list_of_objects_to_outputlist(objlist):
+ if hasattr(objlist, "__len__"):
+ outputlist = OutputList()
+ for obj in objlist:
+ if type(obj) is str:
+ outputlist.add_str(obj)
+ else:
+ outputlist.add_value(obj)
+ return outputlist
+ else:
+ return objlist.as_output_list()
+
+
+def list_of_pairs_to_outputlist(objlist):
+ if hasattr(objlist, "__len__"):
+ outputlist = OutputList()
+ for obj in objlist:
+ o = obj.as_output_list()
+ outputlist.add_vector(o)
+ return outputlist
+ else:
+ return objlist.as_output_list()
+
+def list_of_atoms_to_outputlist(objlist):
+ if hasattr(objlist, "__len__"):
+ outputlist = OutputList()
+ outputlist.add_symbol('!list')
+ for obj in objlist:
+ if type(obj) is str:
+ outputlist.add_symbol(obj)
+ else:
+ outputlist.add_value(obj)
+ return outputlist
+ else:
+ return objlist.as_output_list()
+
+## Types for transform
+class E3DTransf:
+ def __init__(self):
+ self.mat = []
+ self.inv = []
+
+ def send(self):
+ o3 = OutputList()
+ o3.add_symbol("e3d_transf")
+ o3.add_list(self.mat)
+ o3.add_list(self.inv)
+ return o3
+
+ def load_from(self, tuple):
+ if tuple[0] == "e3d_transf":
+ t_mats = tuple[1]
+ for mt in t_mats:
+ self.mat.append(mt)
+ t_invs = tuple[2]
+ for inv in t_invs:
+ self.inv.append(inv)
+
+
+class Ray:
+ def __init__(self):
+ self.o = None
+ self.d = None
+ self.n = None
+ self.f = None
+ self.bfc = True
+
+ def as_output_list(self):
+ o3 = OutputList()
+ o3.add_symbol("ray")
+ o3.add_vector(self.o)
+ o3.add_vector(self.d)
+ o3.add_float(self.n)
+ o3.add_float(self.f)
+ o3.add_boolean(self.bfc)
+
+ return o3
+
+ def load_from(self, tuple):
+ if tuple[0] == "ray":
+ t_o = tuple[1]
+ self.o = t_o
+ t_d = tuple[2]
+ self.d = t_d
+ t_n = tuple[3]
+ self.n = t_n # Near, far (or MinT MaxT)
+ t_f = tuple[4]
+ self.f = t_f
+ self.bfc = tuple[5]
+
+
+class E3DFace:
+ def __init__(self):
+ self.vs = [] #List of vertex indices.
+ self.vc = [] #Vertex color indices.
+ self.tx = [] #List of texture indices.
+ self.ns = [] #List of normal indices.
+ self.mat = [] #Materials for face.
+ self.sg = 1 #Smooth group for face.
+ self.vis = -1 #Visible edges (as in 3DS).
+
+ def as_output_list(self):
+ o3 = OutputList()
+ o3.add_symbol("e3d_face")
+
+ o3.add_list(list_of_objects_to_outputlist(self.vs))
+ o3.add_list(list_of_objects_to_outputlist(self.vc))
+ o3.add_list(list_of_objects_to_outputlist(self.tx))
+ o3.add_list(list_of_objects_to_outputlist(self.ns))
+ o3.add_list(list_of_atoms_to_outputlist(self.mat))
+
+ o3.add_integer(self.sg)
+ o3.add_integer(self.vis)
+ return o3
+
+ def load_from(self, tuple):
+ if tuple[0] == "e3d_face":
+ t_vs = tuple[1]
+ self.vs = []
+ for vs1 in t_vs:
+ self.vs.append(vs1)
+
+ t_vc = tuple[2]
+ self.vc = []
+ for vc1 in t_vc:
+ self.vc.append(vc1)
+
+ t_tx = tuple[3]
+ self.tx = []
+ for tx1 in t_tx:
+ self.tx.append(tx1)
+
+ t_ns = tuple[4]
+ self.ns = []
+ for ns1 in t_ns:
+ self.ns.append(ns1)
+
+ t_mats = tuple[5]
+ self.mat = [] #Materials for face.
+ for mat1 in t_mats:
+ self.mat.append(mat1)
+ self.sg = tuple[6]
+ self.vis = tuple[7]
+
+## Polygon mesh.
+class E3DMesh:
+ def __init__(self):
+ self.type = "triangle" # 'triangle' | 'quad' | 'polygon',
+ self.vs = [] #Vertex table (list).
+ self.vc = [] #Vertex color table (list).
+ self.tx = [] #Texture coordinates (list).
+ self.ns = [] #Normal table (list).
+ self.fs = [] #Face table (list of e3d_face).
+ self.he = [] #List of chains of hard edges.
+ self.matrix = "identity" #Local coordinate system.
+
+ def as_output_list(self):
+ o3 = OutputList()
+ o3.add_symbol("e3d_mesh")
+ o3.add_symbol(self.type)
+
+ o3.add_list(list_of_objects_to_outputlist(self.vs))
+ o3.add_list(list_of_objects_to_outputlist(self.vc))
+ o3.add_list(list_of_objects_to_outputlist(self.tx))
+ o3.add_list(list_of_objects_to_outputlist(self.ns))
+ o3.add_list(list_of_objects_to_outputlist(self.fs))
+ o3.add_list(list_of_objects_to_outputlist(self.he))
+
+ if self.matrix == 'identity':
+ o3.add_symbol(self.matrix)
+ else:
+ o3.add_vector(list_of_objects_to_outputlist(self.matrix))
+
+ return o3
+
+ def load_from(self, tuple):
+ if tuple[0] == "e3d_mesh":
+ self.type = tuple[1]
+
+ t_vs = tuple[2]
+ self.vs = []
+ for vs1 in t_vs:
+ self.vs.append((vs1[0], vs1[1], vs1[2]))
+
+ t_vc = tuple[3]
+ self.vc = []
+ for vc1 in t_vc:
+ self.vc.append(vc1)
+
+ t_tx = tuple[4]
+ self.tx = []
+ for tx1 in t_tx:
+ self.tx.append(tx1)
+
+ t_ns = tuple[5]
+ self.ns = []
+ for ns1 in t_ns:
+ self.ns.append(ns1)
+
+ t_fs = tuple[6]
+ self.fs = []
+ for fs1 in t_fs:
+ new_fs = E3DFace()
+ new_fs.load_from(fs1)
+ self.fs.append(new_fs)
+
+ t_he = tuple[7]
+ self.he = []
+ for he1 in t_he:
+ self.he.append(he1)
+
+ self.matrix = tuple[8]
+
+class E3DObject:
+ def __init__(self):
+ self.name = None #Name of object (string), or 'undefined' if no name.
+ self.obj = None #Object implementation.
+ self.mat = [] #Materials for this object.
+ self.attr = [] #List of attributes.
+
+ def as_output_list(self):
+ o3 = OutputList()
+ o3.add_symbol("e3d_object")
+ if self.name is None:
+ o3.add_symbol("undefined")
+ else:
+ o3.add_str(self.name)
+
+ if self.obj is None:
+ o3.add_symbol("undefined")
+ else:
+ o_obj = self.obj.as_output_list()
+ o3.add_list(o_obj)
+
+ o3.add_list(list_of_pairs_to_outputlist(self.mat))
+ o3.add_list(list_of_pairs_to_outputlist(self.attr))
+ return o3
+
+ def load_from(self, tuple):
+ if tuple[0] == 'e3d_object':
+ t_name_0 = tuple[1]
+ if t_name_0 != 'undefined':
+ t_name = str(t_name_0)
+ if t_name != '':
+ self.name = t_name
+ t_obj = tuple[2]
+ self.obj = None
+ if t_obj != 'undefined' and hasattr(t_obj, "__len__"):
+ new_obj = E3DMesh()
+ new_obj.load_from(t_obj)
+ self.obj = new_obj
+
+ t_mats = tuple[3]
+ for t_mat in t_mats:
+ pass ## TODO
+
+ t_attrs = tuple[4]
+ for t_attr in t_attrs:
+ pass ## TODO
+
+class E3DImage:
+ def __init__(self):
+ self.type = 'r8g8b8' ## [g8 (gray8), a8 (alpha8) (Ch:Size)+[s|f]=signed|float]
+ self.bytes_pp = 3 ## bytes per pixel
+ self.alignment = 1 ## A = 1|2|4 Next row starts direct|even 2|even 4
+ self.order = 'lower_left' ## First pixel is in: lower_left,lower_right,upper_left,upper_right]
+ self.width = 0 ## in pixels
+ self.height = 0 ## in pixels
+ self.image = None ## This is a temporary file path to a raw image
+ self.filename = None ## Filename or none
+ self.name = "" ## Name of image
+ self.extra = []
+
+ def as_output_list(self):
+ o3 = OutputList()
+ o3.add_symbol('e3d_image')
+ o3.add_symbol(self.type)
+ o3.add_integer(self.bytes_pp)
+ o3.add_integer(self.alignment)
+ o3.add_symbol(self.order)
+ o3.add_integer(self.width)
+ o3.add_integer(self.height)
+
+ if self.image == None:
+ o3.add_symbol('none')
+ else:
+ o3.add_str(self.image) ## Temporary file for raw images
+
+ if self.filename == None:
+ o3.add_symbol('none')
+ else:
+ o3.add_str(self.filename)
+ o3.add_str(self.name)
+ o3.add_list(list_of_objects_to_outputlist(self.extra))
+
+ return o3
+
+ def load_from(self, tuple):
+ if tuple[0] == 'e3d_image':
+ self.type = tuple[1]
+ self.bytes_pp = tuple[2]
+ self.alignment = tuple[3]
+ self.order = tuple[4]
+ self.width = tuple[5]
+ self.height = tuple[6]
+
+ ## TODO: Grab the binary raw image from the temporary file
+ self.image = tuple[7] ## Temporary file to raw image
+
+ self.filename = tuple[8]
+ self.name = tuple[9]
+ self.extra = tuple[10]
+
+class MaterialMaps:
+ def __init__(self):
+ self.maps = {}
+
+ def as_output_list(self):
+ o3 = OutputList()
+
+ for k in self.maps.keys():
+ v = self.maps[k]
+ attr_item = OutputList()
+ attr_item.add_symbol(k)
+ attr_item.add_vector(v.as_output_list())
+ o3.add_vector(attr_item)
+ return o3
+
+ def load_from(self, tlist):
+ for t_m in tlist:
+ if t_m[0] == 'diffuse':
+ new_img = E3DImage()
+ new_img.load_from(t_m[1])
+ self.maps[t_m[0]] = new_img
+
+
+class MaterialOpenGLAttributes:
+ def __init__(self):
+ self.ambient = [0.0,0.0,0.0,0.0]
+ self.specular = [0.1,0.1,0.1,1.0]
+ self.shininess = 0.2
+ self.diffuse = [0.7,0.7,0.7,1.0]
+ self.emission = [0.0,0.0,0.0,1.0]
+ self.metallic = 0.1
+ self.roughness = 0.8
+ self.vertex_colors = 'set'
+
+ def as_output_list(self):
+
+ o3 = OutputList()
+
+ ov_r = OutputList()
+ ov_r.add_numbers(self.ambient)
+ ov = OutputList()
+ ov.add_symbol('ambient')
+ ov.add_vector(ov_r)
+ o3.add_vector(ov)
+
+ ov_r = OutputList()
+ ov_r.add_numbers(self.specular)
+ ov = OutputList()
+ ov.add_symbol('specular')
+ ov.add_vector(ov_r)
+ o3.add_vector(ov)
+
+ ov = OutputList()
+ ov.add_symbol('shininess')
+ ov.add_float(self.shininess)
+ o3.add_vector(ov)
+
+ ov_r = OutputList()
+ ov_r.add_numbers(self.diffuse)
+ ov = OutputList()
+ ov.add_symbol('diffuse')
+ ov.add_vector(ov_r)
+ o3.add_vector(ov)
+
+ ov_r = OutputList()
+ ov_r.add_numbers(self.emission)
+ ov = OutputList()
+ ov.add_symbol('emission')
+ ov.add_vector(ov_r)
+ o3.add_vector(ov)
+
+ ov = OutputList()
+ ov.add_symbol('metallic')
+ ov.add_float(self.metallic)
+ o3.add_vector(ov)
+
+ ov = OutputList()
+ ov.add_symbol('roughness')
+ ov.add_float(self.roughness)
+ o3.add_vector(ov)
+
+ ov = OutputList()
+ ov.add_symbol('vertex_colors')
+ ov.add_symbol(self.vertex_colors)
+ o3.add_vector(ov)
+
+ return o3
+
+ def load_from(self, tlist):
+ for t_at in tlist:
+ if t_at[0] == 'ambient':
+ self.ambient = t_at[1]
+ if t_at[0] == 'specular':
+ self.specular = t_at[1]
+ if t_at[0] == 'shininess':
+ self.shininess = t_at[1]
+ if t_at[0] == 'diffuse':
+ self.diffuse = t_at[1]
+ if t_at[0] == 'emission':
+ self.emission = t_at[1]
+ if t_at[0] == 'metallic':
+ self.metallic = t_at[1]
+ if t_at[0] == 'roughness':
+ self.roughness = t_at[1]
+ if t_at[0] == 'vertex_colors':
+ self.vertex_colors = t_at[1]
+
+
+class Material:
+ def __init__(self):
+ self.name = "material"
+ self.attrs = {}
+
+ def as_output_list(self):
+ o3 = OutputList()
+ o3.add_symbol(self.name)
+
+ attr_list = OutputList()
+ for k in self.attrs.keys():
+ v = self.attrs[k]
+ attr_item = OutputList()
+ attr_item.add_symbol(k)
+ attr_item.add_list(v.as_output_list())
+ attr_list.add_vector(attr_item)
+ o3.add_list(attr_list)
+ return o3
+
+ def load_from(self, tuple):
+ self.name = tuple[0]
+ t_attrs = tuple[1]
+ for t_att in t_attrs:
+ k = t_att[0]
+ if k == 'maps':
+ new_attr = MaterialMaps()
+ new_attr.load_from(t_att[1])
+ elif k == 'opengl':
+ new_attr = MaterialOpenGLAttributes()
+ new_attr.load_from(t_att[1])
+ else:
+ new_attr = t_att[1]
+ self.attrs[k] = new_attr
+
+
+class E3DFile:
+ def __init__(self):
+ self.objs = [] # List of objects.
+ self.mat = [] # List of materials.
+ self.creator = "" # Creator string.
+ self.dir = "" # Directory for file.
+
+ def as_output_list(self):
+ o3 = OutputList()
+ o3.add_symbol("e3d_file")
+
+ o3.add_list(list_of_objects_to_outputlist(self.objs))
+ o3.add_list(list_of_pairs_to_outputlist(self.mat))
+
+ o3.add_str(self.creator)
+ o3.add_str(self.dir)
+ return o3
+
+ def load_from(self, tuple):
+ if tuple[0] == 'e3d_file':
+ t_objects = tuple[1]
+ self.objs = []
+ for t_object in t_objects:
+ new_obj = E3DObject()
+ new_obj.load_from(t_object)
+ self.objs.append(new_obj)
+
+ t_mats = tuple[2]
+ self.mat = []
+ for t_mat in t_mats:
+ new_mat = Material()
+ new_mat.load_from(t_mat)
+ self.mat.append(new_mat)
+
+ self.creator = str(tuple[3])
+
+ t_dir = tuple[4]
+ if t_dir == 'undefined':
+ self.dir = None
+ else:
+ self.dir = str(t_dir)
+
+
+## For scripts that change an E3DMesh and return the results
+class SetE3DMesh:
+ def __init__(self,mesh):
+ self.mesh = mesh
+ def as_output_list(self):
+ o = OutputList()
+ o.add_symbol("set_e3d_mesh")
+ o.add_list(self.mesh.as_output_list())
+ return o
+
+
+def test():
+ b = E3DFile()
+ b.objs.append(E3DObject())
+ o = b.as_output_list()
+ o.write_list_out(sys.stdout)
+
+
diff --git a/plugins_src/scripting/init_scripts/py/w3d_int.py b/plugins_src/scripting/init_scripts/py/w3d_int.py
new file mode 100644
index 000000000..06f349317
--- /dev/null
+++ b/plugins_src/scripting/init_scripts/py/w3d_int.py
@@ -0,0 +1,421 @@
+
+##
+## Scripting for Shapes (Scheme and Python)
+##
+## Copyright 2023-2025 Edward Blake
+##
+## See the file "license.terms" for information on usage and redistribution
+## of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+##
+## $Id$
+##
+
+##
+## Symbolic expression reader and writer for script input/output
+##
+## Python script side
+##
+
+import sys
+import threading
+import time
+
+def t_1():
+ scm_parse("(test '(0 1 2 3) #(2 3 4 5) '(\"Test\" 2 #f))")
+
+def scm_parse(a):
+ inp = a
+ lst = []
+ tok = ""
+ while inp != "":
+ if inp == "" and tok == "":
+ return lst, ""
+ elif inp == "" and len(tok) > 0:
+ lst.append(bare_word(tok))
+ tok = ""
+ elif inp[0] == '\"':
+ r = inp[1:]
+ str1, r_0 = scm_parse_str(r)
+ inp = r_0
+ lst.append(str1)
+ elif inp[0] == '#' and inp[1] == '(':
+ if tok == "":
+ r = inp[2:]
+ sublist, r_0 = scm_parse(r)
+ lst.append(sublist)
+ inp = r_0
+ else:
+ lst.append(bare_word(tok))
+ tok = ""
+ elif inp[0] == '\'' and inp[1] == '(':
+ if tok == "":
+ r = inp[2:]
+ sublist, r_0 = scm_parse(r)
+ lst.append(sublist)
+ inp = r_0
+ else:
+ lst.append(bare_word(tok))
+ tok = ""
+ elif inp[0] == '(':
+ if tok == "":
+ sublist, r_0 = scm_parse(inp[1:])
+ inp = r_0
+ lst.append(sublist)
+ else:
+ lst.append(bare_word(tok))
+ tok = ""
+ elif inp[0] == ')':
+ if tok == "":
+ return lst, inp[1:]
+ else:
+ lst.append(bare_word(tok))
+ tok = ""
+ elif inp[0] == ' ' or inp[0] == '\t' or inp[0] == '\r' or inp[0] == '\n':
+ if tok == "":
+ inp = inp[1:]
+ tok = ""
+ else:
+ lst.append(bare_word(tok))
+ tok = ""
+ elif ((inp[0] >= '0' and inp[0] <= '9') or (inp[0] == '.') or (inp[0] == '-')) and tok == "":
+ num, r_1 = scm_parse_num(inp)
+ lst.append(num)
+ tok = ""
+ inp = r_1
+ else:
+ tok = tok + inp[0]
+ inp = inp[1:]
+ return lst, ""
+
+
+def scm_parse_num(a):
+ inp = a
+ str1 = ""
+ is_float = False
+ while inp != "":
+ if inp[0] == ' ' or inp[0] == ')' or inp[0] == '\r' or inp[0] == '\n':
+ if is_float:
+ num = float(str1)
+ else:
+ num = int(str1)
+ return num, inp
+ else:
+ if inp[0] == '.':
+ is_float = True
+ str1 = str1 + inp[0]
+ inp = inp[1:]
+ if is_float:
+ num = float(str1)
+ else:
+ num = int(str1)
+ return num, ""
+
+def scm_parse_str(a):
+ inp = a
+ str1 = ""
+ while inp != "":
+ if inp[0] == '\\':
+ str1 = str1 + inp[1]
+ inp = inp[2:]
+ elif inp[0] == '"':
+ return str1, inp[1:]
+ else:
+ str1 = str1 + inp[0]
+ inp = inp[1:]
+
+def bare_word(a0):
+ a1 = a0
+ if a1 == "#t":
+ return True
+ elif a1 == "#f":
+ return False
+ else:
+ return a1
+
+def send_get_reply(b):
+ print("")
+ b.write_list_out(sys.stdout)
+ print("")
+ sys.stdout.flush()
+ reply = None
+ while True:
+ line = sys.stdin.readline()
+ if (line == ""):
+ pass
+ reply_0, _ = scm_parse(line)
+ reply = reply_0[0]
+ break
+ return reply
+
+
+def wings_set_var(varname, varval):
+ b = OutputList()
+ b.add_symbol("%setvar")
+ b.add_str(varname)
+ b.add_value(varval)
+ return send_get_reply(b)
+
+def wings_get_var(varname):
+ b = OutputList()
+ b.add_symbol("%getvar")
+ b.add_str(varname)
+ return send_get_reply(b)
+
+def wings_query(str):
+ b = OutputList()
+ b.add_symbol("%query")
+ b.add_str(str)
+ return send_get_reply(b)
+
+
+def wings_we__get(module,fun,*args):
+ b = OutputList()
+ b.add_symbol("%we")
+ lt = OutputList()
+ lt.add_symbol(module)
+ lt.add_symbol(fun)
+ for arg in args:
+ lt.add_value(arg)
+ b.add_list(lt)
+ return send_get_reply(b)
+
+
+def wings_we__change(module,fun,*args):
+ b = OutputList()
+ b.add_symbol("%we!")
+ lt = OutputList()
+ lt.add_symbol(module)
+ lt.add_symbol(fun)
+ for arg in args:
+ lt.add_value(arg)
+ b.add_list(lt)
+ return send_get_reply(b)
+
+
+## TODO: wings_with_we_change Mod Name Args Fun
+
+
+def wings_previous_we__get(stackindex,module,fun,*args):
+ b = OutputList()
+ b.add_symbol("%we-previous")
+ b.add_value(stackindex)
+ b.add_symbol(module)
+ b.add_symbol(fun)
+ for arg in args:
+ b.add_value(arg)
+ return send_get_reply(b)
+
+
+def wings_previous_we__change(stackindex,module,fun,*args):
+ b = OutputList()
+ b.add_symbol("%we-previous!")
+ b.add_value(stackindex)
+ b.add_symbol(module)
+ b.add_symbol(fun)
+ for arg in args:
+ b.add_value(arg)
+ return send_get_reply(b)
+
+
+
+def list_of_objects_to_outputlist(objlist):
+ if hasattr(objlist, "__len__"):
+ outputlist = OutputList()
+ for obj in objlist:
+ if type(obj) is str:
+ outputlist.add_str(obj)
+ else:
+ outputlist.add_value(obj)
+ return outputlist
+ else:
+ return objlist.as_output_list()
+
+def list_of_pairs_to_outputlist(objlist):
+ if hasattr(objlist, "__len__"):
+ outputlist = OutputList()
+ for obj in objlist:
+ o = obj.as_output_list()
+ outputlist.add_vector(o)
+ return outputlist
+ else:
+ return objlist.as_output_list()
+
+class OutputList:
+ def __init__(self):
+ self.lst_cont = []
+ self.lst_type = []
+
+ def add_symbol(self, a):
+ self.add(a, 0)
+
+ def add_str(self, a):
+ self.add(a, 1)
+
+ def add_number(self, a):
+ self.add(a, 2)
+
+ def add_numbers(self, alst):
+ for a in alst:
+ self.add(a, 2)
+
+ def add_integer(self, a):
+ self.add(a, 2)
+
+ def add_integers(self, alst):
+ for a in alst:
+ self.add(a, 2)
+
+ def add_float(self, a):
+ self.add(a, 2)
+
+ def add_floats(self, alst):
+ for a in alst:
+ self.add(a, 2)
+
+ def add_list(self, a):
+ self.add(a, 3)
+
+ def add_vector(self, a):
+ self.add(a, 4)
+
+ def add(self, a, typ):
+ self.lst_cont.append(a)
+ self.lst_type.append(typ)
+
+ def add_value(self, obj):
+ if type(obj) is str:
+ self.add_str(obj)
+ elif type(obj) is tuple:
+ o = list_of_objects_to_outputlist(obj)
+ self.add_vector(o)
+ elif hasattr(obj, "__len__"):
+ o = list_of_objects_to_outputlist(obj)
+ self.add_list(o)
+ elif (type(obj) is int):
+ self.add_integer(obj)
+ elif type(obj) is float:
+ self.add_float(obj)
+ elif hasattr(obj, "as_output_list"):
+ o = obj.as_output_list()
+ self.add_list(o)
+ else:
+ o = obj.as_output_list()
+ self.add_list(o)
+
+ def write_list_out(self, ost):
+ ost.write("(")
+ for i in range(0, len(self.lst_cont)):
+ if i > 0:
+ ost.write(" ")
+ if self.lst_type[i] == 0:
+ ost.write(self.lst_cont[i])
+ elif self.lst_type[i] == 1:
+ ts = self.lst_cont[i]
+ ts = ts.replace("\\", "\\\\")
+ ts = ts.replace("\"", "\\\"")
+ ost.write("\"" + ts + "\"")
+ elif self.lst_type[i] == 2:
+ ost.write(str(self.lst_cont[i]))
+ elif self.lst_type[i] == 3:
+ self.lst_cont[i].write_list_out(ost)
+ elif self.lst_type[i] == 4:
+ ost.write("#")
+ self.lst_cont[i].write_list_out(ost)
+ ost.write(")")
+
+class Okay:
+ def __init__(self,*args):
+ self.args = args
+ def as_output_list(self):
+ o = OutputList()
+ o.add_symbol("ok")
+ for arg in self.args:
+ o.add_list(arg.as_output_list())
+ return o
+
+
+## For scripts that change vertex positions
+class Points:
+ def __init__(self):
+ self.list = []
+
+ def as_output_list(self):
+ o3 = OutputList()
+ for v in self.list:
+ vo2 = OutputList()
+ vo3 = OutputList()
+ vo3.add_float(v[1][0])
+ vo3.add_float(v[1][1])
+ vo3.add_float(v[1][2])
+ vo2.add_integer(v[0])
+ vo2.add_vector(vo3)
+ o3.add_vector(vo2)
+ return o3
+
+ def load_from(self, vlist):
+ for pair in vlist:
+ a1 = pair[0]
+ a2 = pair[1]
+ a2_x = a2[0]
+ a2_y = a2[1]
+ a2_z = a2[2]
+ self.list.append((a1, (a2_x, a2_y, a2_z)))
+
+class SetPoints:
+ def __init__(self,points):
+ self.points = points
+ def as_output_list(self):
+ o = OutputList()
+ o.add_symbol("set_points")
+ o.add_value(self.points)
+ return o
+
+
+## Long running process
+##
+## Example:
+##
+## class Examp:
+## def run(self):
+## sum = 0
+## for i in range(0, 100000000):
+## sum = sum + i
+##
+## print("Begin")
+## lrp = LongRunningProcess()
+## lrp.run(Examp())
+## print("Done")
+##
+
+def lrp_standby_function(lro):
+ while True:
+ lro.standbySema.acquire()
+ v = lro.standbyState
+ lro.standbySema.release()
+ if v:
+ break
+ print("('%keepalive 0)")
+ time.sleep(1)
+
+## A thread that simply sends keep-alive messages back to the scripting
+## plugin to keep it from timing out while the script processes something
+## that takes a while.
+##
+class LongRunningProcess:
+ def __init__(self):
+ self.standbyState = False
+ self.standbySema = threading.Semaphore()
+ self.standbyThread = threading.Thread(target=lrp_standby_function, args=(self,))
+
+ def run(self, runo):
+ self.standbyThread.start()
+ ## Try is needed or main thread will close on an error but leaves
+ ## the standby thread running.
+ try:
+ runo.run()
+ finally:
+ self.standbySema.acquire()
+ self.standbyState = True
+ self.standbySema.release()
+ self.standbyThread.join()
+
diff --git a/plugins_src/scripting/init_scripts/py/w3d_newshape.py b/plugins_src/scripting/init_scripts/py/w3d_newshape.py
new file mode 100644
index 000000000..a1855c01c
--- /dev/null
+++ b/plugins_src/scripting/init_scripts/py/w3d_newshape.py
@@ -0,0 +1,60 @@
+
+##
+## Scripting for Shapes (Scheme and Python)
+##
+## Copyright 2023-2025 Edward Blake
+##
+## See the file "license.terms" for information on usage and redistribution
+## of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+##
+## $Id$
+##
+
+from w3d_int import OutputList, list_of_pairs_to_outputlist
+import sys
+
+class ListOfArrays:
+ def __init__(self, l):
+ self.l = l
+ def as_output_list(self):
+ l2 = OutputList()
+ for item in self.l:
+ l3 = OutputList()
+ l3.add_numbers(item)
+ l2.add_list(l3)
+ return l2
+
+class ListOfTuples:
+ def __init__(self, l):
+ self.l = l
+ def as_output_list(self):
+ l2 = OutputList()
+ for item in self.l:
+ l3 = OutputList()
+ l3.add_numbers(item)
+ l2.add_vector(l3)
+ return l2
+
+class NewShape:
+ def __init__(self):
+ self.prefix = "shape"
+ ## Face and Vertices version
+ self.fs = []
+ self.vs = []
+
+ ## E3DObject version
+ self.obj = None
+ self.mat = []
+
+ def as_output_list(self):
+ o3 = OutputList()
+ o3.add_symbol("new_shape")
+ o3.add_str(self.prefix)
+ if self.obj == None:
+ o3.add_list(list_of_objects_to_outputlist(self.fs))
+ o3.add_list(list_of_objects_to_outputlist(self.vs))
+ else:
+ o3.add_list(self.obj.as_output_list())
+ o3.add_list(list_of_pairs_to_outputlist(self.mat))
+ return o3
+
diff --git a/plugins_src/scripting/init_scripts/py/w3d_we.1.py b/plugins_src/scripting/init_scripts/py/w3d_we.1.py
new file mode 100644
index 000000000..cc3fee182
--- /dev/null
+++ b/plugins_src/scripting/init_scripts/py/w3d_we.1.py
@@ -0,0 +1,17 @@
+
+##
+## Scripting for Shapes (Scheme and Python)
+##
+## Copyright 2025 Edward Blake
+##
+## See the file "license.terms" for information on usage and redistribution
+## of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+##
+## $Id$
+##
+
+from w3d_int import OutputList,wings_we__change,wings_we__get
+
+## This file is automatically generated from callable.conf and py-modnames
+
+
diff --git a/plugins_src/scripting/init_scripts/python.script-init-conf b/plugins_src/scripting/init_scripts/python.script-init-conf
new file mode 100644
index 000000000..fd95bfbec
--- /dev/null
+++ b/plugins_src/scripting/init_scripts/python.script-init-conf
@@ -0,0 +1,29 @@
+
+%%
+%% Scripting for Shapes
+%%
+%% Copyright 2025 Edward Blake
+%%
+%% See the file "license.terms" for information on usage and redistribution
+%% of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+%%
+%% $Id$
+%%
+
+%%
+%% Sets the default python binary filenames
+%%
+
+%% NAME ARGS INITIAL SCRIPT
+{ "py", false, "py/init.py", [
+ %% List of interpreter filenames to try
+ "python",
+ "python38",
+ "python36",
+ "python34",
+ "python32",
+ "python3"
+], [
+ %% No special arguments
+]}.
+
diff --git a/plugins_src/scripting/init_scripts/scheme.script-init-conf b/plugins_src/scripting/init_scripts/scheme.script-init-conf
new file mode 100644
index 000000000..d23377201
--- /dev/null
+++ b/plugins_src/scripting/init_scripts/scheme.script-init-conf
@@ -0,0 +1,38 @@
+
+%%
+%% Scripting for Shapes
+%%
+%% Copyright 2025 Edward Blake
+%%
+%% See the file "license.terms" for information on usage and redistribution
+%% of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+%%
+%% $Id$
+%%
+
+%%
+%% Sets the default gauche binary filenames
+%%
+
+%% NAME ARGS INITIAL SCRIPT
+{ "scm", true, "scm/init.scm", [
+ %% List of interpreter filenames to try
+ "gosh",
+ "csi"
+], [
+ %% Special arguments depending on the interpreter found
+ {auto_fill, [
+ %% Required arguments for Chicken Scheme interpreter (csi)
+ {"csi", [
+ "-q", "-n", "-b",
+ "-eval", {wrap, "(load \"%s\")", {esc, {absname, "init_env_csi.scm", "%BASEDIR%/scm"}}}
+ ]},
+ %% Required arguments for Gauche shell (gosh)
+ {"gosh", [
+ "-q", "-b",
+ {concat, "-A", "%BASEDIR%/scm"},
+ {concat, "-l", "init_env_gauche.scm"}
+ ]}
+ ]}
+]}.
+
diff --git a/plugins_src/scripting/init_scripts/scm-modnames b/plugins_src/scripting/init_scripts/scm-modnames
new file mode 100644
index 000000000..864a233ab
--- /dev/null
+++ b/plugins_src/scripting/init_scripts/scm-modnames
@@ -0,0 +1,36 @@
+
+%%
+%% Scripting for Shapes
+%%
+%% Copyright 2025 Edward Blake
+%%
+%% See the file "license.terms" for information on usage and redistribution
+%% of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+%%
+%% $Id$
+%%
+
+%% How modules are named in the generated python script files.
+[
+{wings_collapse, [we,collapse]},
+{wings_dissolve, [we,dissolve]},
+{wings_edge, [we,edge]},
+{wings_edge_cmd, [we,edge_cmd]},
+{wings_edge_loop, [we,edge_loop]},
+{wings_extrude_edge, [we,extrude_edge]},
+{wings_extrude_face, [we,extrude_face]},
+{wings_face, [we,face]},
+{wings_face_cmd, [we,face_cmd]},
+{wings_facemat, [we,facemat]},
+{wings_subdiv, [we,subdiv]},
+{wings_tesselation, [we,tesselation]},
+{wings_util, [we,util]},
+{wings_va, [we,va]},
+{wings_vertex, [we,vertex]},
+{wings_vertex_cmd, [we,vertex_cmd]},
+{wings_we, [we,we]},
+{e3d_mat, [e3d_mat]},
+{e3d_vec, [e3d_vec]},
+{e3d_bv, [e3d_bv]}
+].
+
diff --git a/plugins_src/scripting/init_scripts/scm/init.1.scm b/plugins_src/scripting/init_scripts/scm/init.1.scm
new file mode 100644
index 000000000..0fda64e4f
--- /dev/null
+++ b/plugins_src/scripting/init_scripts/scm/init.1.scm
@@ -0,0 +1,361 @@
+
+;;
+;; Scripting for Shapes (init for Scheme)
+;;
+;; Copyright 2023-2025 Edward Blake
+;;
+;; See the file "license.terms" for information on usage and redistribution
+;; of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+;;
+;; $Id$
+;;
+
+;;
+;; This scheme code acts as a boot strap to define some useful functions
+;; and then load the actual script. The actual script is sent by the erlang
+;; plugin as a symbolic expression through standard input.
+;;
+
+;; Main function of script, can be called multiple times
+;;
+(define *scr-main-fun* #f)
+(define (main-function a)
+ (set! *scr-main-fun* a))
+
+;; The full path of the script being run
+(define *script-full-path* "")
+
+;; The directory of the script being run, the script
+;; might want to use this to load it's other files.
+(define *script-directory* "")
+
+;; Parameters being passed to the script from the script's
+;; parameter options window.
+(define *params* '())
+
+;; Extra parameters passed as the third argument
+;; These are parameters that are not set via a parameter
+;; window and may be auxiliary variables dependent on the
+;; type of script (e.g. "content" contains an e3d_file tuple
+;; for exporter plugins, parameters set with "script_params"
+;; also show up in here).
+(define *extra-params* '())
+
+
+(define **scr-langstrs '())
+
+(define (set-lang-string List)
+ (set! **scr-langstrs
+ (map (lambda (A)
+ (cons (vector-ref A 0)
+ (vector-ref A 1)) )
+ List))
+ )
+
+
+(define (?__ Id Str)
+ (define Found (assq Id **scr-langstrs))
+ (if (eq? Found #f)
+ Str
+ (cdr Found))
+ )
+
+
+
+;; Long running process
+;;
+;; Example:
+;;
+;; (display "Started")(newline)
+;; (long-running-process
+;; (lambda ()
+;; (let loop ((N 100000000))
+;; (if (> N 0)
+;; (loop (- N 1))
+;; 'ok))
+;; ))
+;; (display "Done")(newline)
+;;
+
+(define (long-running-process Fun)
+ ;; A thread that simply sends keep-alive messages back to the scripting
+ ;; plugin to keep it from timing out while the script processes something
+ ;; that takes a while.
+ ;;
+ (define StandbyState #f)
+ (define StandbySema (make-semaphore 1))
+ (define StandbyThread
+ (make-thread (^[] (guard (e [else (report-error e) #f])
+ (define (get_val)
+ (semaphore-acquire! StandbySema)
+ (let ((Val (if StandbyState #t #f)))
+ (semaphore-release! StandbySema)
+ Val))
+ (let loop ()
+ (if (get_val)
+ 'done
+ (let ()
+ ;; The standard output seems to have some sort of buffering
+ ;; that prevents it from sending from this thread, current
+ ;; workaround is sending to standard error to bypass the buffering
+ (**send-back-list-ep (list '%keepalive 0))
+ (thread-sleep! 1)
+ (loop))))
+ ))))
+
+ (thread-start! StandbyThread)
+ (unwind-protect (Fun)
+ (semaphore-acquire! StandbySema)
+ (set! StandbyState #t)
+ (semaphore-release! StandbySema)
+ (thread-join! StandbyThread)
+ )
+ )
+
+
+(define (script-loop)
+ (define cmd (read))
+ (case (list-ref cmd 0)
+ ((run_init)
+ (begin
+ (set! *script-full-path* (list-ref cmd 1))
+ (set! *script-directory* (**loader-get-script-directory *script-full-path*))
+ (**add-to-load-path *script-directory*)
+ (set-lang-string (list-ref cmd 2))
+ (begin (load *script-full-path*))
+ (if (equal? #f *scr-main-fun*)
+ (begin
+ (display "ERROR: main function not set" (current-error-port))
+ (newline (current-error-port))
+ (exit))
+ (begin
+ (**send-back-list'(ok))
+ (script-loop))))
+ )
+ ((run)
+ (begin
+ (let ((params (list-ref cmd 2))
+ (extra-params (list-ref cmd 3)))
+ (define returned (*scr-main-fun* params extra-params))
+ (if (not (undefined? returned))
+ (begin
+ (newline)
+ (write returned)
+ )
+ (begin
+ )
+ )
+ )
+ (newline)
+ (**flush-out)
+ (**send-back-list '(%ok))
+ (script-loop))
+ )
+ (else
+ (begin
+ (display "Not run")
+ (newline)
+ (exit))))
+ )
+
+
+;; Used by the constructors for E3D objects
+;;
+(define (**loader-construct-from sc default names)
+ (define s-name (car default))
+ (define s-defaults (cdr default))
+ (cons s-name
+ (let loop ((sn names) (sd s-defaults))
+ (if (equal? '() sn)
+ '()
+ (let ((res (assq (car sn) sc)))
+ (if res
+ (cons (list-ref res 1) (loop (cdr sn) (cdr sd)))
+ (cons (car sd) (loop (cdr sn) (cdr sd))))))
+ )
+ )
+ )
+
+;; Useful functions for e3d records
+(define (e3d_transf? l) (equal? 'e3d_transf (list-ref l 0)))
+(define (e3d_transf-mat l) (list-ref l 1)) ;
+(define (e3d_transf-inv l) (list-ref l 2)) ;
+(define (make-e3d_transf . sc)
+ (define default (list 'e3d_transf '() '()))
+ (define names (list 'mat 'inv))
+ (**loader-construct-from sc default names)
+ )
+
+(define (ray? l) (equal? 'ray (list-ref l 0)))
+(define (ray-o l) (list-ref l 1)) ;
+(define (ray-d l) (list-ref l 2)) ;
+(define (ray-n l) (list-ref l 3)) ; Near, far (or MinT MaxT)
+(define (ray-f l) (list-ref l 4)) ;
+(define (ray-bfc l) (list-ref l 5)) ; Backface culling?
+(define (make-ray . sc)
+ (define default (list 'ray #(0 0) #(0 0) 0.0 1.0 #t))
+ (define names (list 'o 'd 'n 'f 'bfc))
+ (**loader-construct-from sc default names)
+ )
+
+(define (e3d_face? l) (equal? 'e3d_face (list-ref l 0)))
+(define (e3d_face-vs l) (list-ref l 1)) ; List of vertex indices.
+(define (e3d_face-vc l) (list-ref l 2)) ; Vertex color indices.
+(define (e3d_face-tx l) (list-ref l 3)) ; List of texture indices.
+(define (e3d_face-ns l) (list-ref l 4)) ; List of normal indices.
+(define (e3d_face-mat l) (list-ref l 5)) ; Materials for face.
+(define (e3d_face-sg l) (list-ref l 6)) ; Smooth group for face.
+(define (e3d_face-vis l) (list-ref l 7)) ; Visible edges (as in 3DS).
+(define (make-e3d_face . sc)
+ (define default (list 'e3d_face '() '() '() '() '() 1 -1))
+ (define names (list 'vs 'vc 'tx 'ns 'mat 'sg 'vis))
+ (define sc_1 (map
+ (lambda (b)
+ (if (eq? (car b) 'mat)
+ ;; Ensure that mat stays a list of atoms
+ (let ((matlist (list-ref b 1)))
+ (list (car b) (cons "!list" matlist)))
+ b
+ )
+ ) sc))
+ (**loader-construct-from sc_1 default names)
+ )
+
+(define (e3d_mesh? l) (equal? 'e3d_mesh (list-ref l 0)))
+(define (e3d_mesh-type l) (list-ref l 1)) ; 'triangle | 'quad | 'polygon
+(define (e3d_mesh-vs l) (list-ref l 2)) ; Vertex table (list).
+(define (e3d_mesh-vc l) (list-ref l 3)) ; Vertex color table (list).
+(define (e3d_mesh-tx l) (list-ref l 4)) ; Texture coordinates (list).
+(define (e3d_mesh-ns l) (list-ref l 5)) ; Normal table (list).
+(define (e3d_mesh-fs l) (list-ref l 6)) ; Face table (list of e3d_face).
+(define (e3d_mesh-he l) (list-ref l 7)) ; List of chains of hard edges.
+(define (e3d_mesh-matrix l) (list-ref l 8)) ; Local coordinate system.
+(define (make-e3d_mesh . sc)
+ (define default (list 'e3d_mesh 'poly '() '() '() '() '() '() 'identity))
+ (define names (list 'type 'vs 'vc 'tx 'ns 'fs 'he 'matrix))
+ (**loader-construct-from sc default names)
+ )
+
+(define (e3d_object? l) (equal? 'e3d_object (list-ref l 0)))
+(define (e3d_object-name l) (list-ref l 1)) ; Name of object (string)
+(define (e3d_object-obj l) (list-ref l 2)) ; Object implementation.
+(define (e3d_object-mat l) (list-ref l 3)) ; Materials for this object.
+(define (e3d_object-attr l) (list-ref l 4)) ; List of attributes.
+(define (make-e3d_object . sc)
+ (define default (list 'e3d_object 'undefined #f '() '()))
+ (define names (list 'name 'obj 'mat 'attr))
+ (**loader-construct-from sc default names)
+ )
+
+(define (e3d_file? l) (equal? 'e3d_file (list-ref l 0)))
+(define (e3d_file-objs l) (list-ref l 1)) ; List of objects.
+(define (e3d_file-mat l) (list-ref l 2)) ; List of materials.
+(define (e3d_file-creator l) (list-ref l 3)) ; Creator string.
+(define (e3d_file-dir l) (list-ref l 4)) ; Directory for file.
+(define (make-e3d_file . sc)
+ (define default (list 'e3d_file '() '() "" ""))
+ (define names (list 'objs 'mat 'creator 'dir))
+ (**loader-construct-from sc default names)
+ )
+; (e3d_file `(objs ,objs) `(mat ,mat))
+;
+
+
+(define (e3d_image? l) (equal? 'e3d_image (list-ref l 0)))
+(define (e3d_image-type l) (list-ref l 1))
+(define (e3d_image-bytes_pp l) (list-ref l 2))
+(define (e3d_image-alignment l) (list-ref l 3))
+(define (e3d_image-order l) (list-ref l 4))
+(define (e3d_image-width l) (list-ref l 5))
+(define (e3d_image-height l) (list-ref l 6))
+(define (e3d_image-image l) (list-ref l 7))
+(define (e3d_image-filename l) (list-ref l 8))
+(define (e3d_image-name l) (list-ref l 9))
+(define (e3d_image-extra l) (list-ref l 10))
+(define (make-e3d_image . sc)
+ (define default (list 'e3d_image 'r8g8b8 3 1 'lower_left 0 0 #f 'none '() '()))
+ (define names (list 'type 'bytes_pp 'alignment 'order 'width 'height 'image 'filename 'name 'extra))
+ (**loader-construct-from sc default names)
+ )
+
+
+(define (**send-back-list l)
+ (newline)
+ (write l)
+ (newline)
+ (**flush-out))
+
+(define (**send-back-list-ep l)
+ (newline (current-error-port))
+ (write l (current-error-port))
+ (newline (current-error-port))
+ (**flush-out))
+
+(define (**returns-reply)
+ (let ((reply (read)))
+ reply))
+
+(define (**loader-get-script-directory path)
+ ;; Assuming only R5RS available, we need a function
+ ;; to get the directory.
+ (let ((len (string-length path)))
+ (let ((chr (string-ref path (- len 1)))
+ (sub (substring path 0 (- len 1))))
+ (if (or (eq? chr #\\) (eq? chr #\/))
+ (substring path 0 len)
+ (**loader-get-script-directory sub)))))
+
+;;
+;; Returned replies from (**returns-reply) are always as a list, car
+;; is used if there is only one value.
+;;
+
+(define (wings-set-variable! varname varval)
+ (**send-back-list (list '%setvar varname varval))
+ (car (**returns-reply)))
+(define (wings-get-variable varname)
+ (**send-back-list (list '%getvar varname))
+ (car (**returns-reply)))
+(define (wings-query str)
+ (**send-back-list (list '%query str))
+ (car (**returns-reply)))
+(define (wings-we! . Args)
+ (**send-back-list (list '%we! Args))
+ (**returns-reply))
+(define (wings-we . Args)
+ (**send-back-list (list '%we Args))
+ (car (**returns-reply)))
+(define (wings-previous-we! . Args)
+ (**send-back-list (list '%we-previous! Args))
+ (**returns-reply))
+(define (wings-previous-we . Args)
+ (**send-back-list (list '%we-previous Args))
+ (car (**returns-reply)))
+;; TODO: wings-with-we-change Mod Name Args Fun
+(define (wings-pb-message . Args)
+ (define A1 (car Args))
+ (define A2 (cdr Args))
+ (cond
+ ((eq? A2 '())
+ (wings-pb-message-1 A1))
+ (#t
+ (wings-pb-message-2 A1 (car A2)))))
+
+(define (wings-pb-message-2 Prc Str)
+ (**send-back-list-ep (list '%pbmessage Prc Str)))
+
+(define (wings-pb-message-1 Str)
+ (**send-back-list-ep (list '%pbmessage 1.0 Str)))
+
+(define (wings-result-text List)
+ (if (string? List)
+ (wings-result-text (list List))
+ (**send-back-list (list '%resulttext List))
+ )
+ )
+
+
+;;
+;;
+
+
diff --git a/plugins_src/scripting/init_scripts/scm/init.2.scm b/plugins_src/scripting/init_scripts/scm/init.2.scm
new file mode 100644
index 000000000..9f57808e7
--- /dev/null
+++ b/plugins_src/scripting/init_scripts/scm/init.2.scm
@@ -0,0 +1,3 @@
+
+(script-loop)
+
diff --git a/plugins_src/scripting/init_scripts/scm/init_env_csi.scm b/plugins_src/scripting/init_scripts/scm/init_env_csi.scm
new file mode 100644
index 000000000..a21fdfb4d
--- /dev/null
+++ b/plugins_src/scripting/init_scripts/scm/init_env_csi.scm
@@ -0,0 +1,55 @@
+
+;;
+;; Scripting for Shapes (Scheme and Python)
+;;
+;; Copyright 2023 Edward Blake
+;;
+;; See the file "license.terms" for information on usage and redistribution
+;; of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+;;
+;; $Id$
+;;
+
+(define (relative_path_from_absolute Path1 Path2)
+ (define (split_components Path)
+ (define Path_1 (list->string (map (lambda (C) (if (eq? C #\\) #\/ C)) (string->list Path))))
+ (filter (lambda (Str) (not (equal? "" Str))) (string-split Path_1 "/")))
+ (define (detour P1 P2)
+ (if (not (pair? P2))
+ P1
+ (cons ".." (detour P1 (cdr P2))))
+ )
+ (define Path1L (split_components Path1))
+ (define Path2L (split_components Path2))
+ (if (not (equal? (car Path1L) (car Path2L)))
+ Path1
+ (let ()
+ (define PathList
+ (let loop ((Path1 Path1L) (Path2 Path2L))
+ (if (not (pair? Path2))
+ Path1
+ (if (equal? (car Path1) (car Path2))
+ (loop (cdr Path1) (cdr Path2))
+ (detour Path1 Path2)
+ )
+ )
+ ))
+ (string-join PathList "/"))
+ )
+ )
+
+(define (filename_dir Path1)
+ (define (split_components Path)
+ (define Path_1 (list->string (map (lambda (C) (if (eq? C #\\) #\/ C)) (string->list Path))))
+ (filter (lambda (Str) (not (equal? "" Str))) (string-split Path_1 "/")))
+ (define Path1L (split_components Path1))
+ (string-join (reverse (cdr (reverse Path1L))) "/")
+ )
+
+;; Chicken scheme requires a flush after every list write.
+(define (**flush-out)
+ (flush-output))
+
+(define (**add-to-load-path Path)
+ #f)
+
diff --git a/plugins_src/scripting/init_scripts/scm/init_env_gauche.scm b/plugins_src/scripting/init_scripts/scm/init_env_gauche.scm
new file mode 100644
index 000000000..99da10563
--- /dev/null
+++ b/plugins_src/scripting/init_scripts/scm/init_env_gauche.scm
@@ -0,0 +1,59 @@
+
+;;
+;; Scripting for Shapes (Scheme and Python)
+;;
+;; Copyright 2023-2025 Edward Blake
+;;
+;; See the file "license.terms" for information on usage and redistribution
+;; of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+;;
+;; $Id$
+;;
+
+(use gauche.threads)
+
+(define (relative_path_from_absolute Path1 Path2)
+ (define (split_components Path)
+ (define Path_1 (list->string (map (lambda (C) (if (eq? C #\\) #\/ C)) (string->list Path))))
+ (filter (lambda (Str) (not (equal? "" Str))) (string-split Path_1 "/")))
+ (define (detour P1 P2)
+ (if (not (pair? P2))
+ P1
+ (cons ".." (detour P1 (cdr P2))))
+ )
+ (define Path1L (split_components Path1))
+ (define Path2L (split_components Path2))
+ (if (not (equal? (car Path1L) (car Path2L)))
+ Path1
+ (let ()
+ (define PathList
+ (let loop ((Path1 Path1L) (Path2 Path2L))
+ (if (not (pair? Path2))
+ Path1
+ (if (equal? (car Path1) (car Path2))
+ (loop (cdr Path1) (cdr Path2))
+ (detour Path1 Path2)
+ )
+ )
+ ))
+ (string-join PathList "/"))
+ )
+ )
+
+(define (filename_dir Path1)
+ (define (split_components Path)
+ (define Path_1 (list->string (map (lambda (C) (if (eq? C #\\) #\/ C)) (string->list Path))))
+ (filter (lambda (Str) (not (equal? "" Str))) (string-split Path_1 "/")))
+ (define Path1L (split_components Path1))
+ (string-join (reverse (cdr (reverse Path1L))) "/")
+ )
+
+;; Gauche scheme does a flush automatically after newline
+(define (**flush-out)
+ (flush-all-ports))
+
+(define (**add-to-load-path p)
+ ;; We only need run-time modification of the load-path variable
+ (set! *load-path* (cons p *load-path*))
+ )
+
diff --git a/plugins_src/scripting/resource/README b/plugins_src/scripting/resource/README
new file mode 100644
index 000000000..07f8cddca
--- /dev/null
+++ b/plugins_src/scripting/resource/README
@@ -0,0 +1,11 @@
+After updating the xbm bitmap, some programs include the entire absolute path
+inside of the xbm file, which breaks the xbm file. The beginning of the xbm
+file should look like this:
+
+#define wpc_shape_from_scripts_btn_refresh_width 32
+#define wpc_shape_from_scripts_btn_refresh_height 32
+static unsigned char wpc_shape_from_scripts_btn_refresh_bits[] = {
+
+Use xbm_to_hrl.escript to update the hrl from the xbm.
+
+
diff --git a/plugins_src/scripting/resource/wpc_scripting_shapes_btn_refresh.hrl b/plugins_src/scripting/resource/wpc_scripting_shapes_btn_refresh.hrl
new file mode 100644
index 000000000..ae0269e37
--- /dev/null
+++ b/plugins_src/scripting/resource/wpc_scripting_shapes_btn_refresh.hrl
@@ -0,0 +1,4 @@
+%% This file is generated from the .xbm file.
+-define(WPC_SHAPE_FROM_SCRIPTS_BTN_REFRESH_WIDTH, 32).
+-define(WPC_SHAPE_FROM_SCRIPTS_BTN_REFRESH_HEIGHT, 32).
+-define(WPC_SHAPE_FROM_SCRIPTS_BTN_REFRESH_BITS, <<16#00,16#00,16#00,16#00,16#00,16#00,16#00,16#00,16#00,16#00,16#00,16#00,16#00,16#40,16#00,16#00,16#00,16#C0,16#00,16#00,16#00,16#C0,16#01,16#00,16#00,16#FF,16#F3,16#00,16#80,16#FF,16#F7,16#01,16#C0,16#FF,16#F3,16#03,16#C0,16#C3,16#C1,16#03,16#C0,16#C1,16#80,16#03,16#C0,16#41,16#80,16#03,16#C0,16#01,16#80,16#03,16#C0,16#01,16#80,16#03,16#C0,16#01,16#80,16#03,16#C0,16#01,16#80,16#03,16#C0,16#01,16#80,16#03,16#C0,16#01,16#80,16#03,16#C0,16#01,16#80,16#03,16#C0,16#01,16#80,16#03,16#C0,16#01,16#81,16#03,16#C0,16#81,16#81,16#03,16#C0,16#C3,16#C1,16#03,16#C0,16#E7,16#FF,16#03,16#80,16#F7,16#FF,16#01,16#00,16#E7,16#FF,16#00,16#00,16#C0,16#01,16#00,16#00,16#80,16#01,16#00,16#00,16#00,16#01,16#00,16#00,16#00,16#00,16#00,16#00,16#00,16#00,16#00,16#00,16#00,16#00,16#00>>).
diff --git a/plugins_src/scripting/resource/wpc_scripting_shapes_btn_refresh.xbm b/plugins_src/scripting/resource/wpc_scripting_shapes_btn_refresh.xbm
new file mode 100644
index 000000000..c4c7cd383
--- /dev/null
+++ b/plugins_src/scripting/resource/wpc_scripting_shapes_btn_refresh.xbm
@@ -0,0 +1,14 @@
+#define wpc_shape_from_scripts_btn_refresh_width 32
+#define wpc_shape_from_scripts_btn_refresh_height 32
+static unsigned char wpc_shape_from_scripts_btn_refresh_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x40, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0x01, 0x00,
+ 0x00, 0xFF, 0xF3, 0x00, 0x80, 0xFF, 0xF7, 0x01, 0xC0, 0xFF, 0xF3, 0x03,
+ 0xC0, 0xC3, 0xC1, 0x03, 0xC0, 0xC1, 0x80, 0x03, 0xC0, 0x41, 0x80, 0x03,
+ 0xC0, 0x01, 0x80, 0x03, 0xC0, 0x01, 0x80, 0x03, 0xC0, 0x01, 0x80, 0x03,
+ 0xC0, 0x01, 0x80, 0x03, 0xC0, 0x01, 0x80, 0x03, 0xC0, 0x01, 0x80, 0x03,
+ 0xC0, 0x01, 0x80, 0x03, 0xC0, 0x01, 0x80, 0x03, 0xC0, 0x01, 0x81, 0x03,
+ 0xC0, 0x81, 0x81, 0x03, 0xC0, 0xC3, 0xC1, 0x03, 0xC0, 0xE7, 0xFF, 0x03,
+ 0x80, 0xF7, 0xFF, 0x01, 0x00, 0xE7, 0xFF, 0x00, 0x00, 0xC0, 0x01, 0x00,
+ 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
diff --git a/plugins_src/scripting/scripting_engines.erl b/plugins_src/scripting/scripting_engines.erl
new file mode 100644
index 000000000..44984104e
--- /dev/null
+++ b/plugins_src/scripting/scripting_engines.erl
@@ -0,0 +1,238 @@
+%%
+%% Scripting for Shapes Engines
+%%
+%% Read script engine config files and set up the arguments for when
+%% a script interpreter is invoked.
+%%
+%% Copyright 2024-2025 Edward Blake
+%%
+%% See the file "license.terms" for information on usage and redistribution
+%% of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+%%
+%% $Id$
+%%
+
+-module(scripting_engines).
+
+-export([init/1, all_engines/0, script_interp/3, extra_script_dirs/0]).
+
+-include_lib("kernel/include/file.hrl").
+
+init(DirName) ->
+ InitDir = filename:absname(code:where_is_file(DirName)),
+ catch ets:new(script_eng, [public,ordered_set,named_table]),
+ find_engines(InitDir),
+ find_script_dirs(InitDir).
+
+all_engines() ->
+ [{all, AllEng}] = ets:lookup(script_eng, all),
+ AllEng.
+
+script_interp(Type, Settings, InitScriptsDir) ->
+ case init_file(Type) of
+ false ->
+ Interpreter = none,
+ Arguments = [],
+ InitFile = "";
+ InitFile0 when is_list(InitFile0) ->
+ Atom1 = list_to_atom("setting_" ++ Type ++ "_int_path"),
+ Atom2 = list_to_atom("setting_" ++ Type ++ "_arguments"),
+ Interpreter = auto_fill_int_path(Type,
+ proplists:get_value(Atom1, Settings, "")),
+ Arguments = auto_fill_arguments(Type, Interpreter,
+ string:split(proplists:get_value(Atom2, Settings, ""), " ", all),
+ InitScriptsDir),
+ InitFile = filename:join(InitScriptsDir, InitFile0)
+ end,
+ {Interpreter, Arguments, InitFile}.
+
+auto_fill_int_path(Type, Str) ->
+ Atom = list_to_atom("setting_autointrp_" ++ Type),
+ auto_fill_int_path(Type, Str, Atom, int_list(Type)).
+auto_fill_int_path(_, Str, Atom, List) ->
+ case Str of
+ "" ->
+ find_first_interpreter(Atom, List);
+ _ ->
+ Str
+ end.
+
+auto_fill_arguments(Type, Interpreter, Extra, BaseDir) ->
+ Interpreter0 = filename:basename(filename:rootname(Interpreter)),
+ Extra_1 = auto_fill_arguments_no_empty(Extra),
+ case get_auto_fill(Type, Interpreter0) of
+ false -> Extra_1;
+ Args ->
+ [ expand_arg(A, BaseDir) || A <- Args]
+ ++ Extra_1
+ end.
+
+expand_arg("%BASEDIR%" ++ Path, BaseDir) ->
+ case Path of
+ [] ->
+ BaseDir;
+ [C|Path1] when C =:= $/; C =:= $\\ ->
+ filename:join(BaseDir,Path1)
+ end;
+expand_arg(A, _) when is_list(A) -> A;
+expand_arg({wrap, A, B}, BaseDir) when is_list(A) ->
+ lists:flatten(string:replace(A, "%s", expand_arg(B, BaseDir), all));
+expand_arg({concat, A, B}, BaseDir) ->
+ expand_arg(A, BaseDir) ++ expand_arg(B, BaseDir);
+expand_arg({esc, A}, BaseDir) ->
+ esc(expand_arg(A, BaseDir));
+expand_arg({absname, A, B}, BaseDir) ->
+ filename:absname(expand_arg(A, BaseDir), expand_arg(B, BaseDir)).
+
+esc(File) ->
+ File_1 = lists:flatten(string:replace(File, "\\", "\\\\", all)),
+ lists:flatten(string:replace(File_1, "\"", "\\\"", all)).
+
+auto_fill_arguments_no_empty(Extra) ->
+ case Extra of
+ [[]] ->
+ [];
+ _ ->
+ Extra
+ end.
+
+find_first_interpreter(_AutoSetting, List) ->
+ Found = find_first_interpreter_1(List),
+ Found.
+find_first_interpreter_1([TryCmd | List]) ->
+ case os:find_executable(TryCmd) of
+ Found when is_list(Found) -> TryCmd;
+ false ->
+ find_first_interpreter_1(List)
+ end;
+find_first_interpreter_1([]) ->
+ "".
+
+%%
+%%
+
+find_engines(Path) ->
+ case file:list_dir(Path) of
+ {ok, Filenames} ->
+ Found =
+ [ read_eng_conf(Path,AName)
+ || AName <- Filenames],
+ Found_1 = [A || A <- Found, A =/= false ],
+ ets:insert(script_eng, {all, Found_1}),
+ ok;
+ _ ->
+ false
+ end.
+
+read_eng_conf(Path,Name) ->
+ case filename:extension(Name) of
+ ".script-init-conf" ->
+ case file:consult(filename:join(Path,Name)) of
+ {ok, [Tuple|_]} ->
+ {A,B,C,D,E} = Tuple,
+ engine_conf(A,B,C,D,E),
+ {A,B}
+ end;
+ _ -> false
+ end.
+
+engine_conf(Type, _HasArgs, InitFile, Commands, Extra) ->
+ ets:insert(script_eng, {{init_file,Type},InitFile}),
+ ets:insert(script_eng, {{int_list,Type}, Commands}),
+
+ [ engine_conf_auto_fill(Type,Cmd,Args)
+ || {Cmd,Args} <- proplists:get_value(auto_fill, Extra, []) ].
+
+engine_conf_auto_fill(Type,Cmd,Args) ->
+ ets:insert(script_eng, {{auto_fill,{Type,Cmd}}, Args}).
+
+init_file(Type) ->
+ case ets:lookup(script_eng, {init_file, Type}) of
+ [{_,InitFile}] -> InitFile;
+ _ -> false
+ end.
+
+int_list(Type) ->
+ case ets:lookup(script_eng, {int_list, Type}) of
+ [{_,IntList}] -> IntList;
+ _ -> false
+ end.
+
+get_auto_fill(Type,Cmd) ->
+ case ets:lookup(script_eng, {auto_fill, {Type,Cmd}}) of
+ [{_,Args}] -> Args;
+ _ -> false
+ end.
+
+%%
+%%
+
+find_script_dirs(InitDir) ->
+ [_|Dir0] = lists:reverse(filename:split(InitDir)),
+ Dir = filename:join(lists:reverse(Dir0)),
+
+ ScriptDir = filename:join(Dir, "scripts"),
+ case file:read_file_info(ScriptDir) of
+ {ok,#file_info{type=directory}=_} ->
+ ets:insert(script_eng, {scriptdir1, [ScriptDir]});
+ _ ->
+ ok
+ end,
+ ScriptPaths = filename:join(InitDir, "paths"),
+ case file:list_dir(ScriptPaths) of
+ {ok,List} ->
+ Paths_0 =
+ [read_path_file(filename:join(ScriptPaths, File))
+ || File <- List],
+ Paths = lists:append(Paths_0),
+ ets:insert(script_eng, {scriptdirs, Paths});
+ _ ->
+ ok
+ end.
+
+read_path_file(File) ->
+ case filename:extension(File) of
+ ".list" ->
+ case file:read_file(File) of
+ {ok, List0} ->
+ List = [read_path_file_1(A)
+ || A <- string:split(binary_to_list(List0), "\n", all)],
+ [A || A <- List, A =/= false, A =/= ""];
+ _ -> []
+ end;
+ _ -> []
+ end.
+
+read_path_file_1("#" ++ _) -> false;
+read_path_file_1(File) ->
+ Path = string:trim(File),
+ case Path of
+ "" -> false;
+ "/" ++ _ ->
+ read_path_file_2(Path);
+ "~/" ++ Path_1 ->
+ read_path_file_2(filename:join(os:getenv("HOME"), Path_1));
+ _ -> false
+ end.
+
+read_path_file_2(File) ->
+ case file:read_file_info(File) of
+ {ok,#file_info{type=directory}=_} ->
+ File;
+ _ ->
+ false
+ end.
+
+%%
+%%
+
+%% Get extra script directories apart from user supplied ones.
+%%
+extra_script_dirs() ->
+ lists:append([
+ case ets:lookup(script_eng, Atom) of
+ [{_,List}] -> List;
+ _ -> []
+ end
+ || Atom <- [scriptdir1, scriptdirs]]).
+
diff --git a/plugins_src/scripting/tools/gen-funs-doc.awk b/plugins_src/scripting/tools/gen-funs-doc.awk
new file mode 100644
index 000000000..85df79794
--- /dev/null
+++ b/plugins_src/scripting/tools/gen-funs-doc.awk
@@ -0,0 +1,211 @@
+
+##
+## This generates the function documentation (mainly the we
+## functions) for the Python3 and Scheme scripting manuals.
+##
+## No extensions are used so this should work with most versions
+## of awk.
+##
+## Copyright 2025 Edward Blake
+##
+## See the file "license.terms" for information on usage and redistribution
+## of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+##
+## $Id$
+##
+
+
+
+function ts (s) {
+ gsub("^[ ]+|[ ]+$","",s)
+ return s
+}
+
+function snk (s) {
+ ## Make snake case
+ v = ""
+ l = split(s, chrs, "");
+ for (i2 = 1; i2 <= l; i2++) {
+ c2 = chrs[i2];
+ if (match(c2, "[A-Z]")) {
+ if (i2 > 1) {
+ v = v "_";
+ }
+ v = v tolower(c2);
+ } else {
+ v = v c2;
+ }
+ }
+ return v
+}
+
+## Print an argument formatted for either Scheme or Python
+##
+function print_args (s) {
+ c = split(s, args, " +");
+ if (type == "scm") {
+ for (i = 1; i <= c; i ++) {
+ if (i > 1) {
+ printf " ";
+ }
+ printf "%s", args[i];
+ }
+ }
+ if (type == "py") {
+ for (i = 1; i <= c; i ++) {
+ if (i > 1) {
+ printf ", ";
+ }
+ printf "%s", snk(args[i]);
+ }
+ }
+}
+
+## Substitute one placeholder
+##
+function note_arg (s) {
+ if (type == "scm") {
+ if (match(s, "^TUPLE ")) {
+ cont = substr(s, RLENGTH+1);
+ return "#(" cont ")";
+ }
+ if (match(s, "^ARG ")) {
+ cont = substr(s, RLENGTH+1);
+ return cont;
+ }
+ if (match(s, "^ATOM ")) {
+ cont = substr(s, RLENGTH+1);
+ return "'" cont;
+ }
+ }
+ if (type == "py") {
+ if (match(s, "^TUPLE ")) {
+ cont = substr(s, RLENGTH+1);
+ cont2 = "";
+ c = split(cont, conta, " +");
+ for (i = 1; i <= c; i++) {
+ if (i > 1) {
+ cont2 = cont2 ", ";
+ }
+ cont2 = cont2 snk(conta[i])
+ }
+ return "(" cont2 ")";
+ }
+ if (match(s, "^ARG ")) {
+ cont = substr(s, RLENGTH+1);
+ return snk(cont);
+ }
+ if (match(s, "^ATOM ")) {
+ cont = substr(s, RLENGTH+1);
+ return "'" cont "'";
+ }
+ }
+
+ return "??";
+}
+
+## Parse out the text placeholders and substitute them
+## with formatted text (for tuples, atoms, etc).
+function notes2 (s) {
+ str2 = ""
+ while (match(s, "\\$\\(")) {
+ str2 = str2 substr(s, 0, RSTART-1);
+ s = substr(s, RSTART+RLENGTH);
+ until = index(s, ")");
+ str2 = str2 note_arg(substr(s, 0, until-1));
+ s = substr(s, until+1);
+ }
+ str2 = str2 s;
+ return str2;
+}
+
+function notes (s) {
+ return notes2(s);
+}
+
+BEGIN {
+ FS = "\\|"
+ funname0 = ""
+}
+
+## Some notes that should be added to the generated text.
+NF == 2 {
+ if ($1 == "") {
+ note = ts($2);
+ print notes(note);
+ }
+}
+
+## A function definition
+NF > 5 {
+ section = ts($1);
+ module = ts($2);
+ funname = ts($3);
+ changeswe = ts($4);
+ reqargs = ts($5)
+ optargs = ts($6)
+ returns = ts($7)
+
+ ## When it is a different function name, add a line break
+ if (funname0 != funname) {
+ printf "\n";
+ funname0 = funname;
+ }
+
+ ## The documentation for Scheme scripting
+ if (type == "scm") {
+ printf "(";
+ if (section != "") {
+ printf "%s:", section;
+ }
+ printf "%s:%s%s", module, funname, changeswe;
+ if ((reqargs != "") || (optargs != "")) {
+ printf "\n ";
+ if (reqargs != "") {
+ print_args(reqargs);
+ if (optargs != "") {
+ printf "\n ";
+ }
+ }
+ if (optargs != "") {
+ printf "optional ";
+ print_args(optargs);
+ }
+ }
+ printf ")\n";
+ }
+
+ ## The documentation for Python3 scripting
+ if (type == "py") {
+ printf "%s__%s(", module, funname;
+ if (reqargs != "") {
+ print_args(reqargs);
+ }
+ if (optargs != "") {
+ if (reqargs != "") {
+ printf " ";
+ }
+ printf "[";
+ if (reqargs != "") {
+ printf ", ";
+ }
+ print_args(optargs);
+ printf "]";
+ }
+ printf ")\n";
+ if (changeswe == "!") {
+ printf "! Changes #we{}\n";
+ }
+ }
+
+ if (returns != "") {
+ if (returns != "_") {
+ printf "returns: %s\n", notes(returns);
+ }
+ }
+}
+END {
+}
+
+
+
diff --git a/plugins_src/scripting/tools/gen-init-we-script.sh b/plugins_src/scripting/tools/gen-init-we-script.sh
new file mode 100755
index 000000000..6f19b71e1
--- /dev/null
+++ b/plugins_src/scripting/tools/gen-init-we-script.sh
@@ -0,0 +1,51 @@
+#!/bin/sh
+
+##
+## Generates the init.scm for scheme, or the w3d_we.py file for python.
+##
+## gen-init-we-script.sh