aboutsummaryrefslogtreecommitdiff
path: root/project/src/lib-transform.c
diff options
context:
space:
mode:
Diffstat (limited to 'project/src/lib-transform.c')
-rw-r--r--project/src/lib-transform.c1391
1 files changed, 1391 insertions, 0 deletions
diff --git a/project/src/lib-transform.c b/project/src/lib-transform.c
new file mode 100644
index 0000000..e463621
--- /dev/null
+++ b/project/src/lib-transform.c
@@ -0,0 +1,1391 @@
+
+/***************************************************************************
+ * _______ _______ _______ *
+ * | ___ |____ | ___ | *
+ * | | |_| / /| | |_| *
+ * | |_____ / / | |_____ *
+ * |_____ | / / |_____ | *
+ * _ | | / / _ | | *
+ * | |___| |/ /____| |___| | *
+ * |_______|_______|_______| *
+ * *
+ * Wiimms SZS Tools *
+ * https://szs.wiimm.de/ *
+ * *
+ ***************************************************************************
+ * *
+ * This file is part of the SZS project. *
+ * Visit https://szs.wiimm.de/ for project details and sources. *
+ * *
+ * Copyright (c) 2011-2020 by Dirk Clemens <wiimm@wiimm.de> *
+ * *
+ ***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt *
+ * *
+ ***************************************************************************/
+
+#include "lib-std.h"
+#include <math.h>
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// options ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+u32 set_flags = M1(u32);
+bool have_set_scale = false;
+bool have_set_rot = false;
+DEFINE_VAR(set_scale);
+DEFINE_VAR(set_rot);
+
+uint have_set_value = 0; // x=bit-0, y=bit-1, z=bit-2
+double3 set_value_min, set_value_max;
+
+///////////////////////////////////////////////////////////////////////////////
+
+int ScanOptSetFlags ( ccp arg )
+{
+ return ScanSizeOptU32(
+ &set_flags, // u32 * num
+ arg, // ccp source
+ 1, // default_factor1
+ 0, // int force_base
+ "set-flag", // ccp opt_name
+ 0, // u64 min
+ 0xffff, // u64 max
+ 0, // u32 multiple
+ 0, // u32 pow2
+ true // bool print_err
+ ) != ERR_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int ScanOptSetScale ( ccp arg )
+{
+ if (!arg)
+ return 0;
+
+ ScanInfo_t si;
+ InitializeSI(&si,arg,strlen(arg),"Option --set-scale",0);
+ enumError err = ScanVectorExprSI(&si,&set_scale,1.0,1);
+ DASSERT( set_scale.mode == VAR_VECTOR );
+ have_set_scale = !err && IsNormalD3(set_scale.v);
+ if ( err != ERR_OK )
+ err = CheckEolSI(&si);
+ ResetSI(&si);
+
+ return err != ERR_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int ScanOptSetRot ( ccp arg )
+{
+ if (!arg)
+ return 0;
+
+ ScanInfo_t si;
+ InitializeSI(&si,arg,strlen(arg),"Option --set-rot",0);
+ enumError err = ScanVectorExprSI(&si,&set_rot,0.0,1);
+ DASSERT( set_rot.mode == VAR_VECTOR );
+ have_set_rot = !err && IsNormalD3(set_rot.v);
+ if ( err != ERR_OK )
+ err = CheckEolSI(&si);
+ ResetSI(&si);
+
+ return err != ERR_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int ScanOptSet ( uint xyz, ccp arg )
+{
+ DASSERT( xyz < 3 );
+ if ( !arg || xyz >= 3 )
+ return 0;
+
+ char optname[10];
+ snprintf(optname,sizeof(optname),"%cset",'x'+xyz);
+
+ ScanInfo_t si;
+ InitializeSI(&si,arg,strlen(arg),optname,0);
+ DEFINE_VAR(min);
+ DEFINE_VAR(max);
+
+ const enumError err = ScanMinMaxExprSI(&si,&min,&max);
+ CheckEolSI(&si);
+ ResetSI(&si);
+ if (err)
+ return 1;
+
+ set_value_min.v[xyz] = min.v[xyz];
+ set_value_max.v[xyz] = max.v[xyz];
+ have_set_value |= 1 << xyz;
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+int ScanOptShift ( ccp arg )
+{
+ if (!arg)
+ return 0;
+
+ ScanInfo_t si;
+ InitializeSI(&si,arg,strlen(arg),"Option --shift",0);
+
+ DEFINE_VAR(temp);
+ const enumError err = ScanVectorExprSI(&si,&temp,0.0,1);
+ DASSERT( temp.mode == VAR_VECTOR );
+ CheckEolSI(&si);
+ ResetSI(&si);
+
+ if (!err)
+ SetShiftMatrixD(&opt_transform,&temp.d3);
+ return err != ERR_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int ScanOptTranslate ( ccp arg )
+{
+ if (!arg)
+ return 0;
+
+ ScanInfo_t si;
+ InitializeSI(&si,arg,strlen(arg),"Option --translate",0);
+
+ DEFINE_VAR(temp);
+ const enumError err = ScanVectorExprSI(&si,&temp,0.0,1);
+ DASSERT( temp.mode == VAR_VECTOR );
+ CheckEolSI(&si);
+ ResetSI(&si);
+
+ if (!err)
+ SetTranslateMatrixD(&opt_transform,&temp.d3);
+ return err != ERR_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int ScanOptScale ( ccp arg )
+{
+ if (!arg)
+ return 0;
+
+ ScanInfo_t si;
+ InitializeSI(&si,arg,strlen(arg),"Option --scale",0);
+ ScanFile_t *sf = si.cur_file;
+ DASSERT(sf);
+
+ DEFINE_VAR(scale);
+ DEFINE_VAR(origin);
+ enumError err = ScanVectorExprSI(&si,&scale,1.0,1);
+ DASSERT( scale.mode == VAR_VECTOR );
+ if ( !err && NextCharSI(&si,false) == '@' )
+ {
+ sf->ptr++;
+ err = ScanVectorExprSI(&si,&origin,0.0,1);
+ DASSERT( origin.mode == VAR_VECTOR );
+ }
+ else
+ {
+ origin.x = origin.y = origin.z = 0.0;
+ origin.mode = VAR_VECTOR;
+ }
+
+ CheckEolSI(&si);
+ ResetSI(&si);
+
+ if (!err)
+ SetScaleMatrixD(&opt_transform,&scale.d3,&origin.d3);
+
+ return err != ERR_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int ScanOptRotate ( ccp arg )
+{
+ if (!arg)
+ return 0;
+
+ ScanInfo_t si;
+ InitializeSI(&si,arg,strlen(arg),"Option --rot",0);
+ ScanFile_t *sf = si.cur_file;
+ DASSERT(sf);
+
+ DEFINE_VAR(rotate);
+ DEFINE_VAR(origin);
+ enumError err = ScanVectorExprSI(&si,&rotate,1.0,1);
+ DASSERT( rotate.mode == VAR_VECTOR );
+ if ( !err && NextCharSI(&si,false) == '@' )
+ {
+ sf->ptr++;
+ err = ScanVectorExprSI(&si,&origin,0.0,1);
+ DASSERT( origin.mode == VAR_VECTOR );
+ }
+ else
+ {
+ origin.x = origin.y = origin.z = 0.0;
+ origin.mode = VAR_VECTOR;
+ }
+
+ CheckEolSI(&si);
+ ResetSI(&si);
+
+ if (!err)
+ {
+ SetRotateMatrixD(&opt_transform,0,rotate.x,0.0,&origin.d3);
+ SetRotateMatrixD(&opt_transform,1,rotate.y,0.0,&origin.d3);
+ SetRotateMatrixD(&opt_transform,2,rotate.z,0.0,&origin.d3);
+ }
+
+ return err != ERR_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int ScanOptXRotate ( uint xyz, ccp arg )
+{
+ DASSERT(xyz<3);
+ if ( !arg || xyz >= 3 )
+ return 0;
+
+ static ccp tab_info[] = { "Option --xrot", "Option --yrot", "Option --zrot" };
+
+ ScanInfo_t si;
+ InitializeSI(&si,arg,strlen(arg),tab_info[xyz],0);
+ ScanFile_t *sf = si.cur_file;
+ DASSERT(sf);
+
+ DEFINE_VAR(temp);
+ enumError err = ScanExprSI(&si,&temp);
+ double degree = GetDoubleV(&temp);
+
+ if ( !err && NextCharSI(&si,false) == '@' )
+ {
+ sf->ptr++;
+ err = ScanVectorExprSI(&si,&temp,0.0,xyz);
+ DASSERT( temp.mode == VAR_VECTOR );
+ }
+ else
+ {
+ temp.x = temp.y = temp.z = 0.0;
+ temp.mode = VAR_VECTOR;
+ }
+ CheckEolSI(&si);
+ ResetSI(&si);
+
+ if (!err)
+ SetRotateMatrixD(&opt_transform,xyz,degree,0.0,&temp.d3);
+
+ return err != ERR_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int ScanOptAScale ( ccp arg )
+{
+ if (!arg)
+ return 0;
+
+ ScanInfo_t si;
+ InitializeSI(&si,arg,strlen(arg),"Option --ascale",0);
+ ScanFile_t *sf = si.cur_file;
+ DASSERT(sf);
+
+ DEFINE_VAR(temp);
+ enumError err = ScanExprSI(&si,&temp);
+ if (err)
+ goto abort;
+ const double scale = GetDoubleV(&temp);
+ if ( fabs(scale) < NULL_EPSILON )
+ {
+ err = ERROR0(ERR_SEMANTIC,
+ "Option --ascale: Scale factor too small: %s\n",arg);
+ goto abort;
+ }
+
+ err = CheckWarnSI(&si,'@',err);
+ if (err)
+ goto abort;
+ ccp arg_vector =sf->ptr;
+
+ DEFINE_VAR(dir);
+ err = ScanVectorExprSI(&si,&dir,0.0,1);
+ if (err)
+ goto abort;
+ DASSERT( dir.mode == VAR_VECTOR );
+
+ const double dir_len = Length3(dir);
+ if ( dir_len < NULL_EPSILON )
+ {
+ err = ERROR0(ERR_SEMANTIC,
+ "Option --ascale: Vector is too small: %s\n",arg_vector);
+ goto abort;
+ }
+ err = CheckEolSI(&si);
+ if (err)
+ goto abort;
+
+ //--- valid axis scale entered
+
+ ascale_valid = fabs(scale) >= NULL_EPSILON && fabs(scale-10.0) >= NULL_EPSILON;
+ ascale = scale;
+ Div3f(ascale_dir,dir,dir_len);
+ NextTransformation(false);
+
+ abort:
+ ResetSI(&si);
+ return err != ERR_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int ScanOptARotate ( ccp arg )
+{
+ if (!arg)
+ return 0;
+
+ ScanInfo_t si;
+ InitializeSI(&si,arg,strlen(arg),"Option --arot",0);
+ ScanFile_t *sf = si.cur_file;
+ DASSERT(sf);
+
+ DEFINE_VAR(temp);
+ enumError err = ScanExprSI(&si,&temp);
+ if (err)
+ goto abort;
+ const double degree = fmod(GetDoubleV(&temp)+180.0,360.0)-180.0;
+
+ err = CheckWarnSI(&si,'@',err);
+ if (err)
+ goto abort;
+ ccp arg_vector =sf->ptr;
+
+ DEFINE_VAR(p1);
+ DEFINE_VAR(p2);
+ err = ScanVectorExprSI(&si,&p2,0.0,1);
+ if (err)
+ goto abort;
+ DASSERT( p2.mode == VAR_VECTOR );
+
+ if ( NextCharSI(&si,false) == '@' )
+ {
+ p1 = p2;
+ sf->ptr++;
+ err = ScanVectorExprSI(&si,&p2,0.0,1);
+ if (err)
+ goto abort;
+ DASSERT( p2.mode == VAR_VECTOR );
+ }
+ else
+ p1.x = p1.y = p1.z = 0.0;
+
+ double3 delta;
+ Sub3(delta,p1,p2);
+ const double len = Length3(delta);
+ if ( len < NULL_EPSILON )
+ {
+ err = ERROR0(ERR_SEMANTIC,
+ "Option --arot: Points to near: %s\n",arg_vector);
+ goto abort;
+ }
+ err = CheckEolSI(&si);
+ if (err)
+ goto abort;
+
+ //--- valid axis rotation entered
+
+ arot_valid = true; //fabs(degree) >= MIN_DEGREE;
+ arot_deg = degree;
+ arot1 = p1.d3;
+ arot2 = p2.d3;
+ NextTransformation(false);
+
+ abort:
+ ResetSI(&si);
+ return err != ERR_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int ScanOptYPos ( ccp arg )
+{
+ if (!arg)
+ return 0;
+
+ ScanInfo_t si;
+ InitializeSI(&si,arg,strlen(arg),"Options --ypos",0);
+
+ DEFINE_VAR(temp);
+ enumError err = ScanExprSI(&si,&temp);
+ yposition_valid = temp.mode != VAR_UNSET;
+ yposition = GetYDoubleV(&temp);
+ CheckEolSI(&si);
+ ResetSI(&si);
+ return err != ERR_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+int ScanOptXSS ( uint xyz, ccp arg )
+{
+ DASSERT( xyz < 3 );
+ if ( !arg || xyz >= 3 )
+ return 0;
+
+ static ccp tab_info[] = { "Option --xss", "Option --yss", "Option --zss" };
+ ccp opt_info = tab_info[xyz];
+
+ ScanInfo_t si;
+ InitializeSI(&si,arg,strlen(arg),opt_info,0);
+ ScanFile_t *sf = si.cur_file;
+ DASSERT(sf);
+ sf->disable_comma++;
+
+ DEFINE_VAR(temp);
+ enumError err = ScanExprSI(&si,&temp);
+ err = CheckWarnSI(&si,',',err);
+ if (err)
+ goto abort;
+ double old1 = GetDoubleV(&temp);
+
+ err = ScanExprSI(&si,&temp);
+ err = CheckWarnSI(&si,',',err);
+ if (err)
+ goto abort;
+ double new1 = GetDoubleV(&temp);
+
+ err = ScanExprSI(&si,&temp);
+ err = CheckWarnSI(&si,',',err);
+ if (err)
+ goto abort;
+ double old2 = GetDoubleV(&temp);
+
+ err = ScanExprSI(&si,&temp);
+ if (err)
+ goto abort;
+ double new2 = GetDoubleV(&temp);
+
+ CheckEolSI(&si);
+
+ if ( fabs(old1-old2) < 1e-6 )
+ {
+ err = ERROR0(ERR_SEMANTIC,
+ "%s: 'old' values are to close together, delta=%6.3e",
+ opt_info, fabs(old1-old2) );
+ goto abort;
+ }
+
+ SetScaleShiftMatrixD(&opt_transform,xyz,old1,new1,old2,new2);
+
+ abort:
+ ResetSI(&si);
+ return err != ERR_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/////////////// Transformation Script ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static ccp tfs_filename = 0;
+static u8 *tfs_data = 0;
+static uint tfs_size = 0;
+static ScanInfo_t tfs_si = {0};
+static ScanMacro_t *tfs_macro = 0;
+static uint tfs_index = 0;
+
+#if HAVE_PRINT
+ static uint tfs_count_f = 0;
+ static uint tfs_count_d = 0;
+#endif
+
+bool tform_script_enabled = false;
+
+///////////////////////////////////////////////////////////////////////////////
+
+int ScanOptTformScript ( ccp arg )
+{
+ tfs_filename = arg;
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static enumError ScanTformScript()
+{
+ tfs_macro = 0;
+ tform_script_enabled = false;
+
+ if ( !tfs_filename || !*tfs_filename )
+ return ERR_NOTHING_TO_DO;
+
+ enumError err = OpenReadFILE(tfs_filename,0,false,&tfs_data,&tfs_size,0,0);
+ if ( err || !tfs_data || !tfs_size )
+ {
+ abort:
+ FREE(tfs_data);
+ tfs_data = 0;
+ tfs_size = 0;
+ tfs_filename = 0;
+ return err;
+ }
+
+ InitializeSI(&tfs_si,(ccp)tfs_data,tfs_size,tfs_filename,REVISION_NUM);
+ while (NextLineSI(&tfs_si,false,true))
+ ; //--- scan source
+
+ const Var_t *vp = FindVarMap(&tfs_si.macro,"TRANSFORM",0);
+ if ( !vp || !vp->macro )
+ {
+ ResetSI(&tfs_si);
+ err = ERR_NOTHING_TO_DO;
+ goto abort;
+ }
+
+ tfs_macro = vp->macro;
+ PRINT("MACRO TRANSFORM found\n");
+ tform_script_enabled = true;
+ transform_active = true;
+ have_patch_count++;
+ return ERR_OK;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static bool tfs_call_macro
+(
+ const ScanMacro_t *macro, // macro to call
+ uint dimension // '2' for a 2D and '3' for a 3D vector
+)
+{
+ DASSERT(tform_script_enabled);
+ DASSERT(macro);
+
+
+ //--- define local variables, '$P' is already defined
+
+ Var_t *var = InsertVarMap(&tfs_si.lvar,"$D",false,0,0);
+ DASSERT(var);
+ var->mode = VAR_INT;
+ var->i = dimension;
+
+ var = InsertVarMap(&tfs_si.lvar,"$I",false,0,0);
+ DASSERT(var);
+ var->mode = VAR_INT;
+ var->i = tfs_index++;
+
+
+ //--- setup macro
+
+ ScanFile_t *sf = tfs_si.cur_file;
+ DASSERT(sf);
+
+ const uint saved_n_files = tfs_si.n_files;
+ tfs_si.n_files = 0;
+ tfs_si.cur_file = &empty_scan_file;
+ tfs_si.last_result.mode = VAR_UNSET;
+
+ ScanFile_t *newsf = AddSF( &tfs_si, macro->data, macro->data_size,
+ macro->src_name, REVISION_NUM, 0 );
+ DASSERT(newsf);
+
+ newsf->active_macro = macro;
+ newsf->line = macro->line0;
+
+ //--- scan source
+
+ while (NextLineSI(&tfs_si,false,true))
+ ;
+
+ //--- close source
+
+ while (DropSF(&tfs_si))
+ ;
+ tfs_si.cur_file = sf;
+ tfs_si.n_files = saved_n_files;
+
+ //--- terminate
+
+ return tfs_si.last_result.mode == VAR_VECTOR;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool ScanTformBegin()
+{
+ if (!tform_script_enabled)
+ return false;
+ DASSERT(tfs_macro);
+
+ ClearVarMap(&tfs_si.lvar);
+ //ClearVarMap(&tfs_si.gvar);
+
+ const Var_t *vp = FindVarMap(&tfs_si.macro,"BEGIN",0);
+ if ( vp && vp->macro )
+ {
+ tfs_index = -1;
+ tfs_call_macro(vp->macro,0);
+ }
+
+ tfs_index = 0;
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool TformScriptEnd()
+{
+ if (!tform_script_enabled)
+ return false;
+
+ PRINT("### TFROM-STAT: %llu + %llu\n",
+ N_MatrixD_forward, N_MatrixD_inverse );
+ PRINT("### SCRIPT-STAT: %u | %u + %u = %u\n",
+ tfs_index,
+ tfs_count_f, tfs_count_d, tfs_count_f + tfs_count_d );
+
+ const Var_t *vp = FindVarMap(&tfs_si.macro,"END",0);
+ if ( vp && vp->macro )
+ {
+ tfs_call_macro(vp->macro,0);
+ tfs_index--;
+ }
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+enumError TformScriptCallF
+(
+ uint dimension, // '2' for a 2D and '3' for a 3D vector
+ float3 *v, // pointer to list of 3D vectors
+ int n, // number of 3D vectors
+ uint off // if n>1: offset from one to next vector
+)
+{
+ DASSERT(tform_script_enabled);
+ DASSERT(tfs_macro);
+
+ if ( n <= 0 || !tfs_macro )
+ return ERR_NOTHING_TO_DO;
+ DASSERT(v);
+
+ #if HAVE_PRINT
+ tfs_count_f += n;
+ #endif
+
+ for(;;)
+ {
+ ClearVarMap(&tfs_si.lvar);
+ Var_t *var = InsertVarMap(&tfs_si.lvar,"$P",false,0,0);
+ DASSERT(var);
+ var->mode = VAR_VECTOR;
+ var->x = v->x;
+ var->y = v->y;
+ var->z = v->z;
+
+ if (tfs_call_macro(tfs_macro,dimension))
+ {
+ v->x = tfs_si.last_result.x;
+ v->y = tfs_si.last_result.y;
+ v->z = tfs_si.last_result.z;
+ }
+
+ if (!--n)
+ return ERR_OK;
+ v = (typeof(v))( (u8*)v + off );
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+enumError TformScriptCallD
+(
+ uint dimension, // '2' for a 2D and '3' for a 3D vector
+ double3 *v, // pointer to list of 3D vectors
+ int n, // number of 3D vectors
+ uint off // if n>1: offset from one to next vector
+)
+{
+ DASSERT(tform_script_enabled);
+ DASSERT(tfs_macro);
+
+ if ( n <= 0 || !tfs_macro )
+ return ERR_NOTHING_TO_DO;
+ DASSERT(v);
+
+ #if HAVE_PRINT
+ tfs_count_d += n;
+ #endif
+
+ for(;;)
+ {
+ ClearVarMap(&tfs_si.lvar);
+ Var_t *var = InsertVarMap(&tfs_si.lvar,"$P",false,0,0);
+ DASSERT(var);
+ var->mode = VAR_VECTOR;
+ var->x = v->x;
+ var->y = v->y;
+ var->z = v->z;
+
+ if (tfs_call_macro(tfs_macro,dimension))
+ {
+ v->x = tfs_si.last_result.x;
+ v->y = tfs_si.last_result.y;
+ v->z = tfs_si.last_result.z;
+ }
+
+ if (!--n)
+ return ERR_OK;
+ v = (typeof(v))( (u8*)v + off );
+ }
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// Transformation Setup ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+uint force_transform = 0; // bit 0: force tform even if not needed
+ // bit 1: ignore it
+
+MatrixD_t opt_transform = {0}; // current transform values
+MatrixD_t transform_list[N_TRANSFORM] = {{0}}; // list with stored tforms
+
+uint n_transform_list = 0; // number of elements in list
+bool transform_active = false; // true: transformation active
+int disable_transformation = 0; // >0: transformation is temporary disabled
+
+double yposition = 0.0; // defined y position
+bool yposition_valid = false; // 'yposition' is valid
+
+bool ascale_valid = false; // true: --ascale defined
+double ascale = 0.0; // scaling factor
+double3 ascale_dir; // scaling direction (vector)
+
+bool arot_valid = false; // true: --arot defined
+double arot_deg = 0.0; // angle of arot in degree
+double3 arot1, arot2; // points to define rotation axis
+
+uint mdl_switched_to_vertex = 0; // 0: default
+ // 1: switch to 'VERTEX' needed
+ // 2: MDL_MODE switched to 'VERTEX'
+ // 3: '2' and message printed
+
+//
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+void ResetTransformation() // reset all transformation steps
+{
+ if (transform_active)
+ {
+ transform_active = false;
+ have_patch_count--;
+ }
+
+ n_transform_list = 0;
+ InitializeMatrixD(&opt_transform);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int NextTransformation ( bool for_close ) // enter next transformation step
+{
+ CalcMatrixD(&opt_transform,true);
+ if ( opt_transform.transform_enabled || force_transform == 1 && for_close )
+ {
+ force_transform |= 2;
+ if ( n_transform_list >= N_TRANSFORM-!for_close )
+ {
+ static bool err_done = false;
+ if (!err_done)
+ {
+ err_done = true;
+ ERROR0(ERR_SEMANTIC,
+ "Maximum number of --next (%u) already reached.\n",
+ N_TRANSFORM-1 );
+ }
+ return 1;
+ }
+
+ memcpy( transform_list + n_transform_list,
+ &opt_transform,
+ sizeof(*transform_list) );
+
+ n_transform_list++;
+ PRINT("--next: level %u/%u\n",n_transform_list,N_TRANSFORM);
+ }
+
+ InitializeMatrixD(&opt_transform);
+
+
+ //--- special: axis scaling
+
+ if (ascale_valid)
+ {
+ ascale_valid = false;
+ SetAScaleMatrixD(&opt_transform,ascale,&ascale_dir);
+ NextTransformation(for_close);
+ }
+
+
+ //--- special: axis rotation
+
+ if (arot_valid)
+ {
+ arot_valid = false;
+ SetARotateMatrixD(&opt_transform,arot_deg,0.0,&arot1,&arot2);
+ NextTransformation(for_close);
+ }
+
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void CloseTransformation() // close transformation input
+{
+ NextTransformation(true);
+ transform_active = n_transform_list > 0;
+
+ if (!transform_active)
+ InitializeMatrixD(&opt_transform);
+ else
+ {
+ have_patch_count++;
+ memcpy( &opt_transform, transform_list, sizeof(opt_transform) );
+
+ uint i;
+ for ( i = 1; i < n_transform_list; i++ )
+ MultiplyMatrixD(&opt_transform,0,transform_list+i,0);
+ }
+ CalcMatrixD(&opt_transform,true);
+ mdl_switched_to_vertex = !opt_transform.norm_valid;
+
+ ScanTformScript();
+
+ if (transform_active)
+ PATCH_ACTION_LOG("Setup","-","%u transformation step%s defined.\n",
+ n_transform_list, n_transform_list == 1 ? "" : "s" );
+
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void DumpTransformationOpt()
+{
+ if (have_set_scale)
+ printf(" set-scale: %11.3f %11.3f %11.3f\n",
+ set_scale.x, set_scale.y, set_scale.z );
+
+ if (have_set_rot)
+ printf(" set-rot: %11.3f %11.3f %11.3f\n",
+ set_rot.x, set_rot.y, set_rot.z );
+
+ if (have_set_value)
+ printf( " set-xyz-min: %11.3f %11.3f %11.3f\n"
+ " set-xyz-max: %11.3f %11.3f %11.3f\n",
+ set_value_min.x, set_value_min.y, set_value_min.z,
+ set_value_max.x, set_value_max.y, set_value_max.z );
+
+ if ( ascale != 0.0 )
+ {
+ printf( " last axis-scale: %11.3f @ %11.3f %11.3f %11.3f\n",
+ ascale,
+ ascale_dir.x, ascale_dir.y, ascale_dir.z );
+ }
+
+ if ( arot_deg != 0.0 )
+ {
+ printf( " last axis-rot: %11.3f @ %11.3f %11.3f %11.3f\n"
+ "%33s %11.3f %11.3f %11.3f\n",
+ arot_deg, arot1.x, arot1.y, arot1.z,
+ "->", arot2.x, arot2.y, arot2.z );
+ }
+
+ if (!transform_active)
+ return;
+
+ if ( n_transform_list > 1 )
+ {
+ uint i;
+ for ( i = 0; i < n_transform_list; i++ )
+ {
+ printf("\nTransformation step %u/%u:\n",i+1,n_transform_list);
+ if ( verbose > 0 )
+ PrintMatrixD(stdout,2,0,transform_list+i,~0,0x1f);
+ else
+ PrintMatrixD(stdout,2,0,transform_list,1,3);
+ }
+ printf("\nFinal Transformation:\n");
+ }
+ else
+ printf("\nTransformation:\n");
+
+ if ( verbose > 0 )
+ PrintMatrixD(stdout,2,0,&opt_transform,~0,0x1f);
+ else
+ PrintMatrixD(stdout,2,0,&opt_transform,1,3);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+double3 TransformD3( uint mode, double3 *val ) // transformate a single vector
+{
+ DASSERT(val);
+
+ uint i;
+ double3 res;
+ switch (mode)
+ {
+ case 1:
+ res = *val;
+ for ( i = 0; i < n_transform_list; i++ )
+ res = BaseTransformD3MatrixD(transform_list+i,&res);
+ break;
+
+ case 2:
+ res = *val;
+ for ( i = 0; i < n_transform_list; i++ )
+ res = NormTransformD3MatrixD(transform_list+i,&res);
+ break;
+
+ case 3:
+ res = *val;
+ for ( i = 0; i < n_transform_list; i++ )
+ res = TransformD3MatrixD(transform_list+i,&res);
+ break;
+
+ default:
+ res = TransformD3MatrixD(&opt_transform,val);
+ break;
+ }
+ if (tform_script_enabled)
+ TformScriptCallD(3,&res,1,0);
+ return res;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// Transformations ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+void TransformPosFloat2D
+(
+ float *v, // pointer to list of 2D vectors
+ int n, // number of 2D vectors
+ uint off // if n>1: offset from one to next vector
+)
+{
+ DASSERT(v);
+ DASSERT(n>0);
+ DASSERT( n <= 1 || off >= 2 * sizeof(*v) );
+
+ CalcNormMatrixD(&opt_transform); // calc 'norm_pos2d'
+ const double ypos = yposition_valid ? yposition : opt_transform.norm_pos2d.y;
+
+ off -= 2 * sizeof(*v);
+ while ( n-- > 0 )
+ {
+ float3 f;
+ f.x = v[0];
+ f.y = ypos;
+ f.z = v[1];
+ f = TransformF3MatrixD(&opt_transform,&f);
+ if (tform_script_enabled)
+ TformScriptCallF(2,&f,1,0);
+ *v++ = f.x;
+ *v++ = f.z;
+ v = (float*)( (u8*)v + off );
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void TransformPosDouble2D
+(
+ double *v, // pointer to list of 2D vectors
+ int n, // number of 2D vectors
+ uint off // if n>1: offset from one to next vector
+)
+{
+ DASSERT(v);
+ DASSERT(n>0);
+ DASSERT( n <= 1 || off >= 2 * sizeof(*v) );
+
+ CalcNormMatrixD(&opt_transform); // calc 'norm_pos2d'
+ const double ypos = yposition_valid ? yposition : opt_transform.norm_pos2d.y;
+
+ off -= 2 * sizeof(*v);
+ while ( n-- > 0 )
+ {
+ double3 d;
+ d.x = v[0];
+ d.y = ypos;
+ d.z = v[1];
+ d = TransformD3MatrixD(&opt_transform,&d);
+ if (tform_script_enabled)
+ TformScriptCallD(2,&d,1,0);
+ *v++ = d.x;
+ *v++ = d.z;
+ v = (double*)( (u8*)v + off );
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static bool IsNormValid()
+{
+ CalcNormMatrixD(&opt_transform);
+ if (opt_transform.norm_valid)
+ return true;
+
+ static bool done = false;
+ if ( !done && opt_transform.tmatrix_valid )
+ {
+ done = true;
+ ERROR0(ERR_WARNING,
+ "Because of the complex transformation matrix, it is impossible"
+ " to modify the scale and rotate vectors of different KMP sections;"
+ " the vectors keep unchanged. A manual correction is needed."
+ " The position vectors can always be transformed." );
+ }
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void TransformScaleFloat1D
+(
+ float *v, // pointer to list of 3D vectors
+ int n, // number of 2D vectors
+ uint off // if n>1: offset from one to next vector
+)
+{
+ DASSERT(v);
+ DASSERT(n>0);
+ DASSERT( n <= 1 || off >= sizeof(*v) );
+
+ if ( IsNormValid() && opt_transform.scale_enabled )
+ {
+ // only for KMP ENPT+ITPT scales:
+ const float scale_1d = ( fabs(opt_transform.norm_scale.x)
+ + fabs(opt_transform.norm_scale.z) ) / 2;
+
+ while ( n-- > 0 )
+ {
+ *v *= scale_1d;
+ v = (float*)( (u8*)v + off );
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void TransformScaleFloat3D
+(
+ float *v, // pointer to list of 3D vectors
+ int n, // number of 2D vectors
+ uint off // if n>1: offset from one to next vector
+)
+{
+ DASSERT(v);
+ DASSERT(n>0);
+ DASSERT( n <= 1 || off >= 3 * sizeof(*v) );
+
+ if ( IsNormValid() && opt_transform.scale_enabled )
+ {
+ const double3 *scale = &opt_transform.norm_scale;
+
+ while ( n-- > 0 )
+ {
+ v[0] *= scale->x;
+ v[1] *= scale->y;
+ v[2] *= scale->z;
+ v = (float*)( (u8*)v + off );
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void TransformScaleDouble3D
+(
+ double *v, // pointer to list of 3D vectors
+ int n, // number of 3D vectors
+ uint off // if n>1: offset from one to next vector
+)
+{
+ DASSERT(v);
+ DASSERT(n>0);
+ DASSERT( n <= 1 || off >= 3 * sizeof(*v) );
+
+ if ( IsNormValid() && opt_transform.scale_enabled )
+ {
+ const double3 *scale = &opt_transform.norm_scale;
+ while ( n-- > 0 )
+ {
+ v[0] *= scale->x;
+ v[1] *= scale->y;
+ v[2] *= scale->z;
+ v = (double*)( (u8*)v + off );
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+void TransformRotFloat3D
+(
+ float *v, // pointer to list of 3D vectors
+ int n, // number of 2D vectors
+ uint off // if n>1: offset from one to next vector
+)
+{
+ DASSERT(v);
+ DASSERT(n>0);
+ DASSERT( n <= 1 || off >= 3 * sizeof(*v) );
+
+ if ( IsNormValid() && opt_transform.rotate_enabled )
+ {
+ const double3 *rotate = &opt_transform.norm_rotate_deg;
+
+ while ( n-- > 0 )
+ {
+ v[0] = fmod( v[0] + rotate->x, 360.0 );
+ v[1] = fmod( v[1] + rotate->y, 360.0 );
+ v[2] = fmod( v[2] + rotate->z, 360.0 );
+ v = (float*)( (u8*)v + off );
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void TransformRotDouble3D
+(
+ double *v, // pointer to list of 3D vectors
+ int n, // number of 3D vectors
+ uint off // if n>1: offset from one to next vector
+)
+{
+ DASSERT(v);
+ DASSERT(n>0);
+ DASSERT( n <= 1 || off >= 3 * sizeof(*v) );
+
+ if ( IsNormValid() && opt_transform.rotate_enabled )
+ {
+ const double3 *rotate = &opt_transform.norm_rotate_deg;
+
+ while ( n-- > 0 )
+ {
+ v[0] = fmod( v[0] + rotate->x, 360.0 );
+ v[1] = fmod( v[1] + rotate->y, 360.0 );
+ v[2] = fmod( v[2] + rotate->z, 360.0 );
+ v = (double*)( (u8*)v + off );
+ }
+ }
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// command matrix ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+enumError cmd_matrix()
+{
+ enumError stat = ERR_OK;
+ const uint print_mode = verbose < -1 ? 0 : verbose < 0 ? 1 : 2;
+
+ ParamList_t *param;
+ for ( param = first_param; param; param = param->next )
+ {
+ ScanInfo_t si;
+ InitializeSI(&si,param->arg,strlen(param->arg),"TEST",0);
+ DEFINE_VAR(val);
+ const enumError err = ScanVectorExprSI(&si,&val,0.0,1);
+ if (!err)
+ TestMatrix(&val.d3,print_mode,true);
+ }
+
+ if ( verbose >= 0 )
+ PrintTestMatrix(true);
+
+ return stat;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool TestMatrix
+(
+ // returns false on error
+
+ double3 *val, // NULL or value to test
+ uint print_mode, // 0: silent
+ // 1: print vectors on error
+ // 2: print vectors always
+ bool print_matrix // true && if vector shall print:
+ // print matrix before, but only the first time
+)
+{
+ if (!val)
+ return true;
+
+ enum { max_mode = 4 };
+
+ uint mode;
+ double3 inv, tr[max_mode];
+ for ( mode = 0; mode < max_mode; mode++ )
+ tr[mode] = TransformD3(mode,val);
+ inv = InvTransformD3MatrixD(&opt_transform,tr);
+
+ const double null_epsilon = 1e-7;
+ const double diff_epsilon = 1e-6;
+ bool inv_ok = IsEqualD3( &inv, val, null_epsilon, diff_epsilon );
+
+ bool all_ok = inv_ok, tr_ok[max_mode];
+ for ( mode = 1; mode < max_mode; mode++ )
+ {
+ tr_ok[mode] = IsEqualD3(tr,tr+mode,null_epsilon,diff_epsilon);
+ if (!tr_ok[mode])
+ all_ok = false;
+ }
+
+ if ( print_mode > all_ok )
+ {
+ if (print_matrix)
+ PrintTestMatrix(true);
+
+ printf(" Base vector: %11.3f %11.3f %11.3f\n",
+ val->x, val->y, val->z );
+ if ( verbose > 0 || !inv_ok )
+ printf(" T.form+Inverse: %11.3f %11.3f %11.3f [%d]\n",
+ inv.x, inv.y, inv.z, inv_ok );
+ printf(" Transformation: %11.3f %11.3f %11.3f\n",
+ tr->x, tr->y, tr->z );
+ for ( mode = 1; mode < max_mode; mode++ )
+ if ( verbose > 0 || !tr_ok[mode] )
+ printf(" Transform[%u]: %11.3f %11.3f %11.3f [%d]\n",
+ mode, tr[mode].x, tr[mode].y, tr[mode].z, tr_ok[mode] );
+ printf("%.57s\n",Minus300);
+ }
+ return all_ok;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool PrintTestMatrix ( bool print_sep )
+{
+ static uint seq_num = ~0;
+ if ( seq_num == opt_transform.sequence_number )
+ return false;
+ seq_num = opt_transform.sequence_number;
+ putchar('\n');
+
+ static char xyz[8][4] = { "---", "x--", "-y-", "xy-", "--z", "x-z", "-yz", "xyz" };
+ static char use[4] = "-us?";
+
+ uint pmode_step, pmode_final;
+ if ( brief_count > 1 )
+ {
+ pmode_step = 0x08;
+ pmode_final = 0x08;
+ }
+ else if ( brief_count > 0 )
+ {
+ pmode_step = 0x08;
+ pmode_final = 0x16;
+ }
+ else if ( long_count > 1 )
+ {
+ pmode_step = 0x1f;
+ pmode_final = 0x1e;
+ }
+ else if ( long_count > 0 )
+ {
+ pmode_step = 0x17;
+ pmode_final = 0x1e;
+ }
+ else
+ {
+ pmode_step = 0x17;
+ pmode_final = n_transform_list > 1 ? 0x16 : 0x1f;
+ }
+
+ ccp sep;
+ static char sep_eq[] = "========================================"
+ "========================================";
+
+ if ( n_transform_list <= 1 )
+ {
+ pmode_final |= pmode_step;
+ sep = Minus300;
+ }
+ else
+ {
+ sep = sep_eq;
+ uint i;
+ for ( i = 0; i < n_transform_list; i++ )
+ {
+ MatrixD_t *t = transform_list+i;
+ printf("--------------- transformation matrix %u/%u [%s,%s,%s,%c] ---------------\n",
+ i+1, n_transform_list,
+ xyz[t->scale_enabled&7],
+ xyz[t->rotate_enabled&7],
+ xyz[t->translate_enabled&7],
+ use[t->use_matrix&3] );
+ PrintMatrixD(stdout,0,0,t,0,pmode_step);
+ }
+ }
+
+ printf("%.14s Final transformation matrix [%s,%s,%s,%c] %.14s\n",
+ sep,
+ xyz[opt_transform.scale_enabled&7],
+ xyz[opt_transform.rotate_enabled&7],
+ xyz[opt_transform.translate_enabled&7],
+ use[opt_transform.use_matrix&3],
+ sep );
+ PrintMatrixD(stdout,0,0,&opt_transform,0,pmode_final);
+
+ #ifdef TEST
+ fputs("Vectors calculated by Matrix:\n",stdout);
+ MatrixD_t temp = opt_transform;
+ CalcVectorsMatrixD(&temp,true);
+ PrintMatrixD(stdout,0,0,&temp,0,2);
+ #endif
+
+ if (print_sep)
+ printf("%.73s\n",sep);
+
+ return true;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// END ///////////////
+///////////////////////////////////////////////////////////////////////////////
+