From 79ad487580bc66689a82c6e49f929d0f6c25f54a Mon Sep 17 00:00:00 2001 From: Ksenia Date: Wed, 11 Mar 2026 01:52:05 +0100 Subject: [PATCH 01/18] Remove inp_mols_t structure --- python/vmol/main.py | 31 +++++++++++++++++++++---------- src/api.c | 8 ++++---- src/mol/mol.h | 1 + src/v/ac3_read.c | 2 +- src/v/load.c | 4 ++-- src/v/tools.c | 5 +---- src/v/v.h | 9 +-------- 7 files changed, 31 insertions(+), 29 deletions(-) diff --git a/python/vmol/main.py b/python/vmol/main.py index eb453e5..f8332a2 100644 --- a/python/vmol/main.py +++ b/python/vmol/main.py @@ -14,19 +14,30 @@ c_int_p = ctypes.POINTER(c_int) -class inp_mols_t(ctypes.Structure): # noqa: N801 - """C structure for the input molecule data, containing the number of atoms, charge array, coordinate array, and name.""" +class mol_t(ctypes.Structure): # noqa: N801 + """C structure for the input molecule data, containing the number of atoms, charge array, coordinate array, and name. + + Declared in src/mol/mol.h as + ``` + typedef struct { + double * r; + int * q; + char * name; + int n; + } mol; + ``` + """ _fields_ = ( - ("n", c_int), - ("q", c_int_p), ("r", c_double_p), + ("q", c_int_p), ("name", c_char_p), + ("n", c_int), ) ARGS_T = (c_int, ctypes.POINTER(ctypes.c_char_p)) -INP_MOLS_T = (c_int, ctypes.POINTER(inp_mols_t)) +INP_MOLS_T = (c_int, ctypes.POINTER(mol_t)) def mol2struct(get_element, mol): @@ -49,7 +60,7 @@ def mol2struct(get_element, mol): mol (dict or ase.atoms.Atoms-like): The molecule to convert. Returns: - inp_mols_t: An instance of `inp_mols_t` with the fields set according to the input molecule. + mol_t: An instance of `mol_t` with the fields set according to the input molecule. Raises: TypeError: If mol is not a dictionary. @@ -103,7 +114,7 @@ def mol2struct(get_element, mol): n = c_int(n) q = q.ctypes.data_as(c_int_p) r = r.ctypes.data_as(c_double_p) - in_str = inp_mols_t(n=n, q=q, r=r, name=name) + in_str = mol_t(n=n, q=q, r=r, name=name) in_str._keepalive = (n, q, r, name) # keep strong references return in_str @@ -245,10 +256,10 @@ def wrapped_func(argv: list[str], ...) ``` If `add_molecules` is True, the function is also expected to take an integer - and a pointer to an array of `inp_mols_t` structures as additional arguments after the first two, + and a pointer to an array of `mol_t` structures as additional arguments after the first two, which represent the number of molecules and the molecule data, respectively, i.e., ``` - void func(int argc, char ** argv, int nmol, inp_mols_t * mols, ...) -> def wrapped_func(argv: list[str], mols: object or list[object], ...) + void func(int argc, char ** argv, int nmol, mol_t * mols, ...) -> def wrapped_func(argv: list[str], mols: object or list[object], ...) ``` The decorator will convert a molecule or a list of molecules passed as the last argument to the wrapped function. @@ -303,7 +314,7 @@ def myinner(*args): argc, argv, _argv = make_array(args[0], c_char_p, convert_func=lambda x: x.encode('utf-8')) args.pop(0) if add_molecules: - nmol, mols, _mols = make_array(args[0], inp_mols_t, convert_func=lambda x: mol2struct(self.f.get_element, x)) + nmol, mols, _mols = make_array(args[0], mol_t, convert_func=lambda x: mol2struct(self.f.get_element, x)) args.pop(0) args = [nmol, mols, *args] args = [argc, argv, *args] diff --git a/src/api.c b/src/api.c index 9eb9cbf..690b5d4 100644 --- a/src/api.c +++ b/src/api.c @@ -5,9 +5,9 @@ #define FREE0(PTR) { free(PTR); PTR = NULL; } struct { - int n_inp_mols; - inp_mols_t * inp_mols; + mol * inp_mols; char * out_str; + int n_inp_mols; } globals; char * main_wrap_out(int argc, char * argv[], int * ret) { @@ -16,7 +16,7 @@ char * main_wrap_out(int argc, char * argv[], int * ret) { return globals.out_str; } -int main_wrap_in(int argc, char * argv[], int n_inp_mols, inp_mols_t * inp_mols) { +int main_wrap_in(int argc, char * argv[], int n_inp_mols, mol * inp_mols) { globals.inp_mols = inp_mols; globals.n_inp_mols = n_inp_mols; int ret = main(argc, argv); @@ -26,7 +26,7 @@ int main_wrap_in(int argc, char * argv[], int n_inp_mols, inp_mols_t * inp_mols) } char * main_wrap_in_out(int argc, char * argv[], - int n_inp_mols, inp_mols_t * inp_mols, + int n_inp_mols, mol * inp_mols, int * ret){ globals.out_str = calloc(PRINTBUFLEN, 1); *ret = main_wrap_in(argc, argv, n_inp_mols, inp_mols); diff --git a/src/mol/mol.h b/src/mol/mol.h index ded41fd..7c08029 100644 --- a/src/mol/mol.h +++ b/src/mol/mol.h @@ -6,6 +6,7 @@ typedef struct { double * r; int * q; + char * name; int n; } mol; diff --git a/src/v/ac3_read.c b/src/v/ac3_read.c index 57cf11f..8493256 100644 --- a/src/v/ac3_read.c +++ b/src/v/ac3_read.c @@ -46,7 +46,7 @@ atcoord * atcoord_fill(int n, txyz * a, const char * fname, int b, int center, i r3cp(m->r+i*3, a[i].r); } if(inertia){ - mol M = {.n=m->n, .q=m->q, .r=m->r}; + mol M = {.n=n, .q=m->q, .r=m->r, .name=NULL}; position(&M, NULL, 1); } if(center){ diff --git a/src/v/load.c b/src/v/load.c index 6826a92..b6c42a0 100644 --- a/src/v/load.c +++ b/src/v/load.c @@ -166,7 +166,7 @@ void * read_files(drawpars * dp){ return ent; } -atcoords * get_in_str(int N, inp_mols_t * inp_mols, drawpars * dp){ +atcoords * get_in_str(int N, mol * inp_mols, drawpars * dp){ for(int i=0; iinput_files_n; i++){ PRINT_WARN("ignoring file '%s'\n", dp->input_files[i]); @@ -188,7 +188,7 @@ atcoords * get_in_str(int N, inp_mols_t * inp_mols, drawpars * dp){ txyz * xyz = malloc(sizeof(txyz)*nmax); for(int i=0; in; i++){ xyz[i].t = inmol->q[i]; r3cp(xyz[i].r, inmol->r+i*3); diff --git a/src/v/tools.c b/src/v/tools.c index e1fbca7..886bd6e 100644 --- a/src/v/tools.c +++ b/src/v/tools.c @@ -87,10 +87,7 @@ void vibro_text(modestr * ms, drawpars * dp){ void pg(atcoord * a, styp s, double symtol){ int n = a->n; - mol m; - m.n = n; - m.q = a->q; - m.r = malloc(sizeof(double )*n*3); + mol m = {.n = n, .q = a->q, .r=malloc(sizeof(double)*n*3), .name=NULL}; veccp (n*3, m.r, a->r); vecscal(n*3, m.r, AB); diff --git a/src/v/v.h b/src/v/v.h index c64159f..947b1f0 100644 --- a/src/v/v.h +++ b/src/v/v.h @@ -114,16 +114,9 @@ typedef struct { double r[3]; } txyz; -typedef struct { - int n; - int * q; - double * r; - char * name; -} inp_mols_t; - // load.c -atcoords * get_in_str(int N, inp_mols_t * inp_mols, drawpars * dp); +atcoords * get_in_str(int N, mol * inp_mols, drawpars * dp); void acs_readmore (FILE * f, int b, int center, int inertia, int bohr, atcoords * acs, const char * fname); void * read_files(drawpars * dp); // scale.c From f657f3a1bcf5c0f82f15bf8d4573394e0f519650 Mon Sep 17 00:00:00 2001 From: Ksenia Date: Wed, 11 Mar 2026 02:33:32 +0100 Subject: [PATCH 02/18] Simplify reading a mol from python --- obj/v/ac3_read.d | 2 +- src/api.c | 2 +- src/v/ac3_read.c | 38 +++++++++++++++++++++++++++++++------- src/v/load.c | 28 ++++++++++------------------ src/v/v.h | 4 ++-- 5 files changed, 45 insertions(+), 29 deletions(-) diff --git a/obj/v/ac3_read.d b/obj/v/ac3_read.d index 1fcc562..9fc7a3a 100644 --- a/obj/v/ac3_read.d +++ b/obj/v/ac3_read.d @@ -1,2 +1,2 @@ obj/v/ac3_read.o obj-pic/v/ac3_read.o: src/v/ac3_read.c src/v/v.h \ - src/mol/mol.h src/mol/common.h src/math/vec3.h + src/mol/mol.h src/mol/common.h src/math/vecn.h src/math/vec3.h diff --git a/src/api.c b/src/api.c index 690b5d4..cd30079 100644 --- a/src/api.c +++ b/src/api.c @@ -81,7 +81,7 @@ void * READ_FILES(drawpars * dp){ ret = read_files(dp); } else{ - ret = get_in_str(globals.n_inp_mols, globals.inp_mols, dp); + ret = acs_from_var(globals.n_inp_mols, globals.inp_mols, dp); } FREE0(dp->input_files); return ret; diff --git a/src/v/ac3_read.c b/src/v/ac3_read.c index 8493256..4920802 100644 --- a/src/v/ac3_read.c +++ b/src/v/ac3_read.c @@ -1,11 +1,23 @@ #include "v.h" +#include "vecn.h" #include "vec3.h" #define END(S,X) ( (S)->X + (X##_size)/sizeof(*((S)->X)) ) -atcoord * atcoord_fill(int n, txyz * a, const char * fname, int b, int center, int inertia, int bohr){ +atcoord * atcoord_fill(int n_, void * a, const char * fname, int b, int center, int inertia, int bohr){ + + // n_>0 and fname is not NULL => a is (txyz *) + // n_<0 and fname is NULL => a is (mol *) + + int n; + if(n_<0){ + n = ((mol *)a)->n; + } + else{ + n = n_; + } size_t q_size = sizeof(int ) * n; size_t r_size = sizeof(double) * n*3; @@ -36,14 +48,26 @@ atcoord * atcoord_fill(int n, txyz * a, const char * fname, int b, int center, i } memset(m->sym, 0, sizeof(m->sym)); - m->fname = fname; - for(int i=0; iq[i] = a[i].t; - if(bohr){ - r3scal(a[i].r, BA); + if(n_<0){ + mol * m0 = a; + for(int i=0; iq[i] = m0->q[i]; + r3cp(m->r+i*3, m0->r+i*3); + } + m->fname = m0->name; + } + else{ + txyz * xyz = a; + for(int i=0; iq[i] = xyz[i].t; + r3cp(m->r+i*3, xyz[i].r); } - r3cp(m->r+i*3, a[i].r); + m->fname = fname; + } + + if(bohr){ + vecscal(n*3, m->r, BA); } if(inertia){ mol M = {.n=n, .q=m->q, .r=m->r, .name=NULL}; diff --git a/src/v/load.c b/src/v/load.c index b6c42a0..c703b14 100644 --- a/src/v/load.c +++ b/src/v/load.c @@ -166,7 +166,7 @@ void * read_files(drawpars * dp){ return ent; } -atcoords * get_in_str(int N, mol * inp_mols, drawpars * dp){ +atcoords * acs_from_var(int n, mol * m, drawpars * dp){ for(int i=0; iinput_files_n; i++){ PRINT_WARN("ignoring file '%s'\n", dp->input_files[i]); @@ -177,30 +177,22 @@ atcoords * get_in_str(int N, mol * inp_mols, drawpars * dp){ dp->task = AT3COORDS; atcoords * acs = malloc(sizeof(atcoords)); - acs->Nmem = N; + acs->Nmem = acs->n = n; acs->m = malloc(acs->Nmem*sizeof(atcoord *)); - acs->n = N; - int nmax = 0; - for(int i=0; im[i] = atcoord_fill(-1, m+i, NULL, dp->b, dp->center, dp->inertia, dp->bohr); } - txyz * xyz = malloc(sizeof(txyz)*nmax); - for(int i=0; in; i++){ - xyz[i].t = inmol->q[i]; - r3cp(xyz[i].r, inmol->r+i*3); - } - acs->m[i] = atcoord_fill(inmol->n, xyz, inmol->name, dp->b, dp->center, dp->inertia, dp->bohr); - } - - free(xyz); fill_nf(acs, 0); dp->scale = acs_scale(acs); newmol_prep(acs, dp); - intcoord_check(nmax, dp->z); + + int natmax = 0; + for(int i=0; iz); return acs; } diff --git a/src/v/v.h b/src/v/v.h index 947b1f0..a840a71 100644 --- a/src/v/v.h +++ b/src/v/v.h @@ -116,7 +116,7 @@ typedef struct { // load.c -atcoords * get_in_str(int N, mol * inp_mols, drawpars * dp); +atcoords * acs_from_var(int n, mol * m, drawpars * dp); void acs_readmore (FILE * f, int b, int center, int inertia, int bohr, atcoords * acs, const char * fname); void * read_files(drawpars * dp); // scale.c @@ -125,7 +125,7 @@ double acs_scale(atcoords * acs); // mode_read.c modestr * mode_read(FILE * f, int na); // ac3_read*.c -atcoord * atcoord_fill(int n, txyz * a, const char * fname, int b, int center, int inertia, int bohr); +atcoord * atcoord_fill(int n_, void * a, const char * fname, int b, int center, int inertia, int bohr); atcoord * ac3_read(FILE * f, int b, int center, int inertia, int bohr, const char * fname, format_t * format); txyz * ac3_read_in (int * n_p, int * zmat, FILE * f); txyz * ac3_read_out(int * n_p, FILE * f); From c9414d6023a47b5b78d7aa52056661fe79ab3cfa Mon Sep 17 00:00:00 2001 From: Ksenia Date: Wed, 11 Mar 2026 08:50:57 +0100 Subject: [PATCH 03/18] Simplify pg() --- src/v/evr.c | 2 +- src/v/headless.c | 6 +++--- src/v/tools.c | 4 ++-- src/v/v.h | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/v/evr.c b/src/v/evr.c index 80a647f..1389ab9 100644 --- a/src/v/evr.c +++ b/src/v/evr.c @@ -491,7 +491,7 @@ void kp_pg(void * ent, drawpars * dp){ if(dp->task == AT3COORDS){ atcoord * ac = ((atcoords *)ent)->m[dp->n]; if(!ac->sym[0]){ - pg(ac, ac->sym, dp->symtol); + pg(ac, dp->symtol); redraw_ac3(ent, dp); } } diff --git a/src/v/headless.c b/src/v/headless.c index 0673d4d..8f4698f 100644 --- a/src/v/headless.c +++ b/src/v/headless.c @@ -29,9 +29,9 @@ void run_commands(FILE * f, char * command, drawpars * dp, void * ent){ kp_print(ent, dp); break; case('.'): { - styp sym; - pg(((atcoords *)ent)->m[dp->n], sym, dp->symtol); - PRINTOUT(stdout, "%s\n", sym); + atcoord * ac = ((atcoords *)ent)->m[dp->n]; + pg(ac, dp->symtol); + PRINTOUT(stdout, "%s\n", ac->sym); }; break; case(' '): diff --git a/src/v/tools.c b/src/v/tools.c index 886bd6e..b6b1d7f 100644 --- a/src/v/tools.c +++ b/src/v/tools.c @@ -84,7 +84,7 @@ void vibro_text(modestr * ms, drawpars * dp){ return; } -void pg(atcoord * a, styp s, double symtol){ +void pg(atcoord * a, double symtol){ int n = a->n; mol m = {.n = n, .q = a->q, .r=malloc(sizeof(double)*n*3), .name=NULL}; @@ -92,7 +92,7 @@ void pg(atcoord * a, styp s, double symtol){ vecscal(n*3, m.r, AB); molsym * ms = pointgroup(&m, symtol*AB); - strcpy(s, ms->s); + snprintf(a->sym, sizeof(styp), "%s", ms->s); free(m.r); free(ms); diff --git a/src/v/v.h b/src/v/v.h index a840a71..8d834b2 100644 --- a/src/v/v.h +++ b/src/v/v.h @@ -173,7 +173,7 @@ void acs_free(atcoords * acs); void newmol_prep(atcoords * acs, drawpars * dp); void ac3_text(atcoord * ac, drawpars * dp); void vibro_text(modestr * ms, drawpars * dp); -void pg(atcoord * a, styp s, double symtol); +void pg(atcoord * a, double symtol); // headless.c void run_commands(FILE * f, char * command, drawpars * dp, void * ent); From 7f3521c8640f3668076d336c1ca44ff528a2e5eb Mon Sep 17 00:00:00 2001 From: Ksenia Date: Wed, 11 Mar 2026 09:26:57 +0100 Subject: [PATCH 04/18] Check for NULL pointers --- src/api.c | 15 ++++++++++++++- src/v/mode_read.c | 4 +++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/api.c b/src/api.c index cd30079..d81d201 100644 --- a/src/api.c +++ b/src/api.c @@ -12,6 +12,10 @@ struct { char * main_wrap_out(int argc, char * argv[], int * ret) { globals.out_str = calloc(PRINTBUFLEN, 1); + if(!globals.out_str){ + *ret = -1; + return NULL; + } *ret = main(argc, argv); return globals.out_str; } @@ -29,6 +33,10 @@ char * main_wrap_in_out(int argc, char * argv[], int n_inp_mols, mol * inp_mols, int * ret){ globals.out_str = calloc(PRINTBUFLEN, 1); + if(!globals.out_str){ + *ret = -1; + return NULL; + } *ret = main_wrap_in(argc, argv, n_inp_mols, inp_mols); return globals.out_str; } @@ -65,7 +73,12 @@ void PRINTOUT(FILE * f, char * format, ...){ if(m >= size){ N = m < N ? N * 2 : N + 2*m; - globals.out_str = realloc(globals.out_str, N); + char * tmp = realloc(globals.out_str, N); + if(!tmp){ + PRINT_ERR("cannot reallocate output buffer\n"); + abort(); + } + globals.out_str = tmp; va_start(args, format); vsnprintf(globals.out_str+n, N-n, format, args); va_end(args); diff --git a/src/v/mode_read.c b/src/v/mode_read.c index f2845c9..8b6569c 100644 --- a/src/v/mode_read.c +++ b/src/v/mode_read.c @@ -69,7 +69,9 @@ modestr * mode_read (FILE * f, int na){ break; } } - fgets(s, sizeof(s), f); + if (!fgets(s, sizeof(s), f)){ + return NULL; + } while(1){ if (!fgets(s, sizeof(s), f)) { return NULL; From e8a4df57e1da9aa0ac0e2cd25d49b8d635793b5e Mon Sep 17 00:00:00 2001 From: Ksenia Date: Wed, 11 Mar 2026 12:21:45 +0100 Subject: [PATCH 05/18] Do not overwrite input atom symbols + cleanup macro --- src/mol/inertia.c | 25 +++++++++++++++---------- src/v/get_atpar.c | 7 ++++--- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/mol/inertia.c b/src/mol/inertia.c index 5330345..404a262 100644 --- a/src/mol/inertia.c +++ b/src/mol/inertia.c @@ -4,6 +4,16 @@ #define EPS 1e-10 +static inline void swap_ev(double d[3], double I_b[9], int i, int j){ + double td = d[i]; + d[i] = d[j]; + d[j] = td; + double tb[3]; + r3cp(tb, I_b+i*3); + r3cp(I_b+i*3, I_b+j*3); + r3cp(I_b+j*3, tb); +} + static const double amass[]={ #include "masses.h" }; @@ -60,23 +70,18 @@ void position(mol * m, double d[3], int preserve_chirality){ double I_b[9]={1,0,0, 0,1,0, 0,0,1}; jacobi(I_t, I_b, d, 3, 1e-15, 20, NULL); -#if 1 -#define SWITCH(I,J) { double td, tb[3];\ - td = d[I]; d[I] = d[J]; d[J] = td; \ - r3cp(tb, I_b+I*3); r3cp(I_b+I*3, I_b+J*3); r3cp(I_b+J*3, tb); } //sort ev - if(d[0]n; i++){ double u[3]; diff --git a/src/v/get_atpar.c b/src/v/get_atpar.c index 325c929..7114a12 100644 --- a/src/v/get_atpar.c +++ b/src/v/get_atpar.c @@ -57,13 +57,14 @@ int get_element(styp s){ return (int)q; } - s[0] = toupper(s[0]); + styp ts = ""; + ts[0] = toupper(s[0]); for(int i=1; s[i]; i++){ - s[i] = tolower(s[i]); + ts[i] = tolower(s[i]); } for(int q=1; q<=NATOMS; q++){ - if(!strncmp(s, aname[q], NAMELEN)){ + if(!strncmp(ts, aname[q], NAMELEN)){ return q; } } From be11dfb7ee00247b66e6d8813dc88dc42eebe0a6 Mon Sep 17 00:00:00 2001 From: Ksenia Date: Wed, 11 Mar 2026 12:48:44 +0100 Subject: [PATCH 06/18] Get rid of magic numbers (1) --- src/v/ac3_draw.c | 7 +++++-- src/v/bonds.c | 3 ++- src/v/evr.c | 3 ++- src/v/loop.c | 4 +++- src/v/scale.c | 4 +++- 5 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/v/ac3_draw.c b/src/v/ac3_draw.c index fe85e91..0429906 100644 --- a/src/v/ac3_draw.c +++ b/src/v/ac3_draw.c @@ -1,6 +1,9 @@ #include "v.h" #include "x.h" +#define BOND_OFFSET 0.666 // bond line starts this fraction of the atom radius away from the atom center +#define RESOL_SCALE (128.0/768.0) // reference resolution for atom sizes + extern int W,H; extern int screen; extern Display * dis; @@ -42,7 +45,7 @@ void ac3_draw(atcoord * ac, double r0, double scale, double xy0[2], int b, int n int * ks = (b>0) ? malloc(sizeof(int)*n) : NULL; double d = MIN(H,W) * scale; - double resol = MIN(H,W) * (128.0/768.0); + double resol = MIN(H,W) * RESOL_SCALE; double r1 = r0 * resol * scale; for(int k=0; k 0.0) ? bmax : (2.01 * rl * getmaxradius(ac->n, ac->q)); + double dmax = (bmax > 0.0) ? bmax : (DMAX_SCALE * rl * getmaxradius(ac->n, ac->q)); double rmin[3], rmax[3]; r3cp(rmin, ac->r); diff --git a/src/v/evr.c b/src/v/evr.c index 1389ab9..0aa0ee6 100644 --- a/src/v/evr.c +++ b/src/v/evr.c @@ -8,6 +8,7 @@ static const double step_move = 0.2; static const double step_zoom = 1.1; static const double step_r = 1.1; static const double step_mod = 0.03125; +static const double rl_move_pbc_scale = 0.9; static void redraw_ac3(void * ent, drawpars * dp){ atcoord * ac = ((atcoords *)ent)->m[dp->n]; @@ -310,7 +311,7 @@ static void move_pbc(atcoords * acs, drawpars * dp, int dir, double d){ } if(dp->b>0){ acs->m[i]->bond_flag = 0; - acs->m[i]->bond_rl *= 0.9; + acs->m[i]->bond_rl *= rl_move_pbc_scale; } } return; diff --git a/src/v/loop.c b/src/v/loop.c index b8409f0..ab56dec 100644 --- a/src/v/loop.c +++ b/src/v/loop.c @@ -2,6 +2,8 @@ #include "x.h" #include "evr.h" +#define VIBRO_SUBSTEPS 4 + extern int W,H; extern Display * dis; extern Window win; @@ -129,7 +131,7 @@ void main_loop(void * ent, drawpars * dp, ptf kp[NKP]){ * because we need an XEvent to reiterate the main loop. * Alternatively, we can send an event manually. */ - if(++tr == 4){ + if(++tr == VIBRO_SUBSTEPS){ tr = 0; dp->t++; } diff --git a/src/v/scale.c b/src/v/scale.c index 189b988..18e6025 100644 --- a/src/v/scale.c +++ b/src/v/scale.c @@ -1,6 +1,8 @@ #include "v.h" #include "vec3.h" +#define VIEWPORT_FILL 0.5 // molecule fills this fraction of the viewport + double ac3_scale(atcoord * ac){ double center[3] = {}; for(int k=0; kn; k++){ @@ -13,7 +15,7 @@ double ac3_scale(atcoord * ac){ double d2 = r3d2(center, ac->r+3*k); d2max = MAX(d2max, d2+rad*rad); } - return 0.5 / sqrt(d2max); + return VIEWPORT_FILL / sqrt(d2max); } double acs_scale(atcoords * acs){ From 85548640eb4bed0570b089b283ab2d793d407648 Mon Sep 17 00:00:00 2001 From: Ksenia Date: Fri, 13 Mar 2026 02:48:28 +0100 Subject: [PATCH 07/18] Refactor coordinate readers Fix: if zmat and units=bohr, the unit conversion was made before finishing the zmat -> cart conversion, and the molecule was broken --- obj/math/zmat.d | 2 + obj/v/ac3_read_in.d | 4 +- obj/v/ac3_read_out.d | 2 +- src/math/3d.h | 6 ++ src/math/zmat.c | 62 ++++++++++++ src/mol/mol.h | 10 +- src/v/ac3_read.c | 75 ++++---------- src/v/ac3_read_in.c | 232 ++++++++++++++++++++----------------------- src/v/ac3_read_out.c | 35 ++++--- src/v/ac3_read_xyz.c | 16 +-- src/v/load.c | 2 +- src/v/v.h | 14 +-- 12 files changed, 249 insertions(+), 211 deletions(-) create mode 100644 obj/math/zmat.d create mode 100644 src/math/zmat.c diff --git a/obj/math/zmat.d b/obj/math/zmat.d new file mode 100644 index 0000000..fd6fbda --- /dev/null +++ b/obj/math/zmat.d @@ -0,0 +1,2 @@ +obj/math/zmat.o obj-pic/math/zmat.o: src/math/zmat.c src/math/3d.h \ + src/math/matrix.h src/math/vecn.h src/math/vec3.h diff --git a/obj/v/ac3_read_in.d b/obj/v/ac3_read_in.d index c91c418..4ee71dd 100644 --- a/obj/v/ac3_read_in.d +++ b/obj/v/ac3_read_in.d @@ -1,3 +1,3 @@ obj/v/ac3_read_in.o obj-pic/v/ac3_read_in.o: src/v/ac3_read_in.c \ - src/v/v.h src/mol/mol.h src/mol/common.h src/math/vec3.h src/math/3d.h \ - src/math/matrix.h src/math/vecn.h + src/v/v.h src/mol/mol.h src/mol/common.h src/math/3d.h src/math/matrix.h \ + src/math/vecn.h src/math/vec3.h diff --git a/obj/v/ac3_read_out.d b/obj/v/ac3_read_out.d index 725c44c..e2ff908 100644 --- a/obj/v/ac3_read_out.d +++ b/obj/v/ac3_read_out.d @@ -1,2 +1,2 @@ obj/v/ac3_read_out.o obj-pic/v/ac3_read_out.o: src/v/ac3_read_out.c \ - src/v/v.h src/mol/mol.h src/mol/common.h + src/v/v.h src/mol/mol.h src/mol/common.h src/math/vec3.h diff --git a/src/math/3d.h b/src/math/3d.h index c905d6b..04f9de9 100644 --- a/src/math/3d.h +++ b/src/math/3d.h @@ -1,6 +1,12 @@ #include "matrix.h" +#define DEG2RAD (M_PI/180.0) + void rotmx0_update(double mx[9], double mx1[9], double phi, int axis); void rot3d (int n, double * r, double m[9]); void rotmx (double * rot, double * u, double phi); void rot_around_perp(double rot[9], double dx, double dy, double factor); + +int zmat2cart(int n, double r[3], + double a[3], double b[3], double c[3], + double R, double phi, double theta); diff --git a/src/math/zmat.c b/src/math/zmat.c new file mode 100644 index 0000000..e90eb5b --- /dev/null +++ b/src/math/zmat.c @@ -0,0 +1,62 @@ +#include "3d.h" +#include "vec3.h" + +#define EPS 1e-15 + +int zmat2cart(int n, double r[3], + double a[3], double b[3], double c[3], + double R, double phi, double theta){ + + if(n == 0){ + r[0] = r[1] = r[2] = 0.0; + } + + else if(n == 1){ + r[0] = a[0]; + r[1] = a[1] + R; + r[2] = a[2]; + } + + else if(n == 2){ + r[0] = a[0] + R * sqrt( 1 - cos(phi)*cos(phi) ); + r[1] = a[1] + ( (b[1]n = n; + m->r = (double *)(m + 1); + m->q = (int *)(m->r + 3*n); + return m; +} + void position(mol * m, double d[3], int preserve_chirality); void center_mol(int n, double * r, int * q); diff --git a/src/v/ac3_read.c b/src/v/ac3_read.c index 4920802..634f76c 100644 --- a/src/v/ac3_read.c +++ b/src/v/ac3_read.c @@ -2,22 +2,11 @@ #include "vecn.h" #include "vec3.h" - #define END(S,X) ( (S)->X + (X##_size)/sizeof(*((S)->X)) ) +atcoord * atcoord_fill(mol * m0, int b, int center, int inertia, int bohr){ -atcoord * atcoord_fill(int n_, void * a, const char * fname, int b, int center, int inertia, int bohr){ - - // n_>0 and fname is not NULL => a is (txyz *) - // n_<0 and fname is NULL => a is (mol *) - - int n; - if(n_<0){ - n = ((mol *)a)->n; - } - else{ - n = n_; - } + int n = m0->n; size_t q_size = sizeof(int ) * n; size_t r_size = sizeof(double) * n*3; @@ -49,29 +38,18 @@ atcoord * atcoord_fill(int n_, void * a, const char * fname, int b, int center, memset(m->sym, 0, sizeof(m->sym)); - if(n_<0){ - mol * m0 = a; - for(int i=0; iq[i] = m0->q[i]; - r3cp(m->r+i*3, m0->r+i*3); - } - m->fname = m0->name; - } - else{ - txyz * xyz = a; - for(int i=0; iq[i] = xyz[i].t; - r3cp(m->r+i*3, xyz[i].r); - } - m->fname = fname; + for(int i=0; iq[i] = m0->q[i]; + r3cp(m->r+i*3, m0->r+i*3); } + m->fname = m0->name; if(bohr){ vecscal(n*3, m->r, BA); } if(inertia){ - mol M = {.n=n, .q=m->q, .r=m->r, .name=NULL}; - position(&M, NULL, 1); + // should not change m0 + position(&((mol){.n=n, .q=m->q, .r=m->r}), NULL, 1); } if(center){ center_mol(n, m->r, center==2 ? m->q : NULL); @@ -80,58 +58,49 @@ atcoord * atcoord_fill(int n_, void * a, const char * fname, int b, int center, return m; } - atcoord * ac3_read(FILE * f, int b, int center, int inertia, int bohr, const char * fname, format_t * format){ - int n; - int zmat=0; - txyz * a; + mol * m = NULL; switch(*format){ case XYZ: - if((a=ac3_read_xyz(&n, f))){ + if((m=ac3_read_xyz(f))){ *format = XYZ; } break; case IN: - if((a=ac3_read_in(&n, &zmat, f))){ + if((m=ac3_read_in(f))){ *format = IN; } break; case OUT: - if((a=ac3_read_out(&n, f))){ + if((m=ac3_read_out(f))){ *format = OUT; } break; default: - if((a=ac3_read_xyz(&n, f))){ + if((m=ac3_read_xyz(f))){ *format = XYZ; } - if(!a){ - if((a=ac3_read_in(&n, &zmat, f))){ + if(!m){ + if((m=ac3_read_in(f))){ *format = IN; } } - if(!a){ - if((a=ac3_read_out(&n, f))){ + if(!m){ + if((m=ac3_read_out(f))){ *format = OUT; } } break; } - if(!a){ + if(!m){ return NULL; } + m->name = fname; - atcoord * m = atcoord_fill(n, a, fname, b, center, inertia, bohr); - -#if 0 - printf("%d\n\n", n); - for(int i=0; iq[i], m->r[i*3 ], m->r[i*3+1], m->r[i*3+2]); - } -#endif - free(a); - return m; + atcoord * M = atcoord_fill(m, b, center, inertia, bohr); + free(m); + return M; } diff --git a/src/v/ac3_read_in.c b/src/v/ac3_read_in.c index 5515893..ad1a2d7 100644 --- a/src/v/ac3_read_in.c +++ b/src/v/ac3_read_in.c @@ -1,83 +1,93 @@ +#include #include "v.h" -#include "vec3.h" -#define EPS 1e-15 #include "3d.h" +#include "vec3.h" -static int zmat2cart(int n, txyz * mr, double r[3], - int a1, int a2, int a3, - double R, double phi, double theta){ - - if(n == 0){ - r[0] = r[1] = r[2] = 0.0; +int read_cart_atom(FILE * f, int n, mol * m){ + int q; + double r[3]; + int res = (fscanf(f, "%d%lf%lf%lf", &q, r, r+1, r+2)==4); + if(res && m){ + m->q[n] = q; + r3cp(m->r+3*n, r); } + return res; +} + +static int read_z_atom( FILE * f, int n, mol * m){ + + int res, q, a, b, c; + double R, phi, theta; - else if(n == 1){ - r[0] = mr[a1].r[0]; - r[1] = mr[a1].r[1] + R; - r[2] = mr[a1].r[2]; + switch(n){ + case 0: + res = (fscanf(f, "%d", &q) == 1); + break; + case 1: + res = (fscanf(f, "%d%d%lf", &q, &a, &R) == 3); + break; + case 2: + res = (fscanf(f, "%d%d%lf%d%lf", &q, &a, &R, &b, &phi) == 5); + break; + default: + res = (fscanf(f, "%d%d%lf%d%lf%d%lf", &q, &a, &R, &b, &phi, &c, &theta) == 7); + break; } - else if(n == 2){ - r[0] = mr[a1].r[0] + R * sqrt( 1 - cos(phi)*cos(phi) ); - r[1] = mr[a1].r[1] + ( (mr[a2].r[1]q[n] = q; + res = !zmat2cart(n, m->r+3*n, + m->r+3*(a-1), m->r+3*(b-1), m->r+3*(c-1), + R, phi*DEG2RAD, theta*DEG2RAD); } + return res; +} +static inline int read_atom(FILE * f, int zmat, int n, mol * m){ + if(zmat){ + return read_z_atom(f, n, m); + } else{ + return read_cart_atom(f, n, m); + } +} - double ab[3], bc[3]; - double r1[3], r2[3]; - double perp[3]; - double rot[9]; - double t; - - double * a = mr[a1].r; - double * b = mr[a2].r; - double * c = mr[a3].r; - - r3diff(ab, b, a); - t = r3dot(ab,ab); - if(tr, BA); + } + return m; + hell: - fseek(f, pos, SEEK_SET); - free(a); + fseek(f, pos0, SEEK_SET); return NULL; } diff --git a/src/v/ac3_read_out.c b/src/v/ac3_read_out.c index 7d42703..0faf923 100644 --- a/src/v/ac3_read_out.c +++ b/src/v/ac3_read_out.c @@ -1,12 +1,13 @@ #include "v.h" +#include "vec3.h" +#include "mol.h" -txyz * ac3_read_out(int * n_p, FILE * f){ - - txyz * a = NULL; +mol * ac3_read_out(FILE * f){ + // find where the molecule begins char s[STRLEN]; - while (1){ - if (!fgets(s, sizeof(s), f)) { + while(1){ + if(!fgets(s, sizeof(s), f)){ return NULL; } if(strstr(s, "Atomic Coordinates:")){ @@ -14,16 +15,22 @@ txyz * ac3_read_out(int * n_p, FILE * f){ } } - int n = 0; - while(1) { - a = realloc(a, sizeof(txyz)*(n+1)); - if (fscanf (f, "%d%lf%lf%lf", - &(a[n].t), a[n].r, a[n].r+1, a[n].r+2) != 4) { - break; - } + // count atoms + long pos = ftell(f); + int n=0; + while(read_cart_atom(f, n, NULL)){ n++; } + if(!n){ + return NULL; + } + fseek(f, pos, SEEK_SET); + + // fill in + mol * m = alloc_mol(n); + for(int i=0; ir+3*i, m->r+3*i+1, m->r+3*i+2, tmp_str) < 4) { + free(m); return NULL; } - a[i].t = get_element(type); + m->q[i] = get_element(type); } - *n_p = n; - return a; + return m; } diff --git a/src/v/load.c b/src/v/load.c index c703b14..4ddac00 100644 --- a/src/v/load.c +++ b/src/v/load.c @@ -181,7 +181,7 @@ atcoords * acs_from_var(int n, mol * m, drawpars * dp){ acs->m = malloc(acs->Nmem*sizeof(atcoord *)); for(int i=0; im[i] = atcoord_fill(-1, m+i, NULL, dp->b, dp->center, dp->inertia, dp->bohr); + acs->m[i] = atcoord_fill(m+i, dp->b, dp->center, dp->inertia, dp->bohr); } fill_nf(acs, 0); diff --git a/src/v/v.h b/src/v/v.h index 8d834b2..69147e9 100644 --- a/src/v/v.h +++ b/src/v/v.h @@ -109,11 +109,6 @@ typedef struct { } drawpars; -typedef struct { - int t; - double r[3]; -} txyz; - // load.c atcoords * acs_from_var(int n, mol * m, drawpars * dp); @@ -125,11 +120,12 @@ double acs_scale(atcoords * acs); // mode_read.c modestr * mode_read(FILE * f, int na); // ac3_read*.c -atcoord * atcoord_fill(int n_, void * a, const char * fname, int b, int center, int inertia, int bohr); +int read_cart_atom(FILE * f, int n, mol * m); +atcoord * atcoord_fill(mol * m, int b, int center, int inertia, int bohr); atcoord * ac3_read(FILE * f, int b, int center, int inertia, int bohr, const char * fname, format_t * format); -txyz * ac3_read_in (int * n_p, int * zmat, FILE * f); -txyz * ac3_read_out(int * n_p, FILE * f); -txyz * ac3_read_xyz(int * n_p, FILE * f); +mol * ac3_read_in (FILE * f); +mol * ac3_read_out(FILE * f); +mol * ac3_read_xyz(FILE * f); // man.c void printman(FILE * f, char * exename); From d20e6b42c6d5ec2d78fa1aef0ca995c743930ccd Mon Sep 17 00:00:00 2001 From: Ksenia Date: Sun, 15 Mar 2026 15:12:46 +0100 Subject: [PATCH 08/18] Fix 8554864: Rewind on unsuccessful .out read If ac3_read_in() fail, the file is rewinded. Add this to ac3_read_out() so the order in ac3_read() won't matter --- src/v/ac3_read_out.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/v/ac3_read_out.c b/src/v/ac3_read_out.c index 0faf923..96365d5 100644 --- a/src/v/ac3_read_out.c +++ b/src/v/ac3_read_out.c @@ -4,11 +4,13 @@ mol * ac3_read_out(FILE * f){ + long pos0 = ftell(f); + // find where the molecule begins char s[STRLEN]; while(1){ if(!fgets(s, sizeof(s), f)){ - return NULL; + goto hell; } if(strstr(s, "Atomic Coordinates:")){ break; @@ -16,21 +18,24 @@ mol * ac3_read_out(FILE * f){ } // count atoms - long pos = ftell(f); + long pos1 = ftell(f); int n=0; while(read_cart_atom(f, n, NULL)){ n++; } if(!n){ - return NULL; + goto hell; } - fseek(f, pos, SEEK_SET); + fseek(f, pos1, SEEK_SET); // fill in mol * m = alloc_mol(n); for(int i=0; i Date: Fri, 13 Mar 2026 09:28:36 +0100 Subject: [PATCH 09/18] Refactor the obj/vibr object Merge vibr into acs (object) --- src/api.c | 4 +- src/v.c | 2 +- src/v/evr.c | 149 ++++++++++++++++++++++------------------------- src/v/evr.h | 84 +++++++++++++------------- src/v/headless.c | 8 +-- src/v/load.c | 40 +++++++------ src/v/loop.c | 2 +- src/v/scale.c | 2 +- src/v/tools.c | 12 ++-- src/v/v.h | 42 +++++++------ 10 files changed, 174 insertions(+), 171 deletions(-) diff --git a/src/api.c b/src/api.c index d81d201..d940d89 100644 --- a/src/api.c +++ b/src/api.c @@ -88,8 +88,8 @@ void PRINTOUT(FILE * f, char * format, ...){ } } -void * READ_FILES(drawpars * dp){ - void * ret; +object * READ_FILES(drawpars * dp){ + object * ret; if(!globals.inp_mols){ ret = read_files(dp); } diff --git a/src/v.c b/src/v.c index f3eee5f..136be99 100644 --- a/src/v.c +++ b/src/v.c @@ -80,7 +80,7 @@ int main (int argc, char * argv[]) { drawpars dp = cli_parse(argc, argv); - void * ent = READ_FILES(&dp); + object * ent = READ_FILES(&dp); if(!ent){ PRINT_ERR("no files to read\n"); diff --git a/src/v/evr.c b/src/v/evr.c index 0aa0ee6..6cb0525 100644 --- a/src/v/evr.c +++ b/src/v/evr.c @@ -10,8 +10,8 @@ static const double step_r = 1.1; static const double step_mod = 0.03125; static const double rl_move_pbc_scale = 0.9; -static void redraw_ac3(void * ent, drawpars * dp){ - atcoord * ac = ((atcoords *)ent)->m[dp->n]; +static void redraw_ac3(object * ent, drawpars * dp){ + atcoord * ac = ent->m[dp->n]; if(dp->b>0 && !ac->bond_flag){ bonds_fill(dp->rl, dp->bmax, ac); @@ -35,34 +35,33 @@ static void redraw_ac3(void * ent, drawpars * dp){ return; } -static void redraw_vibro(void * ent, drawpars * dp){ +static void redraw_vibro(object * ent, drawpars * dp){ - atcoord * ac = ((vibrstr *)ent)->ac; - double * m0 = ((vibrstr *)ent)->mode0; - modestr * ms = ((vibrstr *)ent)->modes; - double * m = ms->d + dp->n * ac->n*3; + atcoord * m = ent->m[0]; + double * r0 = ent->vib.mode0; + double * mode = ent->vib.modes->d + dp->n * m->n*3; - if(dp->b>0 && !ac->bond_flag){ - bonds_fill(dp->rl, dp->bmax, ac); - ac->bond_flag = 1; + if(dp->b>0 && !m->bond_flag){ + bonds_fill(dp->rl, dp->bmax, m); + m->bond_flag = 1; } - vecsums(ac->n*3, ac->r, m0, m, sin( dp->t * 2.0*M_PI/TMAX ) * 0.1*sqrt(ac->n) ); - for(int j=0; jn; j++){ + vecsums(m->n*3, m->r, r0, mode, sin( dp->t * 2.0*M_PI/TMAX ) * 0.1*sqrt(m->n) ); + for(int j=0; jn; j++){ double v[3]; - r3mx(v, ac->r+3*j, dp->ac3rmx); - r3cp(ac->r+3*j, v); + r3mx(v, m->r+3*j, dp->ac3rmx); + r3cp(m->r+3*j, v); } - ac3_draw(ac, dp->r, dp->scale, dp->xy0, dp->b, dp->num); - vibro_text(ms, dp); + ac3_draw(m, dp->r, dp->scale, dp->xy0, dp->b, dp->num); + vibro_text(ent->vib.modes, dp); return; } -void kp_readmore(void * ent, drawpars * dp){ +void kp_readmore(object * ent, drawpars * dp){ if(dp->task == AT3COORDS){ - atcoords * acs = ent; + object * acs = ent; if(!dp->f){ PRINT_ERR("cannot read from the file '%s'\n", dp->fname); return; @@ -75,7 +74,7 @@ void kp_readmore(void * ent, drawpars * dp){ return; } -void kp_readagain(void * ent, drawpars * dp){ +void kp_readagain(object * ent, drawpars * dp){ if(dp->task == AT3COORDS){ if(!dp->f || !(fclose(dp->f), dp->f = fopen(dp->fname, "r"))){ @@ -83,7 +82,7 @@ void kp_readagain(void * ent, drawpars * dp){ return; } - atcoords * acs = ent; + object * acs = ent; for(int i=0; in; i++){ free(acs->m[i]); } @@ -96,23 +95,23 @@ void kp_readagain(void * ent, drawpars * dp){ return; } -void kp_print(void * ent, drawpars * dp){ +void kp_print(object * ent, drawpars * dp){ if (dp->task == AT3COORDS){ - atcoord * ac = ((atcoords *)ent)->m[dp->n]; + atcoord * ac = ent->m[dp->n]; ac3_print(ac, dp->xy0, dp->b); } return; } -void kp_print_xyz(void * ent, drawpars * dp){ +void kp_print_xyz(object * ent, drawpars * dp){ if (dp->task == AT3COORDS){ - atcoord * ac = ((atcoords *)ent)->m[dp->n]; + atcoord * ac = ent->m[dp->n]; ac3_print_xyz(ac, dp->xy0); } return; } -void kp_printrot(void * ent __attribute__ ((unused)), drawpars * dp){ +void kp_printrot(object * ent __attribute__ ((unused)), drawpars * dp){ double * U = dp->ac3rmx; for(int i=0; i<3; i++){ PRINTOUT(stdout, "rotation> % 20.15lf % 20.15lf % 20.15lf\n", U[i*3], U[i*3+1], U[i*3+2]); @@ -122,7 +121,7 @@ void kp_printrot(void * ent __attribute__ ((unused)), drawpars * dp){ return; } -void kp_print2fig(void * ent, drawpars * dp){ +void kp_print2fig(object * ent, drawpars * dp){ if (dp->task == AT3COORDS){ double v[3*8]; if(dp->vert == 1){ @@ -130,27 +129,21 @@ void kp_print2fig(void * ent, drawpars * dp){ r3mx (v+3*i, dp->vertices+3*i, dp->ac3rmx); } } - atcoord * ac = ((atcoords *)ent)->m[dp->n]; + atcoord * ac = ent->m[dp->n]; ac3_print2fig(ac, dp->xy0, dp->b, dp->vert==1?v:NULL); } return; } -static void rl_changed(void * ent, drawpars * dp){ - if(dp->task==AT3COORDS){ - for(int i=0; iN; i++){ - ((atcoords *)ent)->m[i]->bond_flag = 0; - } - redraw_ac3(ent, dp); - } - else{ - ((vibrstr *)ent)->ac->bond_flag = 0; - redraw_vibro(ent, dp); +static void rl_changed(object * ent, drawpars * dp){ + for(int i=0; in; i++){ + ent->m[i]->bond_flag = 0; } + exp_redraw(ent, dp); return; } -void kp_rl_dec(void * ent, drawpars * dp){ +void kp_rl_dec(object * ent, drawpars * dp){ if(dp->b>0){ dp->rl /= step_r; rl_changed(ent, dp); @@ -158,7 +151,7 @@ void kp_rl_dec(void * ent, drawpars * dp){ return; } -void kp_rl_inc(void * ent, drawpars * dp){ +void kp_rl_inc(object * ent, drawpars * dp){ if(dp->b>0){ dp->rl *= step_r; rl_changed(ent, dp); @@ -166,31 +159,31 @@ void kp_rl_inc(void * ent, drawpars * dp){ return; } -void kp_r_dec(void * ent, drawpars * dp){ +void kp_r_dec(object * ent, drawpars * dp){ dp->r /= step_r; exp_redraw(ent, dp); return; } -void kp_r_inc(void * ent, drawpars * dp){ +void kp_r_inc(object * ent, drawpars * dp){ dp->r *= step_r; exp_redraw(ent, dp); return; } -void kp_zoom_out(void * ent, drawpars * dp){ +void kp_zoom_out(object * ent, drawpars * dp){ dp->scale /= step_zoom; exp_redraw(ent, dp); return; } -void kp_zoom_in(void * ent, drawpars * dp){ +void kp_zoom_in(object * ent, drawpars * dp){ dp->scale *= step_zoom; exp_redraw(ent, dp); return; } -void kp_frame_inc(void * ent, drawpars * dp){ +void kp_frame_inc(object * ent, drawpars * dp){ if (dp->n < dp->N-1){ dp->n++; exp_redraw(ent, dp); @@ -201,7 +194,7 @@ void kp_frame_inc(void * ent, drawpars * dp){ return; } -void kp_frame_dec(void * ent, drawpars * dp){ +void kp_frame_dec(object * ent, drawpars * dp){ if (dp->n > 0){ dp->n--; exp_redraw(ent, dp); @@ -212,7 +205,7 @@ void kp_frame_dec(void * ent, drawpars * dp){ return; } -void rot_ent_pointer(void * ent, drawpars * dp, int dx, int dy, double speed){ +void rot_ent_pointer(object * ent, drawpars * dp, int dx, int dy, double speed){ double rotation_matrix[9]; rot_around_perp(rotation_matrix, (double)dx, (double)dy, speed); @@ -221,7 +214,7 @@ void rot_ent_pointer(void * ent, drawpars * dp, int dx, int dy, double speed){ veccp(9, mx0, dp->ac3rmx); mx_multmx(3,3,3, dp->ac3rmx, rotation_matrix, mx0); if(dp->task == AT3COORDS){ - atcoords * acs = ent; + object * acs = ent; for(int i=0; iN; i++){ rot3d(acs->m[i]->n, acs->m[i]->r, rotation_matrix); } @@ -229,7 +222,7 @@ void rot_ent_pointer(void * ent, drawpars * dp, int dx, int dy, double speed){ return; } -static void rot_ent(void * ent, drawpars * dp, int axis, double angle){ +static void rot_ent(object * ent, drawpars * dp, int axis, double angle){ if(dp->modkey){ angle *= step_mod; } @@ -238,7 +231,7 @@ static void rot_ent(void * ent, drawpars * dp, int axis, double angle){ rotmx0_update(dp->ac3rmx, m, angle, axis); if(dp->task == AT3COORDS){ - atcoords * acs = ent; + object * acs = ent; for(int i=0; iN; i++){ rot3d(acs->m[i]->n, acs->m[i]->r, m); } @@ -246,37 +239,37 @@ static void rot_ent(void * ent, drawpars * dp, int axis, double angle){ return; } -void kp_rotx_l(void * ent, drawpars * dp){ +void kp_rotx_l(object * ent, drawpars * dp){ rot_ent(ent, dp, 0, +step_rot); exp_redraw(ent, dp); return; } -void kp_rotx_r(void * ent, drawpars * dp){ +void kp_rotx_r(object * ent, drawpars * dp){ rot_ent(ent, dp, 0, -step_rot); exp_redraw(ent, dp); return; } -void kp_roty_l(void * ent, drawpars * dp){ +void kp_roty_l(object * ent, drawpars * dp){ rot_ent(ent, dp, 1, +step_rot); exp_redraw(ent, dp); return; } -void kp_roty_r(void * ent, drawpars * dp){ +void kp_roty_r(object * ent, drawpars * dp){ rot_ent(ent, dp, 1, -step_rot); exp_redraw(ent, dp); return; } -void kp_rotz_l(void * ent, drawpars * dp){ +void kp_rotz_l(object * ent, drawpars * dp){ rot_ent(ent, dp, 2, +step_rot); exp_redraw(ent, dp); return; } -void kp_rotz_r(void * ent, drawpars * dp){ +void kp_rotz_r(object * ent, drawpars * dp){ rot_ent(ent, dp, 2, -step_rot); exp_redraw(ent, dp); return; @@ -302,7 +295,7 @@ static void mol2cell(double r0[3], drawpars * dp){ return; } -static void move_pbc(atcoords * acs, drawpars * dp, int dir, double d){ +static void move_pbc(object * acs, drawpars * dp, int dir, double d){ for(int i=0; iN; i++){ for(int j=0; jm[i]->n; j++){ double * r = acs->m[i]->r+j*3; @@ -317,7 +310,7 @@ static void move_pbc(atcoords * acs, drawpars * dp, int dir, double d){ return; } -static void move_ent(void * ent, drawpars * dp, int dir, double step){ +static void move_ent(object * ent, drawpars * dp, int dir, double step){ if(dp->modkey){ step *= step_mod; } @@ -330,50 +323,50 @@ static void move_ent(void * ent, drawpars * dp, int dir, double step){ return; } -void kp_move_l(void * ent, drawpars * dp){ +void kp_move_l(object * ent, drawpars * dp){ move_ent(ent, dp, 0, -step_move); exp_redraw(ent, dp); return; } -void kp_move_r(void * ent, drawpars * dp){ +void kp_move_r(object * ent, drawpars * dp){ move_ent(ent, dp, 0, +step_move); exp_redraw(ent, dp); return; } -void kp_move_u(void * ent, drawpars * dp){ +void kp_move_u(object * ent, drawpars * dp){ move_ent(ent, dp, 1, +step_move); exp_redraw(ent, dp); return; } -void kp_move_d(void * ent, drawpars * dp){ +void kp_move_d(object * ent, drawpars * dp){ move_ent(ent, dp, 1, -step_move); exp_redraw(ent, dp); return; } -void kp_exit(void * ent, drawpars * dp){ +void kp_exit(object * ent, drawpars * dp){ run_commands(NULL, dp->on_exit, dp, ent); ent_free(ent, dp); close_x(); dp->closed = 1; } -void kp_fw_toggle(void * ent __attribute__ ((unused)), drawpars * dp){ +void kp_fw_toggle(object * ent __attribute__ ((unused)), drawpars * dp){ dp->fbw = (dp->fbw == 1) ? 0 : 1; return; } -void kp_bw_toggle(void * ent __attribute__ ((unused)), drawpars * dp){ +void kp_bw_toggle(object * ent __attribute__ ((unused)), drawpars * dp){ if (dp->task == AT3COORDS){ dp->fbw = (dp->fbw == -1) ? 0 : -1; } return; } -void kp_l_toggle(void * ent, drawpars * dp){ +void kp_l_toggle(object * ent, drawpars * dp){ if(dp->b>0){ dp->b = 1+!(dp->b-1); exp_redraw(ent, dp); @@ -381,7 +374,7 @@ void kp_l_toggle(void * ent, drawpars * dp){ return; } -void kp_b_toggle(void * ent, drawpars * dp){ +void kp_b_toggle(object * ent, drawpars * dp){ if(dp->b>-1){ dp->b = !dp->b; } @@ -389,19 +382,19 @@ void kp_b_toggle(void * ent, drawpars * dp){ return; } -void kp_n_toggle(void * ent, drawpars * dp){ +void kp_n_toggle(object * ent, drawpars * dp){ dp->num = (dp->num == 1) ? 0 : 1; exp_redraw(ent, dp); return; } -void kp_t_toggle(void * ent, drawpars * dp){ +void kp_t_toggle(object * ent, drawpars * dp){ dp->num = (dp->num == -1) ? 0 : -1; exp_redraw(ent, dp); return; } -void kp_goto_last(void * ent, drawpars * dp){ +void kp_goto_last(object * ent, drawpars * dp){ if (dp->n < dp->N-1){ dp->n=dp->N-1; exp_redraw(ent, dp); @@ -409,7 +402,7 @@ void kp_goto_last(void * ent, drawpars * dp){ return; } -void kp_goto_1st(void * ent, drawpars * dp){ +void kp_goto_1st(object * ent, drawpars * dp){ if (dp->n > 0){ dp->n = 0; exp_redraw(ent, dp); @@ -417,7 +410,7 @@ void kp_goto_1st(void * ent, drawpars * dp){ return; } -void exp_redraw(void * ent, drawpars * dp){ +void exp_redraw(object * ent, drawpars * dp){ switch (dp->task){ case AT3COORDS: redraw_ac3(ent, dp); @@ -431,7 +424,7 @@ void exp_redraw(void * ent, drawpars * dp){ return; } -void time_gone(void * ent, drawpars * dp){ +void time_gone(object * ent, drawpars * dp){ if(dp->task == VIBRO){ if(dp->t >= TMAX){ dp->t = dp->t-TMAX; @@ -454,10 +447,10 @@ static void savevib(drawpars * dp, int c){ return; } -void kp_savepic(void * ent __attribute__ ((unused)), drawpars * dp){ +void kp_savepic(object * ent __attribute__ ((unused)), drawpars * dp){ char s[STRLEN]; int l = (int)(log10(dp->N+0.5))+1; - atcoord * ac = ((atcoords *)ent)->m[dp->n]; + atcoord * ac = ent->m[dp->n]; snprintf(s, sizeof(s), "%s_%0*d.xpm", ac->fname, l, dp->n+1); if (savepic(s) != XpmSuccess){ PRINT_ERR("cannot save '%s'\n", s); @@ -468,7 +461,7 @@ void kp_savepic(void * ent __attribute__ ((unused)), drawpars * dp){ return; } -void kp_film(void * ent, drawpars * dp){ +void kp_film(object * ent, drawpars * dp){ if(dp->task != VIBRO){ kp_savepic (ent, dp); while(dp->nN-1){ @@ -488,9 +481,9 @@ void kp_film(void * ent, drawpars * dp){ return; } -void kp_pg(void * ent, drawpars * dp){ +void kp_pg(object * ent, drawpars * dp){ if(dp->task == AT3COORDS){ - atcoord * ac = ((atcoords *)ent)->m[dp->n]; + atcoord * ac = ent->m[dp->n]; if(!ac->sym[0]){ pg(ac, dp->symtol); redraw_ac3(ent, dp); @@ -499,7 +492,7 @@ void kp_pg(void * ent, drawpars * dp){ return; } -void kp_jump(void * ent, drawpars * dp){ +void kp_jump(object * ent, drawpars * dp){ if(!dp->input){ dp->input = 1; exp_redraw(ent, dp); diff --git a/src/v/evr.h b/src/v/evr.h index 54cb720..949e32d 100644 --- a/src/v/evr.h +++ b/src/v/evr.h @@ -1,45 +1,45 @@ #include "3d.h" #define TMAX 20 -void kp_readmore (void * ent, drawpars * dp); -void kp_readagain(void * ent, drawpars * dp); -void kp_print (void * ent, drawpars * dp); -void kp_print_xyz(void * ent, drawpars * dp); -void kp_print2fig(void * ent, drawpars * dp); -void kp_printrot (void * ent, drawpars * dp); -void kp_rl_inc (void * ent, drawpars * dp); -void kp_rl_dec (void * ent, drawpars * dp); -void kp_r_inc (void * ent, drawpars * dp); -void kp_r_dec (void * ent, drawpars * dp); -void kp_zoom_out (void * ent, drawpars * dp); -void kp_zoom_in (void * ent, drawpars * dp); -void kp_lat_dec (void * ent, drawpars * dp); -void kp_lat_inc (void * ent, drawpars * dp); -void kp_frame_inc(void * ent, drawpars * dp); -void kp_frame_dec(void * ent, drawpars * dp); -void kp_rotz_l (void * ent, drawpars * dp); -void kp_rotz_r (void * ent, drawpars * dp); -void kp_roty_l (void * ent, drawpars * dp); -void kp_roty_r (void * ent, drawpars * dp); -void kp_rotx_l (void * ent, drawpars * dp); -void kp_rotx_r (void * ent, drawpars * dp); -void kp_move_l (void * ent, drawpars * dp); -void kp_move_r (void * ent, drawpars * dp); -void kp_move_u (void * ent, drawpars * dp); -void kp_move_d (void * ent, drawpars * dp); -void kp_exit (void * ent, drawpars * dp); -void kp_l_toggle (void * ent, drawpars * dp); -void kp_b_toggle (void * ent, drawpars * dp); -void kp_t_toggle (void * ent, drawpars * dp); -void kp_n_toggle (void * ent, drawpars * dp); -void kp_fw_toggle(void * ent, drawpars * dp); -void kp_bw_toggle(void * ent, drawpars * dp); -void kp_goto_last(void * ent, drawpars * dp); -void kp_goto_1st (void * ent, drawpars * dp); -void exp_redraw (void * ent, drawpars * dp); -void time_gone (void * ent, drawpars * dp); -void kp_savepic (void * ent, drawpars * dp); -void kp_film (void * ent, drawpars * dp); -void kp_pg (void * ent, drawpars * dp); -void kp_jump (void * ent, drawpars * dp); -void rot_ent_pointer(void * ent, drawpars * dp, int dx, int dy, double speed); +void kp_readmore (object * ent, drawpars * dp); +void kp_readagain(object * ent, drawpars * dp); +void kp_print (object * ent, drawpars * dp); +void kp_print_xyz(object * ent, drawpars * dp); +void kp_print2fig(object * ent, drawpars * dp); +void kp_printrot (object * ent, drawpars * dp); +void kp_rl_inc (object * ent, drawpars * dp); +void kp_rl_dec (object * ent, drawpars * dp); +void kp_r_inc (object * ent, drawpars * dp); +void kp_r_dec (object * ent, drawpars * dp); +void kp_zoom_out (object * ent, drawpars * dp); +void kp_zoom_in (object * ent, drawpars * dp); +void kp_lat_dec (object * ent, drawpars * dp); +void kp_lat_inc (object * ent, drawpars * dp); +void kp_frame_inc(object * ent, drawpars * dp); +void kp_frame_dec(object * ent, drawpars * dp); +void kp_rotz_l (object * ent, drawpars * dp); +void kp_rotz_r (object * ent, drawpars * dp); +void kp_roty_l (object * ent, drawpars * dp); +void kp_roty_r (object * ent, drawpars * dp); +void kp_rotx_l (object * ent, drawpars * dp); +void kp_rotx_r (object * ent, drawpars * dp); +void kp_move_l (object * ent, drawpars * dp); +void kp_move_r (object * ent, drawpars * dp); +void kp_move_u (object * ent, drawpars * dp); +void kp_move_d (object * ent, drawpars * dp); +void kp_exit (object * ent, drawpars * dp); +void kp_l_toggle (object * ent, drawpars * dp); +void kp_b_toggle (object * ent, drawpars * dp); +void kp_t_toggle (object * ent, drawpars * dp); +void kp_n_toggle (object * ent, drawpars * dp); +void kp_fw_toggle(object * ent, drawpars * dp); +void kp_bw_toggle(object * ent, drawpars * dp); +void kp_goto_last(object * ent, drawpars * dp); +void kp_goto_1st (object * ent, drawpars * dp); +void exp_redraw (object * ent, drawpars * dp); +void time_gone (object * ent, drawpars * dp); +void kp_savepic (object * ent, drawpars * dp); +void kp_film (object * ent, drawpars * dp); +void kp_pg (object * ent, drawpars * dp); +void kp_jump (object * ent, drawpars * dp); +void rot_ent_pointer(object * ent, drawpars * dp, int dx, int dy, double speed); diff --git a/src/v/headless.c b/src/v/headless.c index 8f4698f..f24dfd9 100644 --- a/src/v/headless.c +++ b/src/v/headless.c @@ -1,7 +1,7 @@ #include "v.h" #include "evr.h" -void run_commands(FILE * f, char * command, drawpars * dp, void * ent){ +void run_commands(FILE * f, char * command, drawpars * dp, object * ent){ char * com = command; char c; @@ -29,7 +29,7 @@ void run_commands(FILE * f, char * command, drawpars * dp, void * ent){ kp_print(ent, dp); break; case('.'): { - atcoord * ac = ((atcoords *)ent)->m[dp->n]; + atcoord * ac = ent->m[dp->n]; pg(ac, dp->symtol); PRINTOUT(stdout, "%s\n", ac->sym); }; break; @@ -47,8 +47,8 @@ void run_commands(FILE * f, char * command, drawpars * dp, void * ent){ return; } -int headless(drawpars * dp, void * ent){ - atcoord * ac = ((atcoords *)ent)->m[dp->n]; +int headless(drawpars * dp, object * ent){ + atcoord * ac = ent->m[dp->n]; if(dp->b>0 && !ac->bond_flag){ bonds_fill(dp->rl, dp->bmax, ac); } diff --git a/src/v/load.c b/src/v/load.c index 4ddac00..7983cc3 100644 --- a/src/v/load.c +++ b/src/v/load.c @@ -5,7 +5,7 @@ #define N_MIN 256 -static inline void fill_nf(atcoords * acs, int n0){ +static inline void fill_nf(object * acs, int n0){ for(int j=n0; jn; j++){ acs->m[j]->nf[0] = j-n0; acs->m[j]->nf[1] = acs->n-n0; @@ -13,7 +13,7 @@ static inline void fill_nf(atcoords * acs, int n0){ return; } -void acs_readmore(FILE * f, int b, int center, int inertia, int bohr, atcoords * acs, const char * fname){ +void acs_readmore(FILE * f, int b, int center, int inertia, int bohr, object * acs, const char * fname){ // needed to reset nf int n0 = acs->n; @@ -49,7 +49,7 @@ void acs_readmore(FILE * f, int b, int center, int inertia, int bohr, atcoords * return; } -static vibrstr * mode_read_try(FILE * f, atcoord * ac){ +static object * mode_read_try(FILE * f, atcoord * ac){ long pos = ftell(f); rewind(f); @@ -58,11 +58,13 @@ static vibrstr * mode_read_try(FILE * f, atcoord * ac){ modestr * modes = mode_read(f, n); if(modes){ - vibrstr * vib = malloc(sizeof(vibrstr) + sizeof(double)*n*3); - vib->ac = ac; - vib->modes = modes; - vib->mode0 = (double *)(vib + 1); - veccp(n*3, vib->mode0, ac->r); + object * vib = malloc(sizeof(object)); + vib->Nmem = vib->n = 1; + vib->m = malloc(vib->Nmem*sizeof(atcoord *)); + vib->m[0] = ac; + vib->vib.modes = modes; + vib->vib.mode0 = malloc(sizeof(double)*n*3); + veccp(n*3, vib->vib.mode0, ac->r); return vib; } else{ @@ -71,7 +73,7 @@ static vibrstr * mode_read_try(FILE * f, atcoord * ac){ } } -static FILE * acs_read_newfile(atcoords * acs, char * fname, drawpars * dp){ +static FILE * acs_read_newfile(object * acs, char * fname, drawpars * dp){ FILE * f; if(!strcmp(fname, "-")){ f = stdin; @@ -86,9 +88,9 @@ static FILE * acs_read_newfile(atcoords * acs, char * fname, drawpars * dp){ return f; } -static void * ent_read(char * fname, drawpars * dp){ +static object * ent_read(char * fname, drawpars * dp){ - atcoords * acs = malloc(sizeof(atcoords)); + object * acs = malloc(sizeof(object)); acs->Nmem = 0; acs->n = 0; acs->m = NULL; @@ -101,13 +103,13 @@ static void * ent_read(char * fname, drawpars * dp){ dp->fname = fname; if(dp->task==UNKNOWN || dp->task==VIBRO){ - vibrstr * vib = mode_read_try(f, acs->m[acs->n-1]); + object * vib = mode_read_try(f, acs->m[acs->n-1]); if(vib){ acs->n--; acs_free(acs); fclose(f); - dp->scale = ac3_scale(vib->ac); - dp->N = vib->modes->n; + dp->scale = ac3_scale(vib->m[0]); + dp->N = vib->vib.modes->n; dp->task = VIBRO; return vib; } @@ -123,11 +125,11 @@ static void * ent_read(char * fname, drawpars * dp){ return acs; } -void * read_files(drawpars * dp){ +object * read_files(drawpars * dp){ int fn = dp->input_files_n; char ** flist = dp->input_files; - void * ent = NULL; + object * ent = NULL; int i=0; // read the first available file @@ -138,7 +140,7 @@ void * read_files(drawpars * dp){ // if the first file does not contain normal modes, try to read other files if(ent && (dp->task == AT3COORDS)){ - atcoords * acs = ent; + object * acs = ent; int n0 = acs->n; for(i++; iinput_files_n; i++){ PRINT_WARN("ignoring file '%s'\n", dp->input_files[i]); @@ -176,7 +178,7 @@ atcoords * acs_from_var(int n, mol * m, drawpars * dp){ } dp->task = AT3COORDS; - atcoords * acs = malloc(sizeof(atcoords)); + object * acs = malloc(sizeof(object)); acs->Nmem = acs->n = n; acs->m = malloc(acs->Nmem*sizeof(atcoord *)); diff --git a/src/v/loop.c b/src/v/loop.c index ab56dec..68361d0 100644 --- a/src/v/loop.c +++ b/src/v/loop.c @@ -8,7 +8,7 @@ extern int W,H; extern Display * dis; extern Window win; -void main_loop(void * ent, drawpars * dp, ptf kp[NKP]){ +void main_loop(object * ent, drawpars * dp, ptf kp[NKP]){ // To handle window closing. Thanks to https://stackoverflow.com/a/1186544 Atom wm_delete_window = XInternAtom(dis, "WM_DELETE_WINDOW", False); diff --git a/src/v/scale.c b/src/v/scale.c index 18e6025..4721933 100644 --- a/src/v/scale.c +++ b/src/v/scale.c @@ -18,7 +18,7 @@ double ac3_scale(atcoord * ac){ return VIEWPORT_FILL / sqrt(d2max); } -double acs_scale(atcoords * acs){ +double acs_scale(object * acs){ double d2max = ac3_scale(acs->m[0]); for(int i=1; in; i++){ d2max = MIN(ac3_scale(acs->m[i]), d2max); diff --git a/src/v/tools.c b/src/v/tools.c index b6b1d7f..90e8ebb 100644 --- a/src/v/tools.c +++ b/src/v/tools.c @@ -1,11 +1,11 @@ #include "v.h" #include "sym.h" -void ent_free(void * ent, drawpars * dp){ +void ent_free(object * ent, drawpars * dp){ if (dp->task == VIBRO){ - free(((vibrstr *)ent)->ac); - free(((vibrstr *)ent)->modes); - free(ent); + free(ent->vib.mode0); + free(ent->vib.modes); + acs_free(ent); } else if (dp->task == AT3COORDS){ if(dp->f){ @@ -16,7 +16,7 @@ void ent_free(void * ent, drawpars * dp){ return; } -void acs_free(atcoords * acs){ +void acs_free(object * acs){ for(int i=0; in; i++){ free(acs->m[i]); } @@ -25,7 +25,7 @@ void acs_free(atcoords * acs){ return; } -void newmol_prep(atcoords * acs, drawpars * dp){ +void newmol_prep(object * acs, drawpars * dp){ for(int j=dp->N; jn; j++){ atcoord * ac = acs->m[j]; for(int i=0; in; i++){ diff --git a/src/v/v.h b/src/v/v.h index 69147e9..7c78329 100644 --- a/src/v/v.h +++ b/src/v/v.h @@ -41,11 +41,6 @@ typedef struct { int nf[2]; // number of molecule in file, file size } atcoord; -typedef struct { - int n, Nmem; - atcoord ** m; -} atcoords; - typedef struct { int n; double * f; @@ -55,9 +50,22 @@ typedef struct { typedef struct { modestr * modes; double * mode0; - atcoord * ac; } vibrstr; +typedef struct { +// +// object +// + int n, Nmem; + atcoord ** m; +// +// vibrstr +// +vibrstr vib; +// +} object; + + typedef struct { task_t task; // data type @@ -111,12 +119,12 @@ typedef struct { // load.c -atcoords * acs_from_var(int n, mol * m, drawpars * dp); -void acs_readmore (FILE * f, int b, int center, int inertia, int bohr, atcoords * acs, const char * fname); -void * read_files(drawpars * dp); +object * acs_from_var(int n, mol * m, drawpars * dp); +void acs_readmore (FILE * f, int b, int center, int inertia, int bohr, object * acs, const char * fname); +object * read_files(drawpars * dp); // scale.c double ac3_scale(atcoord * ac); -double acs_scale(atcoords * acs); +double acs_scale(object * acs); // mode_read.c modestr * mode_read(FILE * f, int na); // ac3_read*.c @@ -133,7 +141,7 @@ void printman(FILE * f, char * exename); drawpars cli_parse(int argc, char ** argv); // loop.c -void main_loop(void * ent, drawpars * dp, ptf kp[NKP]); +void main_loop(object * ent, drawpars * dp, ptf kp[NKP]); // ac3_draw.c void ac3_draw (atcoord * ac, double r0, double scale, double xy0[2], int b, int num); @@ -164,21 +172,21 @@ int savepic (char * s); int process_x_input(drawpars * dp, void * event); // tools.c -void ent_free(void * ent, drawpars * dp); -void acs_free(atcoords * acs); -void newmol_prep(atcoords * acs, drawpars * dp); +void ent_free(object * ent, drawpars * dp); +void acs_free(object * acs); +void newmol_prep(object * acs, drawpars * dp); void ac3_text(atcoord * ac, drawpars * dp); void vibro_text(modestr * ms, drawpars * dp); void pg(atcoord * a, double symtol); // headless.c -void run_commands(FILE * f, char * command, drawpars * dp, void * ent); -int headless(drawpars * dp, void * ent); +void run_commands(FILE * f, char * command, drawpars * dp, object * ent); +int headless(drawpars * dp, object * ent); // main.c int main (int argc, char * argv[]); // api.c void PRINTOUT(FILE * f, char * format, ...); -void * READ_FILES(drawpars * dp); +object * READ_FILES(drawpars * dp); int SHOULD_PRINT_MAN(int argc); From 49c08df34621ac95142b802d9d4eb376cedb1f19 Mon Sep 17 00:00:00 2001 From: Ksenia Date: Fri, 13 Mar 2026 10:57:40 +0100 Subject: [PATCH 10/18] Refactor the coord/vibr object simplify vibrstr (vibr_t) --- src/v/evr.c | 17 ++++++++++------- src/v/headless.c | 5 ++++- src/v/load.c | 25 +++++++++++++------------ src/v/mode_read.c | 19 ++++++++++--------- src/v/tools.c | 30 +++++++++--------------------- src/v/v.h | 29 ++++++++--------------------- 6 files changed, 54 insertions(+), 71 deletions(-) diff --git a/src/v/evr.c b/src/v/evr.c index 6cb0525..a1da900 100644 --- a/src/v/evr.c +++ b/src/v/evr.c @@ -37,16 +37,16 @@ static void redraw_ac3(object * ent, drawpars * dp){ static void redraw_vibro(object * ent, drawpars * dp){ - atcoord * m = ent->m[0]; - double * r0 = ent->vib.mode0; - double * mode = ent->vib.modes->d + dp->n * m->n*3; + atcoord * m = ent->m[0]; + double * r0 = ent->vib->r0; + double * dr = ent->vib->disp + dp->n * m->n*3; if(dp->b>0 && !m->bond_flag){ bonds_fill(dp->rl, dp->bmax, m); m->bond_flag = 1; } - vecsums(m->n*3, m->r, r0, mode, sin( dp->t * 2.0*M_PI/TMAX ) * 0.1*sqrt(m->n) ); + vecsums(m->n*3, m->r, r0, dr, sin( dp->t * 2.0*M_PI/TMAX ) * 0.1*sqrt(m->n) ); for(int j=0; jn; j++){ double v[3]; r3mx(v, m->r+3*j, dp->ac3rmx); @@ -54,7 +54,7 @@ static void redraw_vibro(object * ent, drawpars * dp){ } ac3_draw(m, dp->r, dp->scale, dp->xy0, dp->b, dp->num); - vibro_text(ent->vib.modes, dp); + vibro_text(ent->vib, dp); return; } @@ -349,9 +349,12 @@ void kp_move_d(object * ent, drawpars * dp){ void kp_exit(object * ent, drawpars * dp){ run_commands(NULL, dp->on_exit, dp, ent); - ent_free(ent, dp); - close_x(); + obj_free(ent); + if(dp->f){ + fclose(dp->f); + } dp->closed = 1; + close_x(); } void kp_fw_toggle(object * ent __attribute__ ((unused)), drawpars * dp){ diff --git a/src/v/headless.c b/src/v/headless.c index f24dfd9..8ee8d96 100644 --- a/src/v/headless.c +++ b/src/v/headless.c @@ -53,6 +53,9 @@ int headless(drawpars * dp, object * ent){ bonds_fill(dp->rl, dp->bmax, ac); } run_commands(stdin, dp->com, dp, ent); - ent_free(ent, dp); + obj_free(ent); + if(dp->f){ + fclose(dp->f); + } return 0; } diff --git a/src/v/load.c b/src/v/load.c index 7983cc3..c751ec0 100644 --- a/src/v/load.c +++ b/src/v/load.c @@ -35,7 +35,7 @@ void acs_readmore(FILE * f, int b, int center, int inertia, int bohr, object * a int N = acs->Nmem ? acs->Nmem*2 : N_MIN; atcoord ** ms = realloc(acs->m, N*sizeof(atcoord *)); if(!ms){ - acs_free(acs); + obj_free(acs); free(m); PRINT_ERR("cannot reallocate memory\n"); abort(); @@ -55,17 +55,16 @@ static object * mode_read_try(FILE * f, atcoord * ac){ rewind(f); int n = ac->n; - modestr * modes = mode_read(f, n); + vibr_t * modes = mode_read(f, n); if(modes){ - object * vib = malloc(sizeof(object)); - vib->Nmem = vib->n = 1; - vib->m = malloc(vib->Nmem*sizeof(atcoord *)); - vib->m[0] = ac; - vib->vib.modes = modes; - vib->vib.mode0 = malloc(sizeof(double)*n*3); - veccp(n*3, vib->vib.mode0, ac->r); - return vib; + object * ent = malloc(sizeof(object)); + ent->Nmem = ent->n = 1; + ent->m = malloc(ent->Nmem*sizeof(atcoord *)); + ent->m[0] = ac; + ent->vib = modes; + veccp(n*3, ent->vib->r0, ac->r); + return ent; } else{ fseek(f, pos, SEEK_SET); @@ -94,6 +93,7 @@ static object * ent_read(char * fname, drawpars * dp){ acs->Nmem = 0; acs->n = 0; acs->m = NULL; + acs->vib = NULL; FILE * f = acs_read_newfile(acs, fname, dp); if(!f || !acs->n){ @@ -106,10 +106,10 @@ static object * ent_read(char * fname, drawpars * dp){ object * vib = mode_read_try(f, acs->m[acs->n-1]); if(vib){ acs->n--; - acs_free(acs); + obj_free(acs); fclose(f); dp->scale = ac3_scale(vib->m[0]); - dp->N = vib->vib.modes->n; + dp->N = vib->vib->n; dp->task = VIBRO; return vib; } @@ -181,6 +181,7 @@ object * acs_from_var(int n, mol * m, drawpars * dp){ object * acs = malloc(sizeof(object)); acs->Nmem = acs->n = n; acs->m = malloc(acs->Nmem*sizeof(atcoord *)); + acs->vib = NULL; for(int i=0; im[i] = atcoord_fill(m+i, dp->b, dp->center, dp->inertia, dp->bohr); diff --git a/src/v/mode_read.c b/src/v/mode_read.c index 8b6569c..9854aa9 100644 --- a/src/v/mode_read.c +++ b/src/v/mode_read.c @@ -1,6 +1,6 @@ #include "v.h" -static int readb(FILE * f, int i, int Nmax, int N, int na, modestr * m){ +static int readb(FILE * f, int i, int Nmax, int N, int na, vibr_t * m){ double d; char s[STRLEN]; int t,k,j; @@ -21,7 +21,7 @@ static int readb(FILE * f, int i, int Nmax, int N, int na, modestr * m){ if(s[1]=='i'){ d = -d; } - m->f[i*Nmax+j] = d; + m->freq[i*Nmax+j] = d; } if (!fgets(s, sizeof(s), f)) { @@ -41,7 +41,7 @@ static int readb(FILE * f, int i, int Nmax, int N, int na, modestr * m){ if (fscanf (f, "%lf", &d) != 1) { return -1; } - m->d[3*na*(i*Nmax+j)+k] = d; + m->disp[3*na*(i*Nmax+j)+k] = d; } } #if 0 @@ -55,7 +55,7 @@ static int readb(FILE * f, int i, int Nmax, int N, int na, modestr * m){ return 0; } -modestr * mode_read (FILE * f, int na){ +vibr_t * mode_read (FILE * f, int na){ const int N = 6; int n = 0; @@ -84,11 +84,12 @@ modestr * mode_read (FILE * f, int na){ } } - size_t size = sizeof(modestr) + n*sizeof(double) + n*na*3*sizeof(double); - modestr * m = malloc(size); - m->n = n; - m->f = (double *)(m+1); - m->d = m->f+n; + size_t size = sizeof(vibr_t) + n*sizeof(double) + n*na*3*sizeof(double) + na*3*sizeof(double); + vibr_t * m = malloc(size); + m->n = n; + m->freq = (double *)(m+1); + m->r0 = m->freq+n; + m->disp = m->r0+3*na; for (int i=0; i<3; i++){ if (!fgets(s, sizeof(s), f)) { diff --git a/src/v/tools.c b/src/v/tools.c index 90e8ebb..fceafb0 100644 --- a/src/v/tools.c +++ b/src/v/tools.c @@ -1,27 +1,15 @@ #include "v.h" #include "sym.h" -void ent_free(object * ent, drawpars * dp){ - if (dp->task == VIBRO){ - free(ent->vib.mode0); - free(ent->vib.modes); - acs_free(ent); +void obj_free(object * ent){ + if(ent->vib){ + free(ent->vib); } - else if (dp->task == AT3COORDS){ - if(dp->f){ - fclose(dp->f); - } - acs_free(ent); - } - return; -} - -void acs_free(object * acs){ - for(int i=0; in; i++){ - free(acs->m[i]); + for(int i=0; in; i++){ + free(ent->m[i]); } - free(acs->m); - free(acs); + free(ent->m); + free(ent); return; } @@ -68,9 +56,9 @@ void ac3_text(atcoord * ac, drawpars * dp){ return; } -void vibro_text(modestr * ms, drawpars * dp){ +void vibro_text(vibr_t * ms, drawpars * dp){ char text[STRLEN]; - double fq = ms->f[dp->n]; + double fq = ms->freq[dp->n]; char i = fq > 0.0 ? ' ' : 'i'; snprintf(text, sizeof(text), "%*d / %d %.1lf%c r = %.1lf rl = %.1lf", diff --git a/src/v/v.h b/src/v/v.h index 7c78329..ee362d1 100644 --- a/src/v/v.h +++ b/src/v/v.h @@ -43,29 +43,17 @@ typedef struct { typedef struct { int n; - double * f; - double * d; -} modestr; + double * freq; + double * disp; + double * r0; +} vibr_t; typedef struct { - modestr * modes; - double * mode0; -} vibrstr; - -typedef struct { -// -// object -// int n, Nmem; atcoord ** m; -// -// vibrstr -// -vibrstr vib; -// + vibr_t * vib; } object; - typedef struct { task_t task; // data type @@ -126,7 +114,7 @@ object * read_files(drawpars * dp); double ac3_scale(atcoord * ac); double acs_scale(object * acs); // mode_read.c -modestr * mode_read(FILE * f, int na); +vibr_t * mode_read(FILE * f, int na); // ac3_read*.c int read_cart_atom(FILE * f, int n, mol * m); atcoord * atcoord_fill(mol * m, int b, int center, int inertia, int bohr); @@ -172,11 +160,10 @@ int savepic (char * s); int process_x_input(drawpars * dp, void * event); // tools.c -void ent_free(object * ent, drawpars * dp); -void acs_free(object * acs); +void obj_free(object * ent); void newmol_prep(object * acs, drawpars * dp); void ac3_text(atcoord * ac, drawpars * dp); -void vibro_text(modestr * ms, drawpars * dp); +void vibro_text(vibr_t * ms, drawpars * dp); void pg(atcoord * a, double symtol); // headless.c From 664dd3d61d9ba8a34e97c8bf2a9c628f5d04cdb7 Mon Sep 17 00:00:00 2001 From: Ksenia Date: Fri, 13 Mar 2026 11:32:52 +0100 Subject: [PATCH 11/18] Cleanup structures with flexible array members --- src/mol/common.h | 2 ++ src/mol/mol.h | 9 ++++++--- src/sym/pointgroup.c | 19 +++++++++++++------ src/sym/sym.h | 8 ++++---- src/v/ac3_read.c | 10 ++++------ src/v/mode_read.c | 20 ++++++++++++++------ 6 files changed, 43 insertions(+), 25 deletions(-) diff --git a/src/mol/common.h b/src/mol/common.h index 8436896..5873029 100644 --- a/src/mol/common.h +++ b/src/mol/common.h @@ -6,6 +6,8 @@ #define BA 0.5291772 #define AB 1.88972616356109068947 +#define MEM_END(S,X) ( (S)->X + (X##_size)/sizeof(*((S)->X)) ) + #define MAX(x,y) ( ((x) > (y)) ? (x) : (y) ) #define MIN(x,y) ( ((x) < (y)) ? (x) : (y) ) diff --git a/src/mol/mol.h b/src/mol/mol.h index bc03478..eb2ae76 100644 --- a/src/mol/mol.h +++ b/src/mol/mol.h @@ -11,10 +11,13 @@ typedef struct { } mol; static inline mol * alloc_mol(int n){ - mol * m = calloc(sizeof(mol)+3*n*sizeof(double)+n*sizeof(int), 1); + size_t r_size = sizeof(double) * n*3; + size_t q_size = sizeof(int ) * n; + mol * m = calloc(sizeof(mol)+r_size+q_size, 1); + m->r = (double *) (m + 1); + m->q = (int *) MEM_END(m, r); + m->name = NULL; m->n = n; - m->r = (double *)(m + 1); - m->q = (int *)(m->r + 3*n); return m; } diff --git a/src/sym/pointgroup.c b/src/sym/pointgroup.c index c6f5ec3..49d61d1 100644 --- a/src/sym/pointgroup.c +++ b/src/sym/pointgroup.c @@ -1,5 +1,17 @@ #include "sym.h" +static inline molsym * alloc_molsym(int a, int mssize){ + size_t r_size = sizeof(double) * mssize*3; + size_t o_size = sizeof(int ) * mssize; + size_t e_size = sizeof(elsym ) * mssize; + molsym * ms = calloc(sizeof(molsym)+ e_size + o_size + r_size, 1); + ms->r = (double *) (ms+1); + ms->o = (int *) MEM_END(ms,r); + ms->e = (elsym *) MEM_END(ms,o); + ms->a = a; + return ms; +} + static inline int isnew(int N, double * R, double r[3], double eps2){ for(int i=0; ie[(I)], MS->o[(I)], MS->r[(I)*3], MS->r[(I)*3+1], MS->r[(I)*3+2]); #define MSSIZE 4 - molsym * ms = malloc(sizeof(molsym)+MSSIZE*(sizeof(elsym)+sizeof(int)+3*sizeof(double))); - ms->a = m->n; - ms->n = 0; - ms->r = (double *)(ms+1); - ms->o = (int *)(ms->r + MSSIZE*3); - ms->e = (elsym *)(ms->o + MSSIZE); + molsym * ms = alloc_molsym(m->n, MSSIZE); double d[3]; position(m, d, 0); diff --git a/src/sym/sym.h b/src/sym/sym.h index b933b91..ab50289 100644 --- a/src/sym/sym.h +++ b/src/sym/sym.h @@ -10,12 +10,12 @@ typedef enum {INV, SIGMA, CN, SN} elsym; typedef struct { - styp s; + double * r; // vectors associated with generators + int * o; // orders of generators + elsym * e; // types of generators + styp s; // point group int a; // number of atoms int n; // number of group generators - elsym * e; // types of generators - int * o; // orders of generators - double * r; // vectors associated with generators } molsym; molsym * pointgroup(mol * m, double eps); diff --git a/src/v/ac3_read.c b/src/v/ac3_read.c index 634f76c..06330b2 100644 --- a/src/v/ac3_read.c +++ b/src/v/ac3_read.c @@ -2,8 +2,6 @@ #include "vecn.h" #include "vec3.h" -#define END(S,X) ( (S)->X + (X##_size)/sizeof(*((S)->X)) ) - atcoord * atcoord_fill(mol * m0, int b, int center, int inertia, int bohr){ int n = m0->n; @@ -18,9 +16,9 @@ atcoord * atcoord_fill(mol * m0, int b, int center, int inertia, int bohr){ m = malloc(size); m->n = n; m->r = (double *) (m + 1); - m->bond_r = (double *) END(m,r); - m->q = (int *) END(m,bond_r); - m->bond_a = (int *) END(m,q); + m->bond_r = (double *) MEM_END(m,r); + m->q = (int *) MEM_END(m,bond_r); + m->bond_a = (int *) MEM_END(m,q); m->bond_flag = 0; m->bond_rl = 0.0; } @@ -30,7 +28,7 @@ atcoord * atcoord_fill(mol * m0, int b, int center, int inertia, int bohr){ m->n = n; m->r = (double *) (m + 1); m->bond_r = NULL; - m->q = (int *) END(m,r); + m->q = (int *) MEM_END(m,r); m->bond_a = NULL; m->bond_flag = -1; m->bond_rl = 0.0; diff --git a/src/v/mode_read.c b/src/v/mode_read.c index 9854aa9..4cf2f11 100644 --- a/src/v/mode_read.c +++ b/src/v/mode_read.c @@ -1,5 +1,18 @@ #include "v.h" +static inline vibr_t * make_vibr_t(int n_modes, int n_atoms){ + size_t freq_size = sizeof(double) * n_modes; + size_t r0_size = sizeof(double) * n_atoms*3; + size_t disp_size = sizeof(double) * n_modes*n_atoms*3; + size_t size = sizeof(vibr_t) + freq_size + r0_size + disp_size; + vibr_t * v = malloc(size); + v->n = n_modes; + v->freq = (double *) (v + 1); + v->disp = (double *) MEM_END(v,freq); + v->r0 = (double *) MEM_END(v,disp); + return v; +} + static int readb(FILE * f, int i, int Nmax, int N, int na, vibr_t * m){ double d; char s[STRLEN]; @@ -84,12 +97,7 @@ vibr_t * mode_read (FILE * f, int na){ } } - size_t size = sizeof(vibr_t) + n*sizeof(double) + n*na*3*sizeof(double) + na*3*sizeof(double); - vibr_t * m = malloc(size); - m->n = n; - m->freq = (double *)(m+1); - m->r0 = m->freq+n; - m->disp = m->r0+3*na; + vibr_t * m = make_vibr_t(n, na); for (int i=0; i<3; i++){ if (!fgets(s, sizeof(s), f)) { From 0af719402e3864641ab0ff5fa4e74078680a90bf Mon Sep 17 00:00:00 2001 From: Ksenia Date: Sat, 14 Mar 2026 21:07:16 +0100 Subject: [PATCH 12/18] Refactor parameters (dp) structure and initialization Fix memory leak in `j`ump input --- obj/api.d | 2 +- obj/v.d | 3 +- obj/v/ac3_draw.d | 2 +- obj/v/ac3_print.d | 2 +- obj/v/ac3_read.d | 3 +- obj/v/ac3_read_in.d | 4 +- obj/v/ac3_read_out.d | 2 +- obj/v/ac3_read_xyz.d | 2 +- obj/v/bonds.d | 2 +- obj/v/cli.d | 4 +- obj/v/evr.d | 4 +- obj/v/get_atpar.d | 2 +- obj/v/headless.d | 2 +- obj/v/load.d | 2 +- obj/v/loop.d | 4 +- obj/v/man.d | 2 +- obj/v/mode_read.d | 2 +- obj/v/scale.d | 2 +- obj/v/tools.d | 4 +- obj/v/x.d | 2 +- obj/v/xinput.d | 2 +- src/api.c | 9 +- src/mol/common.h | 3 + src/v.c | 23 ++-- src/v/ac3_draw.c | 22 ++-- src/v/ac3_print.c | 26 ++-- src/v/ac3_read.c | 17 +-- src/v/bonds.c | 20 +-- src/v/cli.c | 288 ++++++++++++++++++++++--------------------- src/v/evr.c | 138 ++++++++++----------- src/v/headless.c | 14 +-- src/v/load.c | 48 ++++---- src/v/loop.c | 30 ++--- src/v/pars.h | 95 ++++++++++++++ src/v/tools.c | 20 +-- src/v/v.h | 90 +++----------- src/v/x.c | 4 +- src/v/xinput.c | 13 +- 38 files changed, 485 insertions(+), 429 deletions(-) create mode 100644 src/v/pars.h diff --git a/obj/api.d b/obj/api.d index 87cc184..dc48b93 100644 --- a/obj/api.d +++ b/obj/api.d @@ -1,2 +1,2 @@ obj/api.o obj-pic/api.o: src/api.c src/v/v.h src/mol/mol.h \ - src/mol/common.h + src/mol/common.h src/v/pars.h diff --git a/obj/v.d b/obj/v.d index e1d7b69..f0ff6dc 100644 --- a/obj/v.d +++ b/obj/v.d @@ -1,2 +1,3 @@ obj/v.o obj-pic/v.o: src/v.c src/v/v.h src/mol/mol.h src/mol/common.h \ - src/v/x.h src/v/evr.h src/math/3d.h src/math/matrix.h src/math/vecn.h + src/v/pars.h src/v/x.h src/v/evr.h src/math/3d.h src/math/matrix.h \ + src/math/vecn.h diff --git a/obj/v/ac3_draw.d b/obj/v/ac3_draw.d index 83c907e..ac2058d 100644 --- a/obj/v/ac3_draw.d +++ b/obj/v/ac3_draw.d @@ -1,2 +1,2 @@ obj/v/ac3_draw.o obj-pic/v/ac3_draw.o: src/v/ac3_draw.c src/v/v.h \ - src/mol/mol.h src/mol/common.h src/v/x.h + src/mol/mol.h src/mol/common.h src/v/pars.h src/v/x.h diff --git a/obj/v/ac3_print.d b/obj/v/ac3_print.d index e95987c..197fbc5 100644 --- a/obj/v/ac3_print.d +++ b/obj/v/ac3_print.d @@ -1,2 +1,2 @@ obj/v/ac3_print.o obj-pic/v/ac3_print.o: src/v/ac3_print.c src/v/v.h \ - src/mol/mol.h src/mol/common.h + src/mol/mol.h src/mol/common.h src/v/pars.h diff --git a/obj/v/ac3_read.d b/obj/v/ac3_read.d index 9fc7a3a..08c3d55 100644 --- a/obj/v/ac3_read.d +++ b/obj/v/ac3_read.d @@ -1,2 +1,3 @@ obj/v/ac3_read.o obj-pic/v/ac3_read.o: src/v/ac3_read.c src/v/v.h \ - src/mol/mol.h src/mol/common.h src/math/vecn.h src/math/vec3.h + src/mol/mol.h src/mol/common.h src/v/pars.h src/math/vecn.h \ + src/math/vec3.h diff --git a/obj/v/ac3_read_in.d b/obj/v/ac3_read_in.d index 4ee71dd..6dcdbcc 100644 --- a/obj/v/ac3_read_in.d +++ b/obj/v/ac3_read_in.d @@ -1,3 +1,3 @@ obj/v/ac3_read_in.o obj-pic/v/ac3_read_in.o: src/v/ac3_read_in.c \ - src/v/v.h src/mol/mol.h src/mol/common.h src/math/3d.h src/math/matrix.h \ - src/math/vecn.h src/math/vec3.h + src/v/v.h src/mol/mol.h src/mol/common.h src/v/pars.h src/math/3d.h \ + src/math/matrix.h src/math/vecn.h src/math/vec3.h diff --git a/obj/v/ac3_read_out.d b/obj/v/ac3_read_out.d index e2ff908..d0f0c45 100644 --- a/obj/v/ac3_read_out.d +++ b/obj/v/ac3_read_out.d @@ -1,2 +1,2 @@ obj/v/ac3_read_out.o obj-pic/v/ac3_read_out.o: src/v/ac3_read_out.c \ - src/v/v.h src/mol/mol.h src/mol/common.h src/math/vec3.h + src/v/v.h src/mol/mol.h src/mol/common.h src/v/pars.h src/math/vec3.h diff --git a/obj/v/ac3_read_xyz.d b/obj/v/ac3_read_xyz.d index e4c8c36..9faf7c0 100644 --- a/obj/v/ac3_read_xyz.d +++ b/obj/v/ac3_read_xyz.d @@ -1,2 +1,2 @@ obj/v/ac3_read_xyz.o obj-pic/v/ac3_read_xyz.o: src/v/ac3_read_xyz.c \ - src/v/v.h src/mol/mol.h src/mol/common.h + src/v/v.h src/mol/mol.h src/mol/common.h src/v/pars.h diff --git a/obj/v/bonds.d b/obj/v/bonds.d index d6da7fd..33d0df6 100644 --- a/obj/v/bonds.d +++ b/obj/v/bonds.d @@ -1,2 +1,2 @@ obj/v/bonds.o obj-pic/v/bonds.o: src/v/bonds.c src/v/v.h src/mol/mol.h \ - src/mol/common.h src/math/vec3.h + src/mol/common.h src/v/pars.h src/math/vec3.h diff --git a/obj/v/cli.d b/obj/v/cli.d index 70cb4ee..b83ea3a 100644 --- a/obj/v/cli.d +++ b/obj/v/cli.d @@ -1,3 +1,3 @@ obj/v/cli.o obj-pic/v/cli.o: src/v/cli.c src/v/v.h src/mol/mol.h \ - src/mol/common.h src/math/vecn.h src/math/matrix.h src/math/vecn.h \ - src/math/vec3.h + src/mol/common.h src/v/pars.h src/math/vecn.h src/math/matrix.h \ + src/math/vecn.h src/math/vec3.h diff --git a/obj/v/evr.d b/obj/v/evr.d index 36e63cb..540e7cf 100644 --- a/obj/v/evr.d +++ b/obj/v/evr.d @@ -1,3 +1,3 @@ obj/v/evr.o obj-pic/v/evr.o: src/v/evr.c src/v/v.h src/mol/mol.h \ - src/mol/common.h src/v/x.h src/v/evr.h src/math/3d.h src/math/matrix.h \ - src/math/vecn.h src/math/vec3.h + src/mol/common.h src/v/pars.h src/v/x.h src/v/evr.h src/math/3d.h \ + src/math/matrix.h src/math/vecn.h src/math/vec3.h diff --git a/obj/v/get_atpar.d b/obj/v/get_atpar.d index b57c45a..cbf981f 100644 --- a/obj/v/get_atpar.d +++ b/obj/v/get_atpar.d @@ -1,2 +1,2 @@ obj/v/get_atpar.o obj-pic/v/get_atpar.o: src/v/get_atpar.c src/v/v.h \ - src/mol/mol.h src/mol/common.h src/mol/elements.h + src/mol/mol.h src/mol/common.h src/v/pars.h src/mol/elements.h diff --git a/obj/v/headless.d b/obj/v/headless.d index f635684..3048fee 100644 --- a/obj/v/headless.d +++ b/obj/v/headless.d @@ -1,3 +1,3 @@ obj/v/headless.o obj-pic/v/headless.o: src/v/headless.c src/v/v.h \ - src/mol/mol.h src/mol/common.h src/v/evr.h src/math/3d.h \ + src/mol/mol.h src/mol/common.h src/v/pars.h src/v/evr.h src/math/3d.h \ src/math/matrix.h src/math/vecn.h diff --git a/obj/v/load.d b/obj/v/load.d index e92d472..ea515ec 100644 --- a/obj/v/load.d +++ b/obj/v/load.d @@ -1,2 +1,2 @@ obj/v/load.o obj-pic/v/load.o: src/v/load.c src/v/v.h src/mol/mol.h \ - src/mol/common.h src/math/vec3.h src/math/vecn.h + src/mol/common.h src/v/pars.h src/math/vec3.h src/math/vecn.h diff --git a/obj/v/loop.d b/obj/v/loop.d index 24176d7..4af8815 100644 --- a/obj/v/loop.d +++ b/obj/v/loop.d @@ -1,3 +1,3 @@ obj/v/loop.o obj-pic/v/loop.o: src/v/loop.c src/v/v.h src/mol/mol.h \ - src/mol/common.h src/v/x.h src/v/evr.h src/math/3d.h src/math/matrix.h \ - src/math/vecn.h + src/mol/common.h src/v/pars.h src/v/x.h src/v/evr.h src/math/3d.h \ + src/math/matrix.h src/math/vecn.h diff --git a/obj/v/man.d b/obj/v/man.d index a7667c4..67a241e 100644 --- a/obj/v/man.d +++ b/obj/v/man.d @@ -1,2 +1,2 @@ obj/v/man.o obj-pic/v/man.o: src/v/man.c src/v/v.h src/mol/mol.h \ - src/mol/common.h + src/mol/common.h src/v/pars.h diff --git a/obj/v/mode_read.d b/obj/v/mode_read.d index c51ab23..f3a3c1c 100644 --- a/obj/v/mode_read.d +++ b/obj/v/mode_read.d @@ -1,2 +1,2 @@ obj/v/mode_read.o obj-pic/v/mode_read.o: src/v/mode_read.c src/v/v.h \ - src/mol/mol.h src/mol/common.h + src/mol/mol.h src/mol/common.h src/v/pars.h diff --git a/obj/v/scale.d b/obj/v/scale.d index 5dd9798..c7301cb 100644 --- a/obj/v/scale.d +++ b/obj/v/scale.d @@ -1,2 +1,2 @@ obj/v/scale.o obj-pic/v/scale.o: src/v/scale.c src/v/v.h src/mol/mol.h \ - src/mol/common.h src/math/vec3.h + src/mol/common.h src/v/pars.h src/math/vec3.h diff --git a/obj/v/tools.d b/obj/v/tools.d index 6e83f5a..5a2c5d4 100644 --- a/obj/v/tools.d +++ b/obj/v/tools.d @@ -1,3 +1,3 @@ obj/v/tools.o obj-pic/v/tools.o: src/v/tools.c src/v/v.h src/mol/mol.h \ - src/mol/common.h src/sym/sym.h src/math/vec3.h src/math/3d.h \ - src/math/matrix.h src/math/vecn.h + src/mol/common.h src/v/pars.h src/sym/sym.h src/math/vec3.h \ + src/math/3d.h src/math/matrix.h src/math/vecn.h diff --git a/obj/v/x.d b/obj/v/x.d index ae44fe9..c409187 100644 --- a/obj/v/x.d +++ b/obj/v/x.d @@ -1,2 +1,2 @@ obj/v/x.o obj-pic/v/x.o: src/v/x.c src/v/v.h src/mol/mol.h \ - src/mol/common.h src/v/x.h + src/mol/common.h src/v/pars.h src/v/x.h diff --git a/obj/v/xinput.d b/obj/v/xinput.d index 7f4499a..069e87e 100644 --- a/obj/v/xinput.d +++ b/obj/v/xinput.d @@ -1,2 +1,2 @@ obj/v/xinput.o obj-pic/v/xinput.o: src/v/xinput.c src/v/v.h src/mol/mol.h \ - src/mol/common.h src/v/x.h + src/mol/common.h src/v/pars.h src/v/x.h diff --git a/src/api.c b/src/api.c index d940d89..563585d 100644 --- a/src/api.c +++ b/src/api.c @@ -2,7 +2,6 @@ #include "v.h" #define PRINTBUFLEN (1024*128) -#define FREE0(PTR) { free(PTR); PTR = NULL; } struct { mol * inp_mols; @@ -88,15 +87,15 @@ void PRINTOUT(FILE * f, char * format, ...){ } } -object * READ_FILES(drawpars * dp){ +object * READ_FILES(allpars * ap){ object * ret; if(!globals.inp_mols){ - ret = read_files(dp); + ret = read_files(ap); } else{ - ret = acs_from_var(globals.n_inp_mols, globals.inp_mols, dp); + ret = acs_from_var(globals.n_inp_mols, globals.inp_mols, ap); } - FREE0(dp->input_files); + FREE0(ap->ip.input_files); return ret; } diff --git a/src/mol/common.h b/src/mol/common.h index 5873029..bc7da6d 100644 --- a/src/mol/common.h +++ b/src/mol/common.h @@ -6,6 +6,9 @@ #define BA 0.5291772 #define AB 1.88972616356109068947 +#define CLOSE0(F) {if(F){ fclose(F); F = NULL; }} +#define FREE0(PTR) {if(PTR){ free(PTR); PTR = NULL; }} + #define MEM_END(S,X) ( (S)->X + (X##_size)/sizeof(*((S)->X)) ) #define MAX(x,y) ( ((x) > (y)) ? (x) : (y) ) diff --git a/src/v.c b/src/v.c index 136be99..a56c657 100644 --- a/src/v.c +++ b/src/v.c @@ -78,31 +78,32 @@ int main (int argc, char * argv[]) { /*= Input ==================================================================*/ - drawpars dp = cli_parse(argc, argv); + allpars ap = cli_parse(argc, argv); - object * ent = READ_FILES(&dp); + object * ent = READ_FILES(&ap); if(!ent){ PRINT_ERR("no files to read\n"); return 1; } - if(dp.n >= dp.N){ - dp.n = dp.n%dp.N; + drawpars * dp = &ap.dp; + if(dp->n >= dp->N){ + dp->n = dp->n%dp->N; } - else if(dp.n<0){ - dp.n = dp.N-(-dp.n)%dp.N; + else if(dp->n<0){ + dp->n = dp->N-(-dp->n)%dp->N; } - if(!dp.gui){ - return headless(&dp, ent); + if(!ap.ip.gui){ + return headless(dp, ent); } /*= X11 init ===============================================================*/ ptf kp[NKP]; - init_x(dp.fname, dp.colors); + init_x(dp->read.fname, ap.ip.colors); init_keys(kp); - init_font(dp.fontname); + init_font(ap.ip.fontname); #if 0 myDrawString = &XDrawString; #else @@ -115,7 +116,7 @@ int main (int argc, char * argv[]) { #endif /*= Main loop ==============================================================*/ - main_loop(ent, &dp, kp); + main_loop(ent, dp, kp); return 0; } diff --git a/src/v/ac3_draw.c b/src/v/ac3_draw.c index 0429906..3de6900 100644 --- a/src/v/ac3_draw.c +++ b/src/v/ac3_draw.c @@ -33,27 +33,27 @@ static int cmpz(const void * p1, const void * p2){ return 0; } -void ac3_draw(atcoord * ac, double r0, double scale, double xy0[2], int b, int num){ +void ac3_draw(atcoord * ac, rendpars rend){ -#define SCREEN_X(X) (W/2 + d*(xy0[0] + (X))) -#define SCREEN_Y(Y) (H/2 - d*(xy0[1] + (Y))) +#define SCREEN_X(X) (W/2 + d*(rend.xy0[0] + (X))) +#define SCREEN_Y(Y) (H/2 - d*(rend.xy0[1] + (Y))) CLEARCANV; int n = ac->n; kzstr * kz = malloc(sizeof(kzstr)*n); - int * ks = (b>0) ? malloc(sizeof(int)*n) : NULL; + int * ks = (rend.bonds>0) ? malloc(sizeof(int)*n) : NULL; - double d = MIN(H,W) * scale; + double d = MIN(H,W) * rend.scale; double resol = MIN(H,W) * RESOL_SCALE; - double r1 = r0 * resol * scale; + double r1 = rend.r * resol * rend.scale; for(int k=0; kr[k*3+2]; } qsort(kz, n, sizeof(kzstr), cmpz); - if(b>0){ + if(rend.bonds>0){ for(int i=0; i0?gc_black:gc_dot[1], x-r, y-r, 2*r, 2*r, 0, 360*64); } - if(num == 1){ + if(rend.num == 1){ char text[16]; snprintf(text, sizeof(text), "%d", k+1); myDrawString(dis, canv, gc_black, x, y, text, strlen(text)); } - else if(num == -1){ + else if(rend.num == -1){ char text[16]; const char * s = getname(q); s ? snprintf(text, sizeof(text), "%s", s) : snprintf(text, sizeof(text), "%d", q ); myDrawString(dis, canv, gc_black, x, y, text, strlen(text)); } - if(b>0){ + if(rend.bonds>0){ for(int j=k*BONDS_MAX; j<(k+1)*BONDS_MAX; j++){ int k1 = ac->bond_a[j]; if(k1 == -1 ){ @@ -104,7 +104,7 @@ void ac3_draw(atcoord * ac, double r0, double scale, double xy0[2], int b, int n } double dd = BOND_OFFSET * r / sqrt(r2d); XDrawLine(dis, canv, gc_black, x+dd*dx, y+dd*dy, x1, y1); - if(b==2){ + if(rend.bonds==2){ char text[16]; snprintf(text, sizeof(text), "%.3lf", ac->bond_r[j]); myDrawString(dis, canv, gc_black, x+dx/2, y+dy/2, text, strlen(text)); diff --git a/src/v/ac3_print.c b/src/v/ac3_print.c index 16d875e..8f6986e 100644 --- a/src/v/ac3_print.c +++ b/src/v/ac3_print.c @@ -1,14 +1,14 @@ #include "v.h" -void ac3_print(atcoord * ac, double xy0[2], int b){ +void ac3_print(atcoord * ac, rendpars rend){ PRINTOUT(stdout, "$molecule\ncart\n"); for(int k=0; kn; k++){ PRINTOUT(stdout, "%3d % lf % lf % lf", ac->q[k], - xy0[0] + ac->r[k*3 ], - xy0[1] + ac->r[k*3+1], + rend.xy0[0] + ac->r[k*3 ], + rend.xy0[1] + ac->r[k*3+1], ac->r[k*3+2]); - if(b>0){ + if(rend.bonds>0){ for(int j=0; jbond_a[k*BONDS_MAX+j]; if(k1 == -1 ){ @@ -23,7 +23,7 @@ void ac3_print(atcoord * ac, double xy0[2], int b){ return; } -void ac3_print_xyz(atcoord * ac, double xy0[2]){ +void ac3_print_xyz(atcoord * ac, rendpars rend){ PRINTOUT(stdout, "%d\n\n", ac->n); for(int k=0; kn; k++){ const char * s = getname(ac->q[k]); @@ -35,33 +35,33 @@ void ac3_print_xyz(atcoord * ac, double xy0[2]){ PRINTOUT(stdout, " %3d", ac->q[k]); } PRINTOUT(stdout, " % lf % lf % lf\n", - xy0[0] + ac->r[k*3 ], - xy0[1] + ac->r[k*3+1], + rend.xy0[0] + ac->r[k*3 ], + rend.xy0[1] + ac->r[k*3+1], ac->r[k*3+2]); } return; } -void ac3_print2fig(atcoord * ac, double xy0[2], int b, double * v){ +void ac3_print2fig(atcoord * ac, rendpars rend, double * v){ int n = ac->n; for(int i=0; iq[i], - xy0[0] + ac->r[i*3 ], - xy0[1] + ac->r[i*3+1], + rend.xy0[0] + ac->r[i*3 ], + rend.xy0[1] + ac->r[i*3+1], ac->r[i*3+2]); } if(v){ for(int i=0; i<8; i++){ PRINTOUT(stdout, "atom %3d% 13.7lf% 13.7lf% 13.7lf\n", 0, - xy0[0] + v[i*3 ], - xy0[1] + v[i*3+1], + rend.xy0[0] + v[i*3 ], + rend.xy0[1] + v[i*3+1], v[i*3+2]); } } - if(b>0){ + if(rend.bonds>0){ for(int k=0; kbond_a[k*BONDS_MAX+j]; diff --git a/src/v/ac3_read.c b/src/v/ac3_read.c index 06330b2..6b89461 100644 --- a/src/v/ac3_read.c +++ b/src/v/ac3_read.c @@ -2,7 +2,7 @@ #include "vecn.h" #include "vec3.h" -atcoord * atcoord_fill(mol * m0, int b, int center, int inertia, int bohr){ +atcoord * atcoord_fill(mol * m0, int b, const geompars geom){ int n = m0->n; @@ -42,23 +42,24 @@ atcoord * atcoord_fill(mol * m0, int b, int center, int inertia, int bohr){ } m->fname = m0->name; - if(bohr){ + if(geom.bohr){ vecscal(n*3, m->r, BA); } - if(inertia){ + if(geom.inertia){ // should not change m0 position(&((mol){.n=n, .q=m->q, .r=m->r}), NULL, 1); } - if(center){ - center_mol(n, m->r, center==2 ? m->q : NULL); + if(geom.center){ + center_mol(n, m->r, geom.center==2 ? m->q : NULL); } return m; } -atcoord * ac3_read(FILE * f, int b, int center, int inertia, int bohr, const char * fname, format_t * format){ +atcoord * ac3_read(readpars read, int b, const geompars geom, format_t * format){ mol * m = NULL; + FILE * f = read.f; switch(*format){ case XYZ: @@ -95,9 +96,9 @@ atcoord * ac3_read(FILE * f, int b, int center, int inertia, int bohr, const cha if(!m){ return NULL; } - m->name = fname; + m->name = read.fname; - atcoord * M = atcoord_fill(m, b, center, inertia, bohr); + atcoord * M = atcoord_fill(m, b, geom); free(m); return M; } diff --git a/src/v/bonds.c b/src/v/bonds.c index 8534273..68084e4 100644 --- a/src/v/bonds.c +++ b/src/v/bonds.c @@ -33,8 +33,9 @@ static void makelist(int bsize_max, int * bsize, int * list, return; } -static void bonds_add(double rl, double bmax, atcoord * ac){ - +static void bonds_add(bondpars bond, atcoord * ac){ + double bmax = bond.bmax; + double rl = bond.rl; double dmax = (bmax > 0.0) ? bmax : (DMAX_SCALE * rl * getmaxradius(ac->n, ac->q)); double rmin[3], rmax[3]; @@ -154,7 +155,7 @@ static void bonds_add(double rl, double bmax, atcoord * ac){ return; } -static void bonds_reduce(double rl, atcoord * ac){ +static void bonds_reduce(bondpars bond, atcoord * ac){ for(int k1=0; k1n; k1++){ double r1 = getradius(ac->q[k1]); int nb = 0; @@ -163,7 +164,7 @@ static void bonds_reduce(double rl, atcoord * ac){ if(k2==-1) break; double r = ac->bond_r[k1*BONDS_MAX+j]; double r2 = getradius(ac->q[k2]); - if( r < rl*(r1+r2) ){ + if( r < bond.rl*(r1+r2) ){ ac->bond_a[k1*BONDS_MAX+nb] = k2; ac->bond_r[k1*BONDS_MAX+nb] = r; nb++; @@ -176,14 +177,15 @@ static void bonds_reduce(double rl, atcoord * ac){ return; } -void bonds_fill(double rl, double bmax, atcoord * ac){ - if(rl > ac->bond_rl){ - bonds_add (rl, bmax, ac); +void bonds_fill(bondpars bond, atcoord * ac){ + if(bond.rl > ac->bond_rl){ + bonds_add (bond, ac); } else{ - bonds_reduce(rl, ac); + bonds_reduce(bond, ac); } - ac->bond_rl = rl; + ac->bond_rl = bond.rl; + ac->bond_flag = 1; return; } diff --git a/src/v/cli.c b/src/v/cli.c index 0bb28d3..4673a66 100644 --- a/src/v/cli.c +++ b/src/v/cli.c @@ -3,104 +3,142 @@ #include "matrix.h" #include "vec3.h" -static int sscan_rot(const char * arg, double rot[9]){ - return sscanf(arg, "rot:%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf", rot, rot+1, rot+2, rot+3, rot+4, rot+5, rot+6, rot+7, rot+8); -} - -static int sscan_cell(const char * arg, double cell[9]){ - int count = sscanf(arg, "cell:b%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf", cell, cell+1, cell+2, cell+3, cell+4, cell+5, cell+6, cell+7, cell+8); - if(count==9 || count==3){ - vecscal(count, cell, BA); - return count; +#define EPS2 1e-15 + +static int lazysscanf(char * const s, const char * const template, char ** ret){ + // if the string `s` begins with the template, + // assign *ret to the part of the string after the template, + // otherwise do nothing + int n = strlen(template); + int r = !strncmp(template, s, n); + if(r){ + if(strlen(s)==n){ + PRINT_WARN("option %s needs an argument\n", template) + } + else{ + *ret = s+n; + } } - count = sscanf(arg, "cell:%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf", cell, cell+1, cell+2, cell+3, cell+4, cell+5, cell+6, cell+7, cell+8); - return count; + return r; } -static int sscan_shell(const char * arg, double shell[2]){ - int count = sscanf (arg, "shell:b%lf,%lf", shell, shell+1); - if(count==2){ - vecscal(count, shell, BA); - return count; +static int sscan_rot(const char * arg, double ac3rmx[9]){ + double rot[9]; + int count = sscanf(arg, "rot:%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf", rot, rot+1, rot+2, rot+3, rot+4, rot+5, rot+6, rot+7, rot+8); + if(count <= 0){ + return 0; } - count = sscanf (arg, "shell:%lf,%lf", shell, shell+1); - return count; + else if(count == 9){ + veccp(9, ac3rmx, rot); // we don't check if it is unitary + } + else{ + PRINT_WARN("option `rot:` takes exactly 9 comma-separated arguments\n") + } + return 1; } -static void getcell(double cell[9], drawpars * dp, int cell_count){ +static int sscan_cell(const char * arg, cellpars * cp){ + double cell[9]; + int count = sscanf(arg, "cell:b%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf", cell, cell+1, cell+2, cell+3, cell+4, cell+5, cell+6, cell+7, cell+8); + if(count > 0){ + if(count==9 || count==3){ + vecscal(count, cell, BA); + } + else{ + PRINT_WARN("option `cell:b` takes exactly 3 or 9 comma-separated arguments\n") + return 1; + } + } + else{ + count = sscanf(arg, "cell:%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf", cell, cell+1, cell+2, cell+3, cell+4, cell+5, cell+6, cell+7, cell+8); + if(count <= 0){ + return 0; + } + else if(count!=3 && count!=9){ + PRINT_WARN("option `cell:` takes exactly 3 or 9 comma-separated arguments\n") + return 1; + } + } - double a[3]={}; - double b[3]={}; - double c[3]={}; - if(cell_count==3){ + double a[3]={}, b[3]={}, c[3]={}; + if(count==3){ a[0] = cell[0]; b[1] = cell[1]; c[2] = cell[2]; } - else if(cell_count==9){ + else{ r3cp(a, cell+0); r3cp(b, cell+3); r3cp(c, cell+6); } - r3sums3(dp->vertices+ 0, a, -0.5, b, -0.5, c, -0.5); - r3sums3(dp->vertices+ 3, a, +0.5, b, -0.5, c, -0.5); - r3sums3(dp->vertices+ 6, a, -0.5, b, +0.5, c, -0.5); - r3sums3(dp->vertices+ 9, a, -0.5, b, -0.5, c, +0.5); - r3sums3(dp->vertices+12, a, +0.5, b, +0.5, c, -0.5); - r3sums3(dp->vertices+15, a, +0.5, b, -0.5, c, +0.5); - r3sums3(dp->vertices+18, a, -0.5, b, +0.5, c, +0.5); - r3sums3(dp->vertices+21, a, +0.5, b, +0.5, c, +0.5); + r3sums3(cp->vertices+ 0, a, -0.5, b, -0.5, c, -0.5); + r3sums3(cp->vertices+ 3, a, +0.5, b, -0.5, c, -0.5); + r3sums3(cp->vertices+ 6, a, -0.5, b, +0.5, c, -0.5); + r3sums3(cp->vertices+ 9, a, -0.5, b, -0.5, c, +0.5); + r3sums3(cp->vertices+12, a, +0.5, b, +0.5, c, -0.5); + r3sums3(cp->vertices+15, a, +0.5, b, -0.5, c, +0.5); + r3sums3(cp->vertices+18, a, -0.5, b, +0.5, c, +0.5); + r3sums3(cp->vertices+21, a, +0.5, b, +0.5, c, +0.5); double rot_to_lab_basis[9] = {a[0], b[0], c[0], a[1], b[1], c[1], a[2], b[2], c[2]}; - veccp(9, dp->rot_to_lab_basis, rot_to_lab_basis); - mx_id(3, dp->rot_to_cell_basis); - mx_inv(3, 3, dp->rot_to_cell_basis, rot_to_lab_basis, 1e-15); + veccp(9, cp->rot_to_lab_basis, rot_to_lab_basis); + mx_id(3, cp->rot_to_cell_basis); + mx_inv(3, 3, cp->rot_to_cell_basis, rot_to_lab_basis, EPS2); - dp->vert = 1; - return; + cp->vert = 1; + return 1; } -static void getshell(double shell[2], drawpars * dp){ - dp->vertices[0] = shell[0]; - dp->vertices[1] = shell[1]; - dp->vert = 2; - return; +static int sscan_shell(const char * arg, cellpars * cp){ + double shell[2]; + int count = sscanf(arg, "shell:b%lf,%lf", shell, shell+1); + if(count > 0){ + if(count==2) + vecscal(count, shell, BA); + } + else{ + count = sscanf(arg, "shell:%lf,%lf", shell, shell+1); + } + if(count <= 0) + return 0; + if(count == 2){ + cp->vertices[0] = shell[0]; + cp->vertices[1] = shell[1]; + cp->vert = 2; + } + return 1; } -static int cli_parse_arg(char * arg, drawpars * dp){ - int vib = -1; - int bonds = 1; - int frame = 1; - double rot [9]={0}; - double cell [9]={0}; - double shell[2]={0}; - double tf = 0.0; - double bmax = 0.0; - char ts[256] = ""; - - int a0 = sscanf (arg, "vib:%d", &vib); - int a1 = sscanf (arg, "dt:%lf", &tf); - int a2 = sscanf (arg, "symtol:%lf", &(dp->symtol)); - int a3 = sscanf (arg, "bonds:%d", &bonds); - int a4 = sscanf (arg, "z:%d,%d,%d,%d,%d", dp->z, dp->z+1, dp->z+2, dp->z+3, dp->z+4); - int a5 = sscanf (arg, "font:%255s", dp->fontname); - int a6 = sscanf (arg, "gui:%d", &(dp->gui)); - int a7 = sscanf (arg, "bohr:%d", &(dp->bohr)); - int a8 = sscanf (arg, "bmax:%lf", &bmax); - int a9 = sscanf (arg, "frame:%d", &frame); - int a10 = sscanf (arg, "center:%d", &(dp->center)); - int a11 = sscanf (arg, "inertia:%d", &(dp->inertia)); - int a12 = sscanf (arg, "com:%255s", dp->com); - int a13 = sscanf (arg, "exitcom:%255s", dp->on_exit); - int a14 = sscanf (arg, "colors:%255s", ts); - int rot_count = sscan_rot (arg, rot); - int cell_count = sscan_cell (arg, cell); - int shell_count = sscan_shell(arg, shell); - - int cli = a0||a1||a2||a3||a4||a5||a6||a7||a8||a9||a10||a11||a12||a13||a14 || rot_count||cell_count||shell_count; +static int cli_parse_arg(char * arg, allpars * ap){ + drawpars * dp = &ap->dp; + initpars * ip = &ap->ip; + int vib = -1, bonds = -2, frame = 0; + double tf = 0, bmax = 0; + char * ts = NULL; + + int cli = + sscanf (arg, "vib:%d", &vib) + || sscanf (arg, "bonds:%d", &bonds) + || sscanf (arg, "dt:%lf", &tf) + || sscanf (arg, "bmax:%lf", &bmax) + || sscanf (arg, "frame:%d", &frame) + || sscanf (arg, "symtol:%lf", &dp->anal.symtol) + || sscanf (arg, "gui:%d", &ip->gui) + || sscanf (arg, "bohr:%d", &dp->geom.bohr) + || sscanf (arg, "center:%d", &dp->geom.center) + || sscanf (arg, "inertia:%d", &dp->geom.inertia) + || sscanf (arg, "z:%d,%d,%d,%d,%d", dp->anal.intcoord, dp->anal.intcoord+1, dp->anal.intcoord+2, dp->anal.intcoord+3, dp->anal.intcoord+4) + || lazysscanf(arg, "font:", &ip->fontname) + || lazysscanf(arg, "com:", &dp->ui.com) + || lazysscanf(arg, "exitcom:", &dp->ui.on_exit) + || lazysscanf(arg, "colors:", &ts) + || sscan_rot (arg, dp->rend.ac3rmx) + || sscan_cell (arg, &dp->cell) + || sscan_shell(arg, &dp->cell) + ; if(vib==0){ dp->task = AT3COORDS; @@ -109,26 +147,30 @@ static int cli_parse_arg(char * arg, drawpars * dp){ dp->task = VIBRO; } - if(!bonds){ - dp->b = -1; + if(bonds==0){ + dp->rend.bonds = -1; } if(tf>0.0){ - dp->dt = ceil(tf*1e6); + dp->anim.dt = ceil(tf*1e6); } if(bmax>0.0){ - dp->bmax = bmax; + dp->bond.bmax = bmax; + } + + if(frame){ + dp->n = frame-1; } - if(ts[0]){ + if(ts){ const char * const colorscheme_names[] = {[V_COLORS] = "v", [CPK_COLORS] = "cpk"}; int ncs = sizeof(colorscheme_names)/sizeof(colorscheme_names[0]); for(int i=0; icolors = i; + ip->colors = i; break; } if(i==ncs-1){ @@ -137,77 +179,45 @@ static int cli_parse_arg(char * arg, drawpars * dp){ } } - if(rot_count==9){ - veccp(9, dp->ac3rmx, rot); // we don't check if the matrix is unitary - } - if(cell_count==3 || cell_count==9){ - getcell(cell, dp, cell_count); - } - if(shell_count==2){ - getshell(shell, dp); - } - dp->n = frame-1; - if(!cli){ - dp->input_files[dp->input_files_n++] = arg; + ip->input_files[ip->input_files_n++] = arg; } return cli; } -static drawpars dp_init(void){ - drawpars dp = {}; - dp.task = UNKNOWN; - dp.gui = 1; - dp.input = 0; - memset(dp.input_text, 0, STRLEN); - dp.dt = DEFAULT_TIMEOUT; - memset(dp.fontname, 0, STRLEN); - dp.n = 0; - dp.fbw = 0; - dp.num = 0; - dp.t = 0; - dp.rl = 1.0; - dp.r = 1.0; - dp.xy0[0] = dp.xy0[1] = 0.0; - mx_id(3, dp.ac3rmx); - // from command-line - dp.inertia = 0; - dp.center = 1; - dp.b = 1; - dp.bmax = 0.0; - dp.symtol = DEFAULT_SYMTOL; - dp.vert = -1; - dp.z[0] = dp.z[1] = dp.z[2] = dp.z[3] = dp.z[4] = 0; - vecset(3*8, dp.vertices, 0.0); - memset(dp.com, 0, STRLEN); - memset(dp.on_exit, 0, STRLEN); - dp.input_files_n = 0; - dp.input_files = NULL; - dp.colors = V_COLORS; - // from data read - dp.scale = 1.0; - dp.N = 0; - dp.f = NULL; - dp.fname = NULL; - dp.bohr = 0; - // runtime - dp.closed = 0; - return dp; +static allpars allpars_init(void){ + allpars ap = {}; // everything not set below is 0 / 0.0 / NULL / '\0' + + ap.ip.gui = 1; + ap.ip.colors = V_COLORS; + + ap.dp.task = UNKNOWN; + ap.dp.anim.dt = DEFAULT_TIMEOUT; + ap.dp.anal.symtol = DEFAULT_SYMTOL; + ap.dp.bond.rl = 1.0; + ap.dp.cell.vert = -1; + ap.dp.geom.center = 1; + + ap.dp.rend.r = 1.0; + ap.dp.rend.scale = 1.0; + ap.dp.rend.bonds = 1; + mx_id(3, ap.dp.rend.ac3rmx); + + return ap; } -drawpars cli_parse(int argc, char ** argv){ - drawpars dp = dp_init(); - dp.input_files = malloc(argc*sizeof(char*)); +allpars cli_parse(int argc, char ** argv){ + allpars ap = allpars_init(); + ap.ip.input_files = malloc(argc*sizeof(char*)); for(int i=1; im[dp->n]; - if(dp->b>0 && !ac->bond_flag){ - bonds_fill(dp->rl, dp->bmax, ac); - ac->bond_flag = 1; + if(dp->rend.bonds>0 && !ac->bond_flag){ + bonds_fill(dp->bond, ac); } - ac3_draw(ac, dp->r, dp->scale, dp->xy0, dp->b, dp->num); + ac3_draw(ac, dp->rend); ac3_text(ac, dp); - if(dp->vert == 1){ + if(dp->cell.vert == 1){ double v[24]; for(int i=0; i<8; i++){ - r3mx (v+3*i, dp->vertices+3*i, dp->ac3rmx); + r3mx (v+3*i, dp->cell.vertices+3*i, dp->rend.ac3rmx); } - drawvertices(v, dp->scale, dp->xy0); + drawvertices(v, dp->rend.scale, dp->rend.xy0); } - else if(dp->vert == 2){ - drawshell(dp->vertices[0], dp->vertices[1], dp->scale, dp->xy0); + else if(dp->cell.vert == 2){ + drawshell(dp->cell.vertices[0], dp->cell.vertices[1], dp->rend.scale, dp->rend.xy0); } return; @@ -41,19 +40,18 @@ static void redraw_vibro(object * ent, drawpars * dp){ double * r0 = ent->vib->r0; double * dr = ent->vib->disp + dp->n * m->n*3; - if(dp->b>0 && !m->bond_flag){ - bonds_fill(dp->rl, dp->bmax, m); - m->bond_flag = 1; + if(dp->rend.bonds>0 && !m->bond_flag){ + bonds_fill(dp->bond, m); } - vecsums(m->n*3, m->r, r0, dr, sin( dp->t * 2.0*M_PI/TMAX ) * 0.1*sqrt(m->n) ); + vecsums(m->n*3, m->r, r0, dr, sin( dp->anim.t * 2.0*M_PI/TMAX ) * 0.1*sqrt(m->n) ); for(int j=0; jn; j++){ double v[3]; - r3mx(v, m->r+3*j, dp->ac3rmx); + r3mx(v, m->r+3*j, dp->rend.ac3rmx); r3cp(m->r+3*j, v); } - ac3_draw(m, dp->r, dp->scale, dp->xy0, dp->b, dp->num); + ac3_draw(m, dp->rend); vibro_text(ent->vib, dp); return; @@ -62,12 +60,12 @@ static void redraw_vibro(object * ent, drawpars * dp){ void kp_readmore(object * ent, drawpars * dp){ if(dp->task == AT3COORDS){ object * acs = ent; - if(!dp->f){ - PRINT_ERR("cannot read from the file '%s'\n", dp->fname); + if(!dp->read.f){ + PRINT_ERR("cannot read from the file '%s'\n", dp->read.fname); return; } - fseek(dp->f, 0, SEEK_CUR); - acs_readmore(dp->f, dp->b, dp->center, dp->inertia, dp->bohr, acs, dp->fname); + fseek(dp->read.f, 0, SEEK_CUR); + acs_readmore(dp->read, dp->rend.bonds, dp->geom, acs); newmol_prep(acs, dp); redraw_ac3 (acs, dp); } @@ -77,8 +75,8 @@ void kp_readmore(object * ent, drawpars * dp){ void kp_readagain(object * ent, drawpars * dp){ if(dp->task == AT3COORDS){ - if(!dp->f || !(fclose(dp->f), dp->f = fopen(dp->fname, "r"))){ - PRINT_WARN("cannot reload the file '%s'\n", dp->fname); + if(!dp->read.f || !(fclose(dp->read.f), dp->read.f = fopen(dp->read.fname, "r"))){ + PRINT_WARN("cannot reload the file '%s'\n", dp->read.fname); return; } @@ -88,7 +86,7 @@ void kp_readagain(object * ent, drawpars * dp){ } acs->n = dp->N = dp->n = 0; - acs_readmore(dp->f, dp->b, dp->center, dp->inertia, dp->bohr, acs, dp->fname); + acs_readmore(dp->read, dp->rend.bonds, dp->geom, acs); newmol_prep(acs, dp); redraw_ac3 (acs, dp); } @@ -98,7 +96,7 @@ void kp_readagain(object * ent, drawpars * dp){ void kp_print(object * ent, drawpars * dp){ if (dp->task == AT3COORDS){ atcoord * ac = ent->m[dp->n]; - ac3_print(ac, dp->xy0, dp->b); + ac3_print(ac, dp->rend); } return; } @@ -106,13 +104,13 @@ void kp_print(object * ent, drawpars * dp){ void kp_print_xyz(object * ent, drawpars * dp){ if (dp->task == AT3COORDS){ atcoord * ac = ent->m[dp->n]; - ac3_print_xyz(ac, dp->xy0); + ac3_print_xyz(ac, dp->rend); } return; } void kp_printrot(object * ent __attribute__ ((unused)), drawpars * dp){ - double * U = dp->ac3rmx; + double * U = dp->rend.ac3rmx; for(int i=0; i<3; i++){ PRINTOUT(stdout, "rotation> % 20.15lf % 20.15lf % 20.15lf\n", U[i*3], U[i*3+1], U[i*3+2]); } @@ -124,13 +122,13 @@ void kp_printrot(object * ent __attribute__ ((unused)), drawpars * dp){ void kp_print2fig(object * ent, drawpars * dp){ if (dp->task == AT3COORDS){ double v[3*8]; - if(dp->vert == 1){ + if(dp->cell.vert == 1){ for(int i=0; i<8; i++){ - r3mx (v+3*i, dp->vertices+3*i, dp->ac3rmx); + r3mx (v+3*i, dp->cell.vertices+3*i, dp->rend.ac3rmx); } } atcoord * ac = ent->m[dp->n]; - ac3_print2fig(ac, dp->xy0, dp->b, dp->vert==1?v:NULL); + ac3_print2fig(ac, dp->rend, dp->cell.vert==1?v:NULL); } return; } @@ -144,41 +142,41 @@ static void rl_changed(object * ent, drawpars * dp){ } void kp_rl_dec(object * ent, drawpars * dp){ - if(dp->b>0){ - dp->rl /= step_r; + if(dp->rend.bonds>0){ + dp->bond.rl /= step_r; rl_changed(ent, dp); } return; } void kp_rl_inc(object * ent, drawpars * dp){ - if(dp->b>0){ - dp->rl *= step_r; + if(dp->rend.bonds>0){ + dp->bond.rl *= step_r; rl_changed(ent, dp); } return; } void kp_r_dec(object * ent, drawpars * dp){ - dp->r /= step_r; + dp->rend.r /= step_r; exp_redraw(ent, dp); return; } void kp_r_inc(object * ent, drawpars * dp){ - dp->r *= step_r; + dp->rend.r *= step_r; exp_redraw(ent, dp); return; } void kp_zoom_out(object * ent, drawpars * dp){ - dp->scale /= step_zoom; + dp->rend.scale /= step_zoom; exp_redraw(ent, dp); return; } void kp_zoom_in(object * ent, drawpars * dp){ - dp->scale *= step_zoom; + dp->rend.scale *= step_zoom; exp_redraw(ent, dp); return; } @@ -189,7 +187,7 @@ void kp_frame_inc(object * ent, drawpars * dp){ exp_redraw(ent, dp); } if (dp->n == dp->N-1 && dp->task == AT3COORDS){ - dp->fbw = 0; + dp->anim.dir = 0; } return; } @@ -200,7 +198,7 @@ void kp_frame_dec(object * ent, drawpars * dp){ exp_redraw(ent, dp); } if (dp->n == 0 && dp->task == AT3COORDS){ - dp->fbw = 0; + dp->anim.dir = 0; } return; } @@ -211,8 +209,8 @@ void rot_ent_pointer(object * ent, drawpars * dp, int dx, int dy, double speed){ rot_around_perp(rotation_matrix, (double)dx, (double)dy, speed); double mx0[9]; - veccp(9, mx0, dp->ac3rmx); - mx_multmx(3,3,3, dp->ac3rmx, rotation_matrix, mx0); + veccp(9, mx0, dp->rend.ac3rmx); + mx_multmx(3,3,3, dp->rend.ac3rmx, rotation_matrix, mx0); if(dp->task == AT3COORDS){ object * acs = ent; for(int i=0; iN; i++){ @@ -223,12 +221,12 @@ void rot_ent_pointer(object * ent, drawpars * dp, int dx, int dy, double speed){ } static void rot_ent(object * ent, drawpars * dp, int axis, double angle){ - if(dp->modkey){ + if(dp->ui.modkey){ angle *= step_mod; } double m[9]; - rotmx0_update(dp->ac3rmx, m, angle, axis); + rotmx0_update(dp->rend.ac3rmx, m, angle, axis); if(dp->task == AT3COORDS){ object * acs = ent; @@ -277,11 +275,11 @@ void kp_rotz_r(object * ent, drawpars * dp){ static void mol2cell(double r0[3], drawpars * dp){ double mat[9], r[3]; - veccp(9, mat, dp->ac3rmx); + veccp(9, mat, dp->rend.ac3rmx); r3cp(r, r0); mx_inv (3, 1, r, mat, 1e-15); double rcell[3]; - r3mx(rcell, r, dp->rot_to_cell_basis); + r3mx(rcell, r, dp->cell.rot_to_cell_basis); for(int i=0; i<3; i++){ if(rcell[i]<-0.5){ rcell[i] += 1.0; @@ -290,8 +288,8 @@ static void mol2cell(double r0[3], drawpars * dp){ rcell[i] -= 1.0; } } - r3mx(r, rcell, dp->rot_to_lab_basis); - r3mx(r0, r, dp->ac3rmx); + r3mx(r, rcell, dp->cell.rot_to_lab_basis); + r3mx(r0, r, dp->rend.ac3rmx); return; } @@ -302,7 +300,7 @@ static void move_pbc(object * acs, drawpars * dp, int dir, double d){ r[dir] += d; mol2cell(r, dp); } - if(dp->b>0){ + if(dp->rend.bonds>0){ acs->m[i]->bond_flag = 0; acs->m[i]->bond_rl *= rl_move_pbc_scale; } @@ -311,14 +309,14 @@ static void move_pbc(object * acs, drawpars * dp, int dir, double d){ } static void move_ent(object * ent, drawpars * dp, int dir, double step){ - if(dp->modkey){ + if(dp->ui.modkey){ step *= step_mod; } - if(dp->vert == 1){ + if(dp->cell.vert == 1){ move_pbc(ent, dp, dir, step); } else { - dp->xy0[dir] += step; + dp->rend.xy0[dir] += step; } return; } @@ -348,51 +346,49 @@ void kp_move_d(object * ent, drawpars * dp){ } void kp_exit(object * ent, drawpars * dp){ - run_commands(NULL, dp->on_exit, dp, ent); + run_commands(NULL, dp->ui.on_exit, dp, ent); obj_free(ent); - if(dp->f){ - fclose(dp->f); - } - dp->closed = 1; close_x(); + CLOSE0(dp->read.f); + dp->ui.closed = 1; } void kp_fw_toggle(object * ent __attribute__ ((unused)), drawpars * dp){ - dp->fbw = (dp->fbw == 1) ? 0 : 1; + dp->anim.dir = (dp->anim.dir == 1) ? 0 : 1; return; } void kp_bw_toggle(object * ent __attribute__ ((unused)), drawpars * dp){ if (dp->task == AT3COORDS){ - dp->fbw = (dp->fbw == -1) ? 0 : -1; + dp->anim.dir = (dp->anim.dir == -1) ? 0 : -1; } return; } void kp_l_toggle(object * ent, drawpars * dp){ - if(dp->b>0){ - dp->b = 1+!(dp->b-1); + if(dp->rend.bonds>0){ + dp->rend.bonds = 1+!(dp->rend.bonds-1); exp_redraw(ent, dp); } return; } void kp_b_toggle(object * ent, drawpars * dp){ - if(dp->b>-1){ - dp->b = !dp->b; + if(dp->rend.bonds>-1){ + dp->rend.bonds = !dp->rend.bonds; } exp_redraw(ent, dp); return; } void kp_n_toggle(object * ent, drawpars * dp){ - dp->num = (dp->num == 1) ? 0 : 1; + dp->rend.num = (dp->rend.num == 1) ? 0 : 1; exp_redraw(ent, dp); return; } void kp_t_toggle(object * ent, drawpars * dp){ - dp->num = (dp->num == -1) ? 0 : -1; + dp->rend.num = (dp->rend.num == -1) ? 0 : -1; exp_redraw(ent, dp); return; } @@ -429,8 +425,8 @@ void exp_redraw(object * ent, drawpars * dp){ void time_gone(object * ent, drawpars * dp){ if(dp->task == VIBRO){ - if(dp->t >= TMAX){ - dp->t = dp->t-TMAX; + if(dp->anim.t >= TMAX){ + dp->anim.t = dp->anim.t-TMAX; } redraw_vibro(ent, dp); } @@ -440,7 +436,7 @@ void time_gone(object * ent, drawpars * dp){ static void savevib(drawpars * dp, int c){ char s[STRLEN]; int l = (int)(log10( dp->N + 0.5 )) + 1; - snprintf(s, sizeof(s), "%s_%0*d_%02d.xpm", dp->fname, l, dp->n+1, c); + snprintf(s, sizeof(s), "%s_%0*d_%02d.xpm", dp->read.fname, l, dp->n+1, c); if (savepic(s) != XpmSuccess){ PRINT_ERR("cannot save '%s'\n", s); } @@ -474,10 +470,10 @@ void kp_film(object * ent, drawpars * dp){ } else{ int c = 0; - dp->t = 0; + dp->anim.t = 0; do{ savevib(dp, c); - dp->t++; + dp->anim.t++; time_gone(ent, dp); } while(++ctask == AT3COORDS){ atcoord * ac = ent->m[dp->n]; if(!ac->sym[0]){ - pg(ac, dp->symtol); + pg(ac, dp->anal.symtol); redraw_ac3(ent, dp); } } @@ -496,8 +492,8 @@ void kp_pg(object * ent, drawpars * dp){ } void kp_jump(object * ent, drawpars * dp){ - if(!dp->input){ - dp->input = 1; + if(!dp->ui.input){ + dp->ui.input = 1; exp_redraw(ent, dp); } return; diff --git a/src/v/headless.c b/src/v/headless.c index 8ee8d96..c934486 100644 --- a/src/v/headless.c +++ b/src/v/headless.c @@ -7,7 +7,7 @@ void run_commands(FILE * f, char * command, drawpars * dp, object * ent){ while(1){ - if(command[0]){ + if(command && command[0]){ c = *(com++); } else if(f){ @@ -30,7 +30,7 @@ void run_commands(FILE * f, char * command, drawpars * dp, object * ent){ case('.'): { atcoord * ac = ent->m[dp->n]; - pg(ac, dp->symtol); + pg(ac, dp->anal.symtol); PRINTOUT(stdout, "%s\n", ac->sym); }; break; @@ -49,13 +49,11 @@ void run_commands(FILE * f, char * command, drawpars * dp, object * ent){ int headless(drawpars * dp, object * ent){ atcoord * ac = ent->m[dp->n]; - if(dp->b>0 && !ac->bond_flag){ - bonds_fill(dp->rl, dp->bmax, ac); + if(dp->rend.bonds>0 && !ac->bond_flag){ + bonds_fill(dp->bond, ac); } - run_commands(stdin, dp->com, dp, ent); + run_commands(stdin, dp->ui.com, dp, ent); obj_free(ent); - if(dp->f){ - fclose(dp->f); - } + CLOSE0(dp->read.f); return 0; } diff --git a/src/v/load.c b/src/v/load.c index c751ec0..3fb4096 100644 --- a/src/v/load.c +++ b/src/v/load.c @@ -13,12 +13,12 @@ static inline void fill_nf(object * acs, int n0){ return; } -void acs_readmore(FILE * f, int b, int center, int inertia, int bohr, object * acs, const char * fname){ +void acs_readmore(readpars read, int b, geompars geom, object * acs){ // needed to reset nf int n0 = acs->n; // if continue reading from a previously opened file, find the first molecule from it - if(ftell(f) && acs->n){ + if(ftell(read.f) && acs->n){ for(int i=1; i<=acs->n; i++){ int n1 = acs->n-i; if(acs->m[n1]->nf[0]==0){ @@ -30,7 +30,7 @@ void acs_readmore(FILE * f, int b, int center, int inertia, int bohr, object * a atcoord * m; format_t format = UNKNOWN_FORMAT; - while((m = ac3_read(f, b, center, inertia, bohr, fname, &format))!=NULL){ + while((m = ac3_read(read, b, geom, &format))!=NULL){ if(acs->n==acs->Nmem){ int N = acs->Nmem ? acs->Nmem*2 : N_MIN; atcoord ** ms = realloc(acs->m, N*sizeof(atcoord *)); @@ -83,7 +83,7 @@ static FILE * acs_read_newfile(object * acs, char * fname, drawpars * dp){ return NULL; } } - acs_readmore(f, dp->b, dp->center, dp->inertia, dp->bohr, acs, fname); + acs_readmore((readpars){f, fname}, dp->rend.bonds, dp->geom, acs); return f; } @@ -100,7 +100,7 @@ static object * ent_read(char * fname, drawpars * dp){ free(acs); return NULL; } - dp->fname = fname; + dp->read.fname = fname; if(dp->task==UNKNOWN || dp->task==VIBRO){ object * vib = mode_read_try(f, acs->m[acs->n-1]); @@ -108,7 +108,7 @@ static object * ent_read(char * fname, drawpars * dp){ acs->n--; obj_free(acs); fclose(f); - dp->scale = ac3_scale(vib->m[0]); + dp->rend.scale = ac3_scale(vib->m[0]); dp->N = vib->vib->n; dp->task = VIBRO; return vib; @@ -121,14 +121,16 @@ static object * ent_read(char * fname, drawpars * dp){ } dp->task = AT3COORDS; - dp->f = f; + dp->read.f = f; return acs; } -object * read_files(drawpars * dp){ +object * read_files(allpars * ap){ + drawpars * dp = &ap->dp; + initpars * ip = &ap->ip; - int fn = dp->input_files_n; - char ** flist = dp->input_files; + int fn = ip->input_files_n; + char ** flist = ip->input_files; object * ent = NULL; int i=0; @@ -151,27 +153,29 @@ object * read_files(drawpars * dp){ PRINT_WARN("cannot find molecules in file '%s'\n", flist[i]); } else{ - fclose(dp->f); - dp->f = f; - dp->fname = flist[i]; + fclose(dp->read.f); + dp->read.f = f; + dp->read.fname = flist[i]; n0 = acs->n; } } - dp->scale = acs_scale(acs); + dp->rend.scale = acs_scale(acs); newmol_prep(acs, dp); - intcoord_check(INT_MAX, dp->z); + intcoord_check(INT_MAX, dp->anal.intcoord); } else{ - dp->z[0] = 0; + dp->anal.intcoord[0] = 0; } return ent; } -object * acs_from_var(int n, mol * m, drawpars * dp){ +object * acs_from_var(int n, mol * m, allpars * ap){ + drawpars * dp = &ap->dp; + initpars * ip = &ap->ip; - for(int i=0; iinput_files_n; i++){ - PRINT_WARN("ignoring file '%s'\n", dp->input_files[i]); + for(int i=0; iinput_files_n; i++){ + PRINT_WARN("ignoring file '%s'\n", ip->input_files[i]); } if(dp->task==VIBRO){ PRINT_WARN("cannot read vibrations from input variable\n"); @@ -184,18 +188,18 @@ object * acs_from_var(int n, mol * m, drawpars * dp){ acs->vib = NULL; for(int i=0; im[i] = atcoord_fill(m+i, dp->b, dp->center, dp->inertia, dp->bohr); + acs->m[i] = atcoord_fill(m+i, dp->rend.bonds, dp->geom); } fill_nf(acs, 0); - dp->scale = acs_scale(acs); + dp->rend.scale = acs_scale(acs); newmol_prep(acs, dp); int natmax = 0; for(int i=0; iz); + intcoord_check(natmax, dp->anal.intcoord); return acs; } diff --git a/src/v/loop.c b/src/v/loop.c index 68361d0..131b300 100644 --- a/src/v/loop.c +++ b/src/v/loop.c @@ -46,31 +46,31 @@ void main_loop(object * ent, drawpars * dp, ptf kp[NKP]){ else if(event->type == ConfigureNotify){ W = event->xconfigure.width; H = event->xconfigure.height; - dp->xy0[0] = dp->xy0[1] = 0.0; + dp->rend.xy0[0] = dp->rend.xy0[1] = 0.0; exp_redraw(ent, dp); } else if(event->type == KeyPress) { - if(dp->input){ - int stop_input = process_x_input(dp, event); + if(dp->ui.input){ //////////////////////////////////// TODO move to function + int stop_input = process_x_input(dp->ui.input_text, event); if(stop_input==1){ - switch(dp->input){ + switch(dp->ui.input){ case(1): { - int frame = atoi(dp->input_text); + int frame = atoi(dp->ui.input_text); frame = MAX(1, MIN(frame, dp->N)); dp->n = frame-1; }; break; } } if(stop_input){ - memset(dp->input_text, 0, STRLEN); - dp->input=0; + memset(dp->ui.input_text, 0, STRLEN); + dp->ui.input=0; } exp_redraw(ent, dp); } else{ if(kp[event->xkey.keycode]){ - dp->modkey = event->xkey.state & (ShiftMask | ControlMask); + dp->ui.modkey = event->xkey.state & (ShiftMask | ControlMask); kp[event->xkey.keycode](ent, dp); } } @@ -108,23 +108,23 @@ void main_loop(object * ent, drawpars * dp, ptf kp[NKP]){ } } - if(dp->closed){ + if(dp->ui.closed){ return; } - if(dp->fbw){ + if(dp->anim.dir){ if(dp->task == AT3COORDS){ - if(dp->fbw > 0){ + if(dp->anim.dir > 0){ kp_frame_inc(ent, dp); } else{ kp_frame_dec(ent, dp); } - usleep(dp->dt); + usleep(dp->anim.dt); } else if(dp->task == VIBRO){ - /* We draw 5 times for each dp->t, + /* We draw 5 times for each dp->anim.t, * because dt is too small to look good * and 5*dt is too big to behave well (keyboard control). * Also we cannot draw only when tr==4, @@ -133,9 +133,9 @@ void main_loop(object * ent, drawpars * dp, ptf kp[NKP]){ */ if(++tr == VIBRO_SUBSTEPS){ tr = 0; - dp->t++; + dp->anim.t++; } - usleep(dp->dt); + usleep(dp->anim.dt); time_gone(ent, dp); } } diff --git a/src/v/pars.h b/src/v/pars.h new file mode 100644 index 0000000..a347ec1 --- /dev/null +++ b/src/v/pars.h @@ -0,0 +1,95 @@ +typedef enum { + UNKNOWN, + AT3COORDS, + VIBRO, +} task_t; + +typedef enum { + V_COLORS, + CPK_COLORS, +} colorscheme_t; + +typedef struct { + int gui; // if gui is enabled + char * fontname; // font + int input_files_n; // number of input files + char ** input_files; // input files + colorscheme_t colors; // colorscheme (v or cpk) +} initpars; + +typedef struct { + FILE * f; // open file for kp_readmore() + const char * fname; // file name +} readpars; + +typedef struct { + int center; // 0: nothing; 1: center each molecule upon reading; 2: center wrt center of mass + int inertia; // 0: nothing; 1: rotate each molecule upon reading wrt axis of inertia + int bohr; // 0: Å 1: Bohr +} geompars; + + +typedef struct { + int closed; // 1: time to go + char * com; // command string for gui:0 + char * on_exit; // command string to run on exit + int input; // 0=no input regime, 1=jump, ... + char input_text[STRLEN]; + int modkey; // whether ctrl or shift are pressed +} uipars; + +typedef struct { + double xy0[2]; // translation vector + double ac3rmx[9]; // rotational matrix + double scale; // zoom + double r; // atom size scale factor + int num; // 0: do not show; 1: show numbers; -1: show atom types + int bonds; // 0: do not show; 1: show bonds; 2: show bond+lengths; -1: never show +} rendpars; + +typedef struct { + double rl; // bond length scale factor + double bmax; // max. bond length to show +} bondpars; + +typedef struct { + double vertices[3*8]; // parameters of cell/shell + double rot_to_lab_basis[3*3]; // "rotation" matrix for PBC + double rot_to_cell_basis[3*3]; // "rotation" matrix for PBC + int vert; // 0: nothing; 1: show cell; 2: show shell +} cellpars; + +typedef struct { + int t; // counter for mode animation + int dir; // 0: nothing; 1: play forwards; -1: play backwards + unsigned int dt; // animation timeout +} animpars; + +typedef struct { + double symtol; // tolerance for symmetry determination + int intcoord[5]; // internal coordinate to show +} analpars; + +typedef struct { + + task_t task; // data type + + int N; // number of structures / modes + int n; // current structure / mode + + readpars read; + geompars geom; + uipars ui; + rendpars rend; + bondpars bond; + cellpars cell; + animpars anim; + analpars anal; + +} drawpars; + +typedef struct { + initpars ip; + drawpars dp; +} allpars; + diff --git a/src/v/tools.c b/src/v/tools.c index fceafb0..ae5897e 100644 --- a/src/v/tools.c +++ b/src/v/tools.c @@ -18,7 +18,7 @@ void newmol_prep(object * acs, drawpars * dp){ atcoord * ac = acs->m[j]; for(int i=0; in; i++){ double v[3]; - r3mx(v, ac->r+3*i, dp->ac3rmx); + r3mx(v, ac->r+3*i, dp->rend.ac3rmx); r3cp(ac->r+3*i, v); } } @@ -30,9 +30,9 @@ void ac3_text(atcoord * ac, drawpars * dp){ char text[STRLEN]; int tp = snprintf(text, sizeof(text), "%*d / %d r = %.1lf rl = %.1lf", - 1+(int)(log10(dp->N)), dp->n+1, dp->N, dp->r, dp->rl); - if( tpz[0] ){ - tp += snprintf(text+tp, sizeof(text)-tp, " | %d,%d,%d,%d,%d: %lf", dp->z[0], dp->z[1], dp->z[2], dp->z[3], dp->z[4], intcoord_calc(1, ac->n, dp->z, ac->r)); + 1+(int)(log10(dp->N)), dp->n+1, dp->N, dp->rend.r, dp->bond.rl); + if( tpanal.intcoord[0] ){ + tp += snprintf(text+tp, sizeof(text)-tp, " | %d,%d,%d,%d,%d: %lf", dp->anal.intcoord[0], dp->anal.intcoord[1], dp->anal.intcoord[2], dp->anal.intcoord[3], dp->anal.intcoord[4], intcoord_calc(1, ac->n, dp->anal.intcoord, ac->r)); } if( tpsym[0] ){ tp += snprintf(text+tp, sizeof(text)-tp, " | PG: %s", ac->sym); @@ -46,9 +46,9 @@ void ac3_text(atcoord * ac, drawpars * dp){ textincorner(text, text2); } - if(dp->input==1){ + if(dp->ui.input==1){ char text3[STRLEN]; - snprintf(text3, sizeof(text3), "JUMP TO >>> %s", dp->input_text); + snprintf(text3, sizeof(text3), "JUMP TO >>> %s", dp->ui.input_text); textincorner2(text3); } @@ -62,11 +62,11 @@ void vibro_text(vibr_t * ms, drawpars * dp){ char i = fq > 0.0 ? ' ' : 'i'; snprintf(text, sizeof(text), "%*d / %d %.1lf%c r = %.1lf rl = %.1lf", - 1+(int)(log10(ms->n)), dp->n+1, ms->n, fabs(fq), i, dp->r, dp->rl); - textincorner(text, dp->fname); - if(dp->input==1){ + 1+(int)(log10(ms->n)), dp->n+1, ms->n, fabs(fq), i, dp->rend.r, dp->bond.rl); + textincorner(text, dp->read.fname); + if(dp->ui.input==1){ char text3[STRLEN]; - snprintf(text3, sizeof(text3), "JUMP TO >>> %s", dp->input_text); + snprintf(text3, sizeof(text3), "JUMP TO >>> %s", dp->ui.input_text); textincorner2(text3); } return; diff --git a/src/v/v.h b/src/v/v.h index ee362d1..7755e8f 100644 --- a/src/v/v.h +++ b/src/v/v.h @@ -8,13 +8,9 @@ #define STRLEN 256 #define BIGSTRLEN 4096 -typedef void (* ptf )(); +#include "pars.h" -typedef enum { - UNKNOWN, - AT3COORDS, - VIBRO, -} task_t; +typedef void (* ptf )(); typedef enum { UNKNOWN_FORMAT, @@ -23,11 +19,6 @@ typedef enum { OUT, } format_t; -typedef enum { - V_COLORS, - CPK_COLORS, -} colorscheme_t; - typedef struct { int n; // number of atoms int bond_flag; // whether bonds are up-to-date. 0: no, 1: yes, -1: disabled @@ -54,62 +45,11 @@ typedef struct { vibr_t * vib; } object; -typedef struct { - - task_t task; // data type - unsigned int dt; // animation timeout - char fontname[STRLEN];// font - int gui; // - - int input; // 0=no input regime, 1=jump, ... - char input_text[STRLEN]; - - double xy0[2]; // translation vector - double ac3rmx[9]; // rotational matrix - - double scale; // zoom - double r; // atom size scale factor - double rl; // bond length scale factor - - FILE * f; // opened file for kp_readmore() - const char * fname; // file name - double vertices[3*8]; // parameters of cell/shell - double rot_to_lab_basis[3*3]; // "rotation" matrix for PBC - double rot_to_cell_basis[3*3]; // "rotation" matrix for PBC - double symtol; // tolerance for symmetry determination - double bmax; // max. bond length to show - int z[5]; // internal coordinate to show - int modkey; // whether ctrl of shift are pressed - - int N; // number of structures / modes - int n; // current structure / mode - int t; // counter for mode animation - - int b; // 0: do not show; 1: show bonds; 2: show bond+lengths; -1: never show - int fbw; // 0: nothing; 1: play forwards; -1: play backwards - int num; // 0: do not show; 1: show numbers; -1: show atom types - int vert; // 0: nothing; 1: show cell; 2: show shell - - int center; // 0: nothing; 1: center each molecule upon reading ; 2: center wrt center of mass - int inertia; // 0: nothing; 1: rotate each molecule upon reading wrt axis of inertia - int bohr; // 0: Å 1: Bohr - // - int closed; // 1: time to go - char com[STRLEN]; // command string for gui:0 - char on_exit[STRLEN]; // command string to run on exit - - int input_files_n; // number of input files - char ** input_files; // input files - // - colorscheme_t colors; // colorscheme (v or cpk) - -} drawpars; - // load.c -object * acs_from_var(int n, mol * m, drawpars * dp); -void acs_readmore (FILE * f, int b, int center, int inertia, int bohr, object * acs, const char * fname); -object * read_files(drawpars * dp); +object * acs_from_var(int n, mol * m, allpars * ap); +void acs_readmore (readpars read, int b, geompars geom, object * acs); +object * read_files(allpars * ap); // scale.c double ac3_scale(atcoord * ac); double acs_scale(object * acs); @@ -117,8 +57,8 @@ double acs_scale(object * acs); vibr_t * mode_read(FILE * f, int na); // ac3_read*.c int read_cart_atom(FILE * f, int n, mol * m); -atcoord * atcoord_fill(mol * m, int b, int center, int inertia, int bohr); -atcoord * ac3_read(FILE * f, int b, int center, int inertia, int bohr, const char * fname, format_t * format); +atcoord * atcoord_fill(mol * m, int b, geompars geom); +atcoord * ac3_read(readpars read, int b, geompars geom, format_t * format); mol * ac3_read_in (FILE * f); mol * ac3_read_out(FILE * f); mol * ac3_read_xyz(FILE * f); @@ -126,19 +66,19 @@ mol * ac3_read_xyz(FILE * f); // man.c void printman(FILE * f, char * exename); // cli.c -drawpars cli_parse(int argc, char ** argv); +allpars cli_parse(int argc, char ** argv); // loop.c void main_loop(object * ent, drawpars * dp, ptf kp[NKP]); // ac3_draw.c -void ac3_draw (atcoord * ac, double r0, double scale, double xy0[2], int b, int num); +void ac3_draw (atcoord * ac, rendpars rend); // ac3_print.c -void ac3_print (atcoord * ac, double xy0[2], int b); -void ac3_print_xyz(atcoord * ac, double xy0[2]); -void ac3_print2fig(atcoord * ac, double xy0[2], int b, double * v); +void ac3_print (atcoord * ac, rendpars rend); +void ac3_print_xyz(atcoord * ac, rendpars rend); +void ac3_print2fig(atcoord * ac, rendpars rend, double * v); // bonds.c -void bonds_fill(double rl, double bmax, atcoord * ac); +void bonds_fill(bondpars bond, atcoord * ac); // get_atpar.c double getradius(int q); @@ -157,7 +97,7 @@ void drawvertices (double * v, double scale, double xy0[2]); void drawshell (double rmin, double rmax, double scale, double * xy0); int savepic (char * s); // xinput.c -int process_x_input(drawpars * dp, void * event); +int process_x_input(char input_text[STRLEN], void * event); // tools.c void obj_free(object * ent); @@ -175,5 +115,5 @@ int main (int argc, char * argv[]); // api.c void PRINTOUT(FILE * f, char * format, ...); -object * READ_FILES(drawpars * dp); +object * READ_FILES(allpars * ap); int SHOULD_PRINT_MAN(int argc); diff --git a/src/v/x.c b/src/v/x.c index 6448a2f..779349e 100644 --- a/src/v/x.c +++ b/src/v/x.c @@ -247,7 +247,9 @@ static void autosize_font(char * fontname){ } void init_font(char * fontname){ - if(!fontname[0]){ + if(!fontname){ + styp s; + fontname = s; autosize_font(fontname); } fontInfo = XLoadQueryFont(dis, fontname); diff --git a/src/v/xinput.c b/src/v/xinput.c index d21c496..f676fac 100644 --- a/src/v/xinput.c +++ b/src/v/xinput.c @@ -3,27 +3,30 @@ extern Display * dis; -int process_x_input(drawpars * dp, void * event_){ - XEvent * event = event_; +// TODO///////////////////////////////////////////////////////// +int process_x_input(char input_text[STRLEN], void * event_){ + XEvent * event = event_; // TODO FIXME move x declarations to x.h int keysyms_per_keycode_return; KeySym * keysym = XGetKeyboardMapping(dis, event->xkey.keycode, 1, &keysyms_per_keycode_return); - int input_length = strlen(dp->input_text); + int input_length = strlen(input_text); if(!((keysym[0]>='0' && keysym[0]<='9')||(keysym[0]>='a' && keysym[0]<='z'))){ if(keysym[0]==XK_Escape){ + XFree(keysym); return 2; // only stop input & clean } else if(keysym[0]==XK_Return){ + XFree(keysym); return 1; // use the input } else if(keysym[0]==XK_BackSpace){ if(input_length>0){ - dp->input_text[input_length-1] = 0; + input_text[input_length-1] = 0; } } } else{ if(input_lengthinput_text[input_length] = keysym[0]; + input_text[input_length] = keysym[0]; } } XFree(keysym); From abd40cf31df67e6368974b72fa6ef11608bc0722 Mon Sep 17 00:00:00 2001 From: Ksenia Date: Sat, 14 Mar 2026 21:11:14 +0100 Subject: [PATCH 13/18] Refactor drawing edges --- obj/v/x.d | 2 +- src/v/ac3_print.c | 21 +++++++++----------- src/v/cli.c | 15 +++++++-------- src/v/x.c | 49 ++++++++++++++++++++++++++--------------------- 4 files changed, 44 insertions(+), 43 deletions(-) diff --git a/obj/v/x.d b/obj/v/x.d index c409187..2f1e1a0 100644 --- a/obj/v/x.d +++ b/obj/v/x.d @@ -1,2 +1,2 @@ obj/v/x.o obj-pic/v/x.o: src/v/x.c src/v/v.h src/mol/mol.h \ - src/mol/common.h src/v/pars.h src/v/x.h + src/mol/common.h src/v/pars.h src/v/x.h src/math/vec2.h diff --git a/src/v/ac3_print.c b/src/v/ac3_print.c index 8f6986e..f0c5ac5 100644 --- a/src/v/ac3_print.c +++ b/src/v/ac3_print.c @@ -77,18 +77,15 @@ void ac3_print2fig(atcoord * ac, rendpars rend, double * v){ if(v){ #define LINE(I,J) PRINTOUT(stdout, "bond %3d %3d % 3d\n", (J)+n+1, (I)+n+1, -1) - LINE(0,1); - LINE(0,2); - LINE(0,3); - LINE(1,4); - LINE(1,5); - LINE(2,4); - LINE(2,6); - LINE(3,5); - LINE(3,6); - LINE(4,7); - LINE(5,7); - LINE(6,7); + for(int i=0; i<8; i+=2){ + LINE(i,i+1); // || z-axis + } + for(int j=0; j<2; j++){ + for(int i=0; i<2; i++){ + LINE(i*4+j, i*4+2+j); // || y-axis + LINE(i*2+j, i*2+4+j); // || x-axis + } + } #undef LINE } diff --git a/src/v/cli.c b/src/v/cli.c index 4673a66..f9d6794 100644 --- a/src/v/cli.c +++ b/src/v/cli.c @@ -72,14 +72,13 @@ static int sscan_cell(const char * arg, cellpars * cp){ r3cp(c, cell+6); } - r3sums3(cp->vertices+ 0, a, -0.5, b, -0.5, c, -0.5); - r3sums3(cp->vertices+ 3, a, +0.5, b, -0.5, c, -0.5); - r3sums3(cp->vertices+ 6, a, -0.5, b, +0.5, c, -0.5); - r3sums3(cp->vertices+ 9, a, -0.5, b, -0.5, c, +0.5); - r3sums3(cp->vertices+12, a, +0.5, b, +0.5, c, -0.5); - r3sums3(cp->vertices+15, a, +0.5, b, -0.5, c, +0.5); - r3sums3(cp->vertices+18, a, -0.5, b, +0.5, c, +0.5); - r3sums3(cp->vertices+21, a, +0.5, b, +0.5, c, +0.5); + for(int i=0; i<2; i++){ + for(int j=0; j<2; j++){ + for(int k=0; k<2; k++){ + r3sums3(cp->vertices + (i*4+j*2+k)*3, a, i-0.5, b, j-0.5, c, k-0.5); + } + } + } double rot_to_lab_basis[9] = {a[0], b[0], c[0], a[1], b[1], c[1], diff --git a/src/v/x.c b/src/v/x.c index 779349e..f619102 100644 --- a/src/v/x.c +++ b/src/v/x.c @@ -1,5 +1,6 @@ #include "v.h" #include "x.h" +#include "vec2.h" extern Display * dis; extern int screen; @@ -279,40 +280,44 @@ void textincorner2(const char * const text1){ void setcaption(const char * const capt){ XStoreName(dis, win, capt); + return; +} + +void draw_edge(double vi[3], double vj[3], double scale, double xy0[2]){ + int iw = (vi[2]>0.0 || vj[2]>0.0) ? 0 : 1; + double pi[2], pj[2]; + r2sum(pi, xy0, vi); + r2sum(pj, xy0, vj); + XDrawLine(dis, win, gc_dot[iw], + W/2+scale*pi[0], H/2-scale*pi[1], + W/2+scale*pj[0], H/2-scale*pj[1]); + return; } void drawvertices(double * v, double scale, double xy0[2]){ double d = MIN(H, W)*scale; - int iw; -#define LINE(I,J) \ - iw=( v[(I)*3+2]>0.0 || v[(J)*3+2]>0.0) ? 0 : 1; \ - XDrawLine(dis, win, gc_dot[iw],\ - W/2+d*(xy0[0]+v[(I)*3]), H/2-d*(xy0[1]+v[(I)*3+1]),\ - W/2+d*(xy0[0]+v[(J)*3]), H/2-d*(xy0[1]+v[(J)*3+1])); - LINE(0,1); - LINE(0,2); - LINE(0,3); - LINE(1,4); - LINE(1,5); - LINE(2,4); - LINE(2,6); - LINE(3,5); - LINE(3,6); - LINE(4,7); - LINE(5,7); - LINE(6,7); +#define LINE(i,j) draw_edge(v+(i)*3, v+(j)*3, d, xy0) + for(int i=0; i<8; i+=2){ + LINE(i,i+1); // || z-axis + } + for(int j=0; j<2; j++){ + for(int i=0; i<2; i++){ + LINE(i*4+j, i*4+2+j); // || y-axis + LINE(i*2+j, i*2+4+j); // || x-axis + } + } #undef LINE return; } void drawshell(double rmin, double rmax, double scale, double xy0[2]){ double d = MIN(H,W)*scale; - rmax *= d; - rmin *= d; + double r[] = {rmax*d, rmin*d}; int x = W/2+d*xy0[0]; int y = H/2-d*xy0[1]; - XDrawArc(dis, win, gc_dot[0], x-rmax, y-rmax, 2*rmax, 2*rmax, 0, 360*64); - XDrawArc(dis, win, gc_dot[1], x-rmin, y-rmin, 2*rmin, 2*rmin, 0, 360*64); + for(int i=0; i<2; i++){ + XDrawArc(dis, win, gc_dot[i], x-r[i], y-r[i], 2*r[i], 2*r[i], 0, 360*64); + } return; } From 16ef253836bbc66c15579fd199be8b978eb48e49 Mon Sep 17 00:00:00 2001 From: Ksenia Date: Sat, 14 Mar 2026 21:28:27 +0100 Subject: [PATCH 14/18] Get rid of magic numbers (2) --- src/mol/common.h | 3 +++ src/mol/inertia.c | 4 +++- src/v/ac3_draw.c | 3 ++- src/v/cli.c | 6 +++--- src/v/evr.c | 7 +++++-- src/v/man.c | 2 +- 6 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/mol/common.h b/src/mol/common.h index bc7da6d..f25a54e 100644 --- a/src/mol/common.h +++ b/src/mol/common.h @@ -6,6 +6,9 @@ #define BA 0.5291772 #define AB 1.88972616356109068947 +#define S_TO_MS 1e6 +#define MS_TO_S (1.0/(S_TO_MS)) + #define CLOSE0(F) {if(F){ fclose(F); F = NULL; }} #define FREE0(PTR) {if(PTR){ free(PTR); PTR = NULL; }} diff --git a/src/mol/inertia.c b/src/mol/inertia.c index 404a262..1ef7450 100644 --- a/src/mol/inertia.c +++ b/src/mol/inertia.c @@ -3,6 +3,8 @@ #include "vec3.h" #define EPS 1e-10 +#define EIGEN_EPS 1e-15 +#define EIGEN_NIT 20 static inline void swap_ev(double d[3], double I_b[9], int i, int j){ double td = d[i]; @@ -68,7 +70,7 @@ void position(mol * m, double d[3], int preserve_chirality){ I_t[mpos(1,2)] -= tm * (y*z); //Iyz } double I_b[9]={1,0,0, 0,1,0, 0,0,1}; - jacobi(I_t, I_b, d, 3, 1e-15, 20, NULL); + jacobi(I_t, I_b, d, 3, EIGEN_EPS, EIGEN_NIT, NULL); //sort ev if(d[0]rot_to_lab_basis, rot_to_lab_basis); mx_id(3, cp->rot_to_cell_basis); - mx_inv(3, 3, cp->rot_to_cell_basis, rot_to_lab_basis, EPS2); + mx_inv(3, 3, cp->rot_to_cell_basis, rot_to_lab_basis, EPS_INV); cp->vert = 1; return 1; @@ -151,7 +151,7 @@ static int cli_parse_arg(char * arg, allpars * ap){ } if(tf>0.0){ - dp->anim.dt = ceil(tf*1e6); + dp->anim.dt = ceil(tf*S_TO_MS); } if(bmax>0.0){ diff --git a/src/v/evr.c b/src/v/evr.c index 7b97f2a..4d076c0 100644 --- a/src/v/evr.c +++ b/src/v/evr.c @@ -3,12 +3,15 @@ #include "evr.h" #include "vec3.h" +#define EPS_INV 1e-15 + static const double step_rot = M_PI/90.0; static const double step_move = 0.2; static const double step_zoom = 1.1; static const double step_r = 1.1; static const double step_mod = 0.03125; static const double rl_move_pbc_scale = 0.9; +static const double vibration_amplitude = 0.1; static void redraw_ac3(object * ent, drawpars * dp){ atcoord * ac = ent->m[dp->n]; @@ -44,7 +47,7 @@ static void redraw_vibro(object * ent, drawpars * dp){ bonds_fill(dp->bond, m); } - vecsums(m->n*3, m->r, r0, dr, sin( dp->anim.t * 2.0*M_PI/TMAX ) * 0.1*sqrt(m->n) ); + vecsums(m->n*3, m->r, r0, dr, sin( dp->anim.t * 2.0*M_PI/TMAX ) * vibration_amplitude*sqrt(m->n) ); for(int j=0; jn; j++){ double v[3]; r3mx(v, m->r+3*j, dp->rend.ac3rmx); @@ -277,7 +280,7 @@ static void mol2cell(double r0[3], drawpars * dp){ double mat[9], r[3]; veccp(9, mat, dp->rend.ac3rmx); r3cp(r, r0); - mx_inv (3, 1, r, mat, 1e-15); + mx_inv (3, 1, r, mat, EPS_INV); double rcell[3]; r3mx(rcell, r, dp->cell.rot_to_cell_basis); for(int i=0; i<3; i++){ diff --git a/src/v/man.c b/src/v/man.c index dd2ead7..fec45eb 100644 --- a/src/v/man.c +++ b/src/v/man.c @@ -71,7 +71,7 @@ void printman(FILE * f, char * exename){ \n\ q / esc quit \n\ \n\ -", exename, DEFAULT_TIMEOUT*1e-6, DEFAULT_SYMTOL); +", exename, DEFAULT_TIMEOUT*MS_TO_S, DEFAULT_SYMTOL); return; } From c8f99d2a999b31d91eedb7a3ae3b0132f3da241d Mon Sep 17 00:00:00 2001 From: Ksenia Date: Sat, 14 Mar 2026 22:09:25 +0100 Subject: [PATCH 15/18] Refactor main loop --- src/v/loop.c | 136 ++++++++++++++++++++++++++++--------------------- src/v/v.h | 4 +- src/v/xinput.c | 8 ++- 3 files changed, 82 insertions(+), 66 deletions(-) diff --git a/src/v/loop.c b/src/v/loop.c index 131b300..1dd4971 100644 --- a/src/v/loop.c +++ b/src/v/loop.c @@ -8,15 +8,73 @@ extern int W,H; extern Display * dis; extern Window win; +typedef struct { + int click; + int x0; + int y0; +} mouse_state_t; + +static void process_mouse(XMotionEvent * event, object * ent, drawpars * dp, mouse_state_t * mouse){ + if(mouse->click){ + int x = event->x; + int y = event->y; + rot_ent_pointer(ent, dp, x-mouse->x0, y-mouse->y0, POINTER_SPEED/MIN(W,H)); + exp_redraw(ent, dp); + mouse->x0 = x; + mouse->y0 = y; + } + return; +} + +static void process_input(XKeyEvent * event, drawpars * dp){ + int stop_input = process_x_input(dp->ui.input_text, event->keycode); + if(stop_input){ + if(stop_input==1){ + switch(dp->ui.input){ + case(1): + { + int frame = atoi(dp->ui.input_text); + frame = MAX(1, MIN(frame, dp->N)); + dp->n = frame-1; + }; break; + } + } + memset(dp->ui.input_text, 0, STRLEN); + dp->ui.input=0; + } + return; +} + +static void run_animation(object * ent, drawpars * dp, int * tr){ + if(dp->task == AT3COORDS){ + dp->anim.dir > 0 ? kp_frame_inc(ent, dp) : kp_frame_dec(ent, dp); + usleep(dp->anim.dt); + } + else{ + /* We draw 5 times for each dp->anim.t, + * because dt is too small to look good + * and 5*dt is too big to behave well (keyboard control). + * Also we cannot draw only when *tr==4, + * because we need an XEvent to reiterate the main loop. + * Alternatively, we can send an event manually. + */ + if(++*tr == VIBRO_SUBSTEPS){ + *tr = 0; + dp->anim.t++; + } + usleep(dp->anim.dt); + time_gone(ent, dp); + } + return; +} + void main_loop(object * ent, drawpars * dp, ptf kp[NKP]){ // To handle window closing. Thanks to https://stackoverflow.com/a/1186544 Atom wm_delete_window = XInternAtom(dis, "WM_DELETE_WINDOW", False); XSetWMProtocols(dis, win, &wm_delete_window, 1); - int mouse_click = 0; - int mouse_x0 = 0; - int mouse_y0 = 0; + mouse_state_t mouse = {.click=0, .x0=0, .y0=0}; int tr = 0; while(1) { XEvent event_rec; @@ -35,37 +93,25 @@ void main_loop(object * ent, drawpars * dp, ptf kp[NKP]){ } while(XEventsQueued(dis, QueuedAlready)); if (event->type == ClientMessage) { - if ((Atom)event->xclient.data.l[0] == wm_delete_window) { - kp_exit(ent, dp); - } + if ((Atom)event->xclient.data.l[0] == wm_delete_window) { + kp_exit(ent, dp); + } } else if(event->type == Expose && event->xexpose.count == 0) { exp_redraw(ent, dp); } + else if(event->type == ConfigureNotify){ W = event->xconfigure.width; H = event->xconfigure.height; dp->rend.xy0[0] = dp->rend.xy0[1] = 0.0; exp_redraw(ent, dp); } + else if(event->type == KeyPress) { - if(dp->ui.input){ //////////////////////////////////// TODO move to function - int stop_input = process_x_input(dp->ui.input_text, event); - if(stop_input==1){ - switch(dp->ui.input){ - case(1): - { - int frame = atoi(dp->ui.input_text); - frame = MAX(1, MIN(frame, dp->N)); - dp->n = frame-1; - }; break; - } - } - if(stop_input){ - memset(dp->ui.input_text, 0, STRLEN); - dp->ui.input=0; - } + if(dp->ui.input){ + process_input(&(event->xkey), dp); exp_redraw(ent, dp); } else{ @@ -80,32 +126,28 @@ void main_loop(object * ent, drawpars * dp, ptf kp[NKP]){ (event->xbutton.button==Button1 || event->xbutton.button==Button2 || event->xbutton.button==Button3)){ - mouse_click = 1; - mouse_x0 = event->xbutton.x; - mouse_y0 = event->xbutton.y; + mouse.click = 1; + mouse.x0 = event->xbutton.x; + mouse.y0 = event->xbutton.y; } + else if(event->type == ButtonRelease && (event->xbutton.button==Button1 || event->xbutton.button==Button2 || event->xbutton.button==Button3)){ - mouse_click = 0; + mouse.click = 0; } + else if(event->type == ButtonPress && event->xbutton.button==Button4){ kp_zoom_in(ent, dp); } + else if(event->type == ButtonPress && event->xbutton.button==Button5){ kp_zoom_out(ent, dp); } else if(event->type == MotionNotify){ - if(mouse_click){ - int x = event->xmotion.x; - int y = event->xmotion.y; - rot_ent_pointer(ent, dp, x-mouse_x0, y-mouse_y0, POINTER_SPEED/MIN(W,H)); - exp_redraw(ent, dp); - mouse_x0 = x; - mouse_y0 = y; - } + process_mouse(&(event->xmotion), ent, dp, &mouse); } if(dp->ui.closed){ @@ -113,31 +155,7 @@ void main_loop(object * ent, drawpars * dp, ptf kp[NKP]){ } if(dp->anim.dir){ - if(dp->task == AT3COORDS){ - if(dp->anim.dir > 0){ - kp_frame_inc(ent, dp); - } - else{ - kp_frame_dec(ent, dp); - } - usleep(dp->anim.dt); - } - - else if(dp->task == VIBRO){ - /* We draw 5 times for each dp->anim.t, - * because dt is too small to look good - * and 5*dt is too big to behave well (keyboard control). - * Also we cannot draw only when tr==4, - * because we need an XEvent to reiterate the main loop. - * Alternatively, we can send an event manually. - */ - if(++tr == VIBRO_SUBSTEPS){ - tr = 0; - dp->anim.t++; - } - usleep(dp->anim.dt); - time_gone(ent, dp); - } + run_animation(ent, dp, &tr); } } } diff --git a/src/v/v.h b/src/v/v.h index 7755e8f..4c4edba 100644 --- a/src/v/v.h +++ b/src/v/v.h @@ -88,7 +88,7 @@ int get_element(char * s); // x.c void close_x (void); -void init_x(const char * const capt, const colorscheme_t colorscheme); +void init_x (const char * const capt, const colorscheme_t colorscheme); void init_font (char * fontname); void textincorner (const char * const text1, const char * const text2); void textincorner2(const char * const text1); @@ -97,7 +97,7 @@ void drawvertices (double * v, double scale, double xy0[2]); void drawshell (double rmin, double rmax, double scale, double * xy0); int savepic (char * s); // xinput.c -int process_x_input(char input_text[STRLEN], void * event); +int process_x_input(char input_text[STRLEN], unsigned int keycode); // tools.c void obj_free(object * ent); diff --git a/src/v/xinput.c b/src/v/xinput.c index f676fac..0e0aab6 100644 --- a/src/v/xinput.c +++ b/src/v/xinput.c @@ -3,16 +3,14 @@ extern Display * dis; -// TODO///////////////////////////////////////////////////////// -int process_x_input(char input_text[STRLEN], void * event_){ - XEvent * event = event_; // TODO FIXME move x declarations to x.h +int process_x_input(char input_text[STRLEN], unsigned int keycode){ int keysyms_per_keycode_return; - KeySym * keysym = XGetKeyboardMapping(dis, event->xkey.keycode, 1, &keysyms_per_keycode_return); + KeySym * keysym = XGetKeyboardMapping(dis, keycode, 1, &keysyms_per_keycode_return); int input_length = strlen(input_text); if(!((keysym[0]>='0' && keysym[0]<='9')||(keysym[0]>='a' && keysym[0]<='z'))){ if(keysym[0]==XK_Escape){ XFree(keysym); - return 2; // only stop input & clean + return -1; // only stop input & clean } else if(keysym[0]==XK_Return){ XFree(keysym); From 17a50e9431ad6b119f0a3dc10af03eae67ea0bab Mon Sep 17 00:00:00 2001 From: Ksenia Date: Sat, 14 Mar 2026 22:43:49 +0100 Subject: [PATCH 16/18] Refactor color palettes Fix: Mt color (fcc3bc7) --- figures/periodic.gif | Bin 90856 -> 91034 bytes figures/periodic2.png | Bin 33127 -> 33176 bytes obj/v/x.d | 3 +- src/mol/palette_cpk.h | 110 ++++++++++++++++++++++++++++ src/mol/palette_v.h | 25 +++++++ src/v/x.c | 164 ++++++------------------------------------ 6 files changed, 158 insertions(+), 144 deletions(-) create mode 100644 src/mol/palette_cpk.h create mode 100644 src/mol/palette_v.h diff --git a/figures/periodic.gif b/figures/periodic.gif index 480bb1a7925207146345b7f5f869d96af972b429..600ff3011f623f09a46013034a7471d8061542d6 100644 GIT binary patch delta 20553 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 AYybcN delta 20328 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 literal 33127 zcmdSAXH*ki+xMMLjWhwJDhMduLQxS!MHB>V6sfVI(t?Ua2xI~ZDk2sH6%cHoCkRml zq97UcR4g<_ng|&M=_OJVNO|{op4W3<>;3Sob$@x+dReTQJ$rWFliA1q9sjwo)7_aY zt09ZQV949IZP|^%NI@73UMP(RF;|-R1c1|$gWGnyVK6a<7z}h4gP8+S&;SM#Wr@KI zQ81WwH!&D>YWB-rb{GtH%g!Bpog@+oNWuTALn)L#ap%6F|}PsR_Eszj-9`?vuS5# z^y)Z>^*Sx>NLtz}s7Lab(qCIJRC5+af=QQ1U>HtE5J^W`e$>g6uRrW9D?0)~cMg_e zj-C4&gGtAvufKCgHty20w6slkSl;Ssm=Bozy>8z7_TTX+EA+=ic{puK$Z&ftVY8du z@+Feb`b*PW4dV(8k7TItO>SyJHerrn;xKiXeV;KN9T+DBW7~%@zjf=@en~C$E9i!; zL{i%!sjZdN-j~#-Not)WwU&}vT}drQl8xz$!hGG0DO^S^oWs17VD3sVX%Y-1!GuWm z`cKgOdoFtP+_nB%stf*KmXh;dk&|mNN0@FVPr)E!54bvS!JH>*Zvz)nr?$H}Ne$s; z$?8V)&VL1BF!93eTO9Y&zKy;*bAQdJUAY}HiE72<1M;LSCzz$LhJSP9-FwQrBO7lz zL04EcH*>}%lJE9q-d=~dZrOS&`t$~c%#%T{D(1X9_I+wr@{F20jZXXj=8qP14Y9bs z?ESOi^xn_U4rh5`F&N(R1=Hz-PqZSFuDvJJO~1VaPZyMYB|HjiWR&=@=e6%Hd~FF1 zgF%!YY+h?nv+9id6L)u;6z#ST_x2BXqXiJI4MsdOn0eB@W@nvJN92v{sCPMkN~UEK zOnvK?QGQ?R^90SbyR%m$_Y^8~{XjGglEINMko~qBlDL`Z8wNt(rhbb8A{m1bDKA4N zb{|tZIC`U1*5@hO7^&;(SGTk3c3lY%n2f8RcD~fyH@&p*f79>(r1d@?boqJE9513# z()?s17ep6doY16wk^!M5cq|yszfX+Y6g=H;H+x4tZh_=iIlWfID_CRmSMyNFcxHB5 z+b_wn($=+$_u(<{^tM7qcxUb>iB?YKT*->A%YoO*y%`FtOAmcKjKQ#V^sprvRZsS2 zfBcmFXx7jVRDtBIkfk+Wbw6lv##4C2!Vv~>dM>!`7~3aq7a%Z{zKSw@M1yDeqgsSA@9`Z$$?r|E7)W0#2$3^M+PD2e z2WXi0k+Pk=+P??C(wlAKRIY=8>aIIpv(lN)R|lg8nPe4ao!Y0cNSFe80bhTv$}3Ht zyoff0XY?>ae8jiONfEq%_@%0r(T@F>#G?G*G_;Hhl?;xcBas%FFAJIur=`g*a`}@> z3hRxI4x-RTAKu~3bU60`Jb^~Ei3*q7xSQzxbANLdq2sK6ixEe@^a+bWhNP#54^!gnr9iA(PmmP8u}`F? z1s1J z@Bz>930!Qvyd|x#CX>X zmogkG=S;?rVAM7_bz@?^Dftn}_;uD9O=aQ{>cgBu(-xV5ma{ zhw-2ca~tc7k{kJpX}`FQ-TA8Y*2(PE^U(&Bag+93$r2ad9;SAj-c}f&royYpmZrVB z76Bf2?`7Ju{N~)|o^9sC{1H851+VlnDNs27Vpr9Nibpp^^MWNc+&12mT;<2_p1#&e zVnlvy#FX?IjrujY;B)0{Q-9Ce5#*`cPm8-eVRHlOI#pIs^8CpZ$f5O0c<$t@P)ec5G=rT0sKN#iKHJ|()ICY(9_e+lgnUv0sSL04w z^4@XQV`Bpjbj$MZ*Ue`K$z@F0y~L7hYVgN+sdIK%ODmDS2ZRws^uS`&D_{ zb`q%1n$uY* zIN=UnhdtKi{fUm|S>>Th{X63IH@*#Gv)_q73&UgmJ!Q<_7B~{5o_ByU%O#EmVRAFz zr5w3+F7UB_ZXTDI$|&fdU4kBxj$Ft1Urm;_by9OmW~tf! zV;)kn#L%x?B+W?d5eUa`haI_6ReYP?d8kKNxr(}l7GdWOE8|OdVT!jq!S;pQ?us|8 zVok0Hi=aqWA8|>4q|mzymvI9Qjf7HmTe1$O*%o6rJX!jMlc?J^mpY;}@4PwbGd%P} ze+9Fn599r-N@g@Va9b>~GS={ks@8#(E?+Bt?!u|1!y_J;wqJE{b@a+7WUafb^n2tB z@!SiCR4^5F~% z{7GAke@;%q)n!#146&MR>9TXbw&B_9R=k}(j}4S5V~#q%y_K-G34`EAWD+OOaZd~HDf`;kxSD{iK}1gov*8eDk!VHf{?unzGzhEvo74g`UBvciLaqi2C7 zfn2DA`ReFv_5zp>8-!V9BaVMNUhH3Ur4Zm zBIuZCd3$suuT% z1R1#QL$&WmoGB3-3vV|WgS^6#Ggzqh1iHMrn+W#43c)SZ3fM2z-@JsTwA z!$pX~bg@j+yn3rG!=Wnx6zt%*bHBq@_@x{ST6N;9s52Z{ExIgwI!4n^w9dq~xuhgv zwTQHwwrbpHIlGIahu0Lks%Rjawh23rBi9%?^7IFwm4VGu=k$g4_Vik%%NbhVE2&{b zhb?dh?xyl0WZipV=Mm%?BS46TDv1z!*P-V)s1NRh-&qMm7fs+AG0G4jE<^+~Wq*nPzx+Yt1y zN;X@AaEXcS&b=-N3lg|T%$e7B!sLSNk-2eUP~R||S~_*^9>i%fM#iPpsGHz<86|02 z`=dg)LN)53SE1b<&AV80Cu3>b`jqdI7CodFQ9iHURxwiA$oaCEZHS!e42%Jbedf=lIPu71$eZ38h|{%$ z)>IC6YRl7km@jr>i|3kr1pAe4yZ8U71~5^E={dveXuiJr;3?(mIZ$l+(RmSj*@ zYz5Y9#qDMk(XqRR$VwHggA@Gt_fTxXHlm$)b@lrIc-ZQJCRmCV7H|@KtQj+$C&bX@ z%rpkEc20vAwvg2VJ9C)&A&xeS_yac7Lv7F~$$pNk^#bDDA8pG!gu7!i-X!(Pcde-! zFXlLo09IN|=EG%3&XQNes%5p(v}l!#N~reg>{7(z1h!zhD?pkrG%Q+E0ajRy?Zn@* zwC$G*p$KW3&l{t2vb0wQ=82nNSM_x};AXRNu*SJ$%hBR8SK;Eql~ zL54+$ubl_HFFqL8?U2-Kij1sWMi{xc8*UE!cvaQX>?oA7REbABAzdjGBHSE|TTm?% zGMQpK#@Gnod0&0el9A&AmqqCLo2?aZfidbog(=dLhf{I(0-e`%$5KdvlSCDljN@q$>egKH#VQzn#nl}reWbS$Clil4<5FXo@%)cGaKf0M_k}Y&mmn^}QX>5YuDu@qbpcG) zPGl)AV==o1hr`>*nl^zqhs~Wd6H+zOp@YT4rJtwso^bv$F2`(z6ReiWB3+m4w!mKa zph#dGq$NxH96<8-xVOOdIJv137_KkqxyH0O^UTAXRO}Xz2<2{Y#3-ia6Uy&AK8FjB zRacc*dtU(uSzAwGC*9s$mTgt#oK5ie16zIQFm*26loa#IlRJl;@s?a zIG%XCh*4|ts&jDmb-~;bysKg3Zd_1IA;zD4fvFvS5LAR@>WgA>>cOfnnrOb7kfVBQ z#`Z{b?Pdb_(O6XP!xV4rg3T*dK3pw|F<#}u*%N;yzJR|?aM%6rm2K z>{`w8PP26yiuQGWa4=!=@lM_Cou%;4C->J_YHB0L)5#<4`tG;l^|jSj@3|@%m}Rb! z^{G-zWp<2U+Vpcw+Fv;DNR=wtl*m+#Eq(`UDPb~ux72iB`@5RAsy(aeuN?1i%4HcS zWtHv9Qi}ot&98{$rK7qDf0Bjuzquu4QSrHYc&=yDJ<4TkC?zre$8I0_(6yGk-D<>59nQ zRlo$=v#}cF{@?uZGI$2S0Kudsj_aXN!%}lpoVyL($=>-;xz-j9oL?;nUnU%J1Qu15 zx|Qf21tgCqfE8XiQiH}x9r!2CX3dPTsKFeehUvj46zBj6(C&ML5LZW^w5EFBm- z%U!Y`kQ!rai7(miVzh!RKvF6Dr%OJ-wKD;0+vn>y#GVG-TJUtZEe}bEQHc_cQaZJEzbL12j>7jt?ad*g}wXDV!q_pcdOn-z|CC|hoh7RT}WlgG&ol4?Xa zo)%?!AUXQo2hOzN^KOnB{*$mX6v5A%FF5_pJbE%ba{UKwVEnQ6h@*9X+>~r^rn&E& zoAvuXEq>+qeUcNA5-XmKxUh$Ql$nbBxWE=v9HFEzT1f7@CFNyzJ*J2A|CBW&EvX2$ zEkgEk+-2=sN-`4^Mk;ODR`fbax4zaSXI#2gIPUP;BRz%2Sn{#vRMU?^npIw*+tDn> z4N}H%#Y1WHq3FHTNw2l7CCLdXV0ORXmUJ0tO>hdf3{T|_wQa8{n2XxYdmNv$kp5SY z15u%`3km(;R23xeMeU!3f7dKq=qyolr4%weNv(pmDGDV#}1n++j zzJ$!F^~UWw_PvY(qYc{kFc@kPCjU~BslUeHkBpqN(3a3xvK z&xTV3mAeGs^eMe%s?(RyChR_@9Gj3UZs_NA{nLC}f6+!>p+F9I)bk9)E!3 zj~pfk)m>QelwrB`z{w$nbG?ms9WEu}&!j_|LK7R-w|}ODf*hEC(E+cLd?TAqmUR6I z_=h^IU&L;YKW#~UDjGpkbkxHA=-^!0z)7~y>G3-tL2g3}<`kYp*6V()G~7{>IrFlV z;Xw|K*l2RQ=^N?!V;QsQk`7o|v_RH6KLtE)tgjp|ysHQVwr~;?%t|d*FpP*xc=iS`k$1I9gmu*^i2+F*-}<+ohHGWmHJsPV z?LULcn7_nj8a@L-S!iIf&)A=U6&h0t%y%oze*>+NS9(x45^L2;8e24I_NH*8f z;^_Pz2lafXa{Ya3#>5*|QtvYkldJ{a9cNql=$IQS&9xn%q%y`y*1ajBSR2;Tpb>5* zlU!;sRq`1=AkiS3&io1Zl1+a{WPu5YvgEyB#L3ggKux*xZJ%M?p@5UyiJa?Ds<>sb z&##~q=0oz)&M3+a2ASj~XrufIWLW1tX+v)eM^TR9zr|?#e1pn^q}0%f-#jy=R_$|t z7tufxvxwabfLf+-_%O1A)3%o?t3+Mr<8hxsB$Zp`-N3b~!byhD-*IB_!?Kh(6<=@6 za8}bxoDUat=TAS(NdE@HNNRI!;gkyuO|t*j@jOZ_-&w41&0Nux8lL`e+2v}fFat){j}I;DI%~UVdaEbX>K9NGidUc8P0w-qPpkAb@wsz0L0=CcdnA1+x8>7poriVGEU+y-UKES0v z1rsya6WcyNb9Cv!x&4xk)sj5P$U0%9cF3Uh;i%Mn%$=Y^%bB`M5f^MW2i}P@x(-Fj zN-w0YlJRNDwUPF@5uwOBo}frw>);PNEKy@UmWlJ1^nHr^9E#i@<|Mn``jS>6S=Xl< zAeq(@^M^bXd6p^)O03?+2%Y)@GT~0~q_K=lU-&O&eie3u#h0eD=4Y!W=nFq}FAQnh|P>+XW}8>8-(Zg1}qJ2IhBw}Ja0 zefkTTIFwT!KfWng6db#E+-?Uv?OG*3PI)s!yrD_&Ma4LM#Kj(G-mi~*l#SoGl(Jcd zegbP7#7k*a=JRmW>YIX9ydOk_z7jk>Z@@<9fLkWBbePlkU~HER?dI==Mo5?2KsT_D zhn}cY2Nx6#KAzbBV&3CToX@%StiHhtZnEb~yh(1nD9-00i1^VsF#g84i&=I@j0I63 z&BWl!@5!EQe8`3koA3RwFM6%*BjA%~_p6S<9}BoAbiItFY2Iny#@@K}{;ZLnKNEXRpK)Nm_7f8xqqS1)R@|*mH{1I~*0(fKAw| z5~x%$0?Qb&?~=9s#8cymwZPuN)L;`lygUdSoDiu-&EP(Sya_09C1Js)D?Ltl?A522 z>R)l2X#Mxtttdy*eCaLXci@rd}K5cgNDbuwP)2RDJlG6ge4TdrYxA$o7 zbdd9>llVP^^rX0>H{+*E(L0m%h4E_aDS4_+2?IUzXz}i!y6^uJJ~A0E{0ef#kZ2d~ zSeN|RhfIPTgEkxFvW;+-Mv^3BPj;9@v`K-Gn|amIr3|zDs@7Q zM_+pI+t>X#9ZAz;sMfb72yS-h`8;BV2yj9+F@a$Bwo;CEQf+7#e4Ucy;4DMasvd0H zpCevynI2Pp)P(5~FID^H=bedEPHInYG_}{rrZb|)R-mm_7y|dWjDT3M3oQDEKPWb8 zvt;dt2OU*;JiTaa+SAF4io6R@vaT3c9kxux6B})UJlytDGT;s$%Nn;pJU3|QA$O-V zCcB7LO1<>~aPxO58Cv(V9)$>OLH$@F)acuk_JU;Bz4HQ8fz$D50vnf&axtq#Oq@=1 zu9#qF)%|iI_1iD!z3{Q_D$rNonLCeUX)Ottm3X#d47Q}?>q~DqYpjus&6R0CrT!dj z1v56n(zL9SN6?#Mtg|e;2TZwwGH)u`5}TH?PL6gk^Ss(ryig=VJ87~8XRpF@o6ury zhbKulFEX!WvAk+6arW`@gJy)00Bpo;gP4%59S|o&izT;ka1F(ux60TKXizhBzoAW- z>jk@=C5XTqaN+f1hDFZbiE$n$)p=LIUcYn3JU^VMby9um^!N^Jw4=u!hrU=j8spR% zIoh0)mLu*?2`25$tQybIf#?N0nhl6Pn2i_~?RO-MFvlvT`!kObP3MhY(`g#mHV)Xj zrh37@BJ}Dqz2(?^x3iPGy|9^_3zk8FCdh=1Qs1W#qQBViHT}#JD22P_rbV$D^*12a zZ(DHY@hbbP*}m{wkXgM=j?HsBD|oGj&1ERhC6qHntHP z!7GNM+1~K_D!`vrPLOHmB0w5{Z155af&P2}!N1NK{zWJF@5ADX`}7eT99Ew~sOr^6 zPT1p$OR(5BFDB%D{jMU5ln5C-mu7D*gj{LTY$*B9=`1+ef~yorj{VPxCe2@Hgw6e7 zfE*MO=$EpfwBIT`tn?zRZV?`~sln1d&twTzb#FHlMh+uCy^g7| zj#evDSL$LjL8}fErP;T+m5>3c`aI)}d{wRk!L(aPnw_2oeG+uZ`2(VDL_Z=e^8ePXX#KgW1wz|yu`;3$01 zJ5HJkE17AM8=WO@TJ0hAKSPqYk~OHa;sulZ`;D=l$i$i@QVx607}vb}Gl(!^LdjKT zKij9HPkF_Rs=WRU{-~B)!_i<4l8fqW_}*5IpsqzeIiTW1=JbZ2O*kJ?aya zjuY>%1{sU68~SRk@{;K{7}L1sMeXS&6D3Q7eg%CxX$KPiHv4O`1Q6E zb+Gzm#5Nha?YVlAnkfG-8aBk0(j2F^AGMy6gzs@a0XRQ-l>xB%OBFDIjoAK8f5CJ~ z9h|DOJo;{@!Je^AHvO5EP4<^}ILo?cPLBJ@v>&JZI=m^B88&-ii&b94H|6$pFrtTL zLRanZ$H%-+3)!e3h$IgzbzosPg#TcZrk*GHNHWMHOK)9cYEQV5PMZ z+e6o|coZ_)Pe?#~O?ccW8ibX!`NK;a||3mV030o%2U16%^9o#3}Cb^i^M2^2O z1tPny=ne%jnOTi3hw-s1OtpvGoL3L})C3B^p*y3hDn4S2WS>*J73<2~5sHuT2YwH; z#-A;!->>Nm1ib)rj2Er1i+?}O_580x(=@lm`1MDwU_wkwV7yqEuI zB6{EE2k`|_TdembPTo&ez!#IuL-k`-e4qX*b31x6>sdbXuB+?_l0UWAo7&9V6Hrqx zh!T~%?aX5glTN=^n2P9vM+=5d-J>{=<+pwq(Hz{?)|=dWkSldXi|qe>hZnxE{8a;N z7WR=f5goXBJLgH-_6Kwf zB%QQE=#$B_MLuVImc2;uyUf0q5U}9a9%AeRb=8qmNtahJGKHY5eU9nz1;XVbcfqc? zoC*B&`X_qrR|eDrF*f)%MnGvd%5#oWl2@6!bDA~2Fyp$$PG0&lp04|lyTV}yr&$(6 zy>f3JglonGQZrHY4y=*_;QI2%lo*~8DSp;$Khgd{e&mZmgVsoF{&`be5(_{7GVsuP zR!NAM8J82h+#T18hccN4&bh5{x&FEq)sd6M168iA^NZKT&)py0WRJ0x@p*2T%B-(F zstnl$ThmcYRYWQu zT&2DK<~mgj#yq(mc59T4ys8H1)8-2?BJ223g$W|yI2wx%1L8x=Gfr5s50IKFMUzD$ zG~yazL0D zH#KbF-anwp90g6EzT68stYq3$l9tfcE!u@P%mT=cev#CMDgtWm(`NGk{Gw@p6PZ}C z(5?9B_yGJz5AqSKLyBDw5C8lD-nLO-bIqo3-_}&`;omeP<{`VHHxkB0TK>ESKHr-{ zc@ht*{VQHuwazV&cdzL5M_svZj)}AJ3(VO#ckwGt_A{L_hM5y!0bsLl6y8^x2>j)X z@(uu&)FwJvRx8;8cXKWZKU6E2Up8OPHO7|7DaWy_Q|Q@Y$@zT?dt@Q@;nR7YdB~^r%fz+^6T+7 z5g-mzKKm^WiiwgRXcE_zA;}RGiK+cLF%eh*%LmqXL|a;VHRoSH*ACW;fdw zZ_%!+d|R<;u0vnzqcanDR{d8n{8A7X!SHTE?ojk@>L=2oNk*T%%^S&grFggWTnD|s z-dBg7$-xn_7qm6^f(OG4UE%L>-|4X*aZeM~r-vW>!F1tufh7kK^;Zf#e*oZiusio{ zz4Ab8RdLr~E^K)MIG6(b7PXVQi(`(0ytD*>`2IqEofkcnTi@QrsWqUMG7L~|*Fwse ztEhpkfIG81VhSCCicxQ)xv0H7N;f&s`S;`afQ0~RJffU@wv*GU=Q9LMo`>Qv%RgRU zzw)uhuSdmv;YovU4h}-POohIw=u47`aXSZNZ_3)BfawPw z(^agUfVjy`cT$kOvk|4tMie8jc-hBANi4YgjpGZF0b{6vV+fLkScCMZ(OcH6I!-Fd zz6}gv?5W_YtI#OK0EyqW$wSHbw~kD?b_(T~q$3ne zmm4W#zAYS(CX2lUDM54)&S4XrDpu8KM}R9oO6;lW(+|wux9D*H1U5JbEcWREqZ`#h zk{rL?1P=0{JkacQk|A=@Bg{*+Mj6~;;q%L&;Dbo5bbCF=01uqtbAS4YHshsP z@dP*x#kuHHdRCh9KsY}%6(Z-lFGNi#)NYw3AN5r6o z!}`eCicb-ubg|OrwXo8Pr%(7k5_Fa6Cic7mAOF*7(0dJZ7^Z+D0R-fWOdwCUqkec_ zBT6FUmjUYo=PzZSQP9yA^d;$yDMYznd+S-XxmgWT{3S?R6dR5tOM}LoW0sMR9#-+~ zgsj)x^%PrK-F+(t49Zj7g0`(C9X=v)A%{)@6KN~3`-;LghGd~Qw_4`OR7p9(Yp!iK z4v^3bhR@w$!EgXEyVisNx?EM%1v_E;Q?dy3p$_`~I2nIlL^bO%!G1cT`q5|DNFACa z0;q>x{R8z{G}xvH{Y((ZQz>g3^nDq!r?F2?(*;RjOrA3{_(K87fC7$JZ3A)+6!HbW zhCkwL6|UTXBfzc?n{82I!33JGiNa7rY~g~RG_2&F4?JXestCHi9sUx~Ut5P$Tg>*y z^nWsO{jc;Fah=O3Zjz5nC~j4b@He@=oMmdfd7Hbs`jlf*>(Vl3^9%r(ky9u5cmGUN zb`PCCxZ>k%h!A?qW24zM}aGnrJdG2~x!geR2QD5bOZ zC;nl7(H0pMEPS@+?*mgfj*#FL=`8gDS z|5~wMbsWB3>)^qe**XAJh_1r3Kei5_1a9i)*qe261l!J7Fi?auF%_QGlf(azJKgx( zMAOx*EpVq+8N|4!htz9s3&F~{ARo$V*4I4_{PReZ8r9mN0SBB7 zM4_uOmUBiXRD~B~bWH}lE_GiVzAn-?BH7c7q9o!0`kWB3;1JH)9_#vsIxqO%FvSV$ zQNUh;W$f_i#|4(?ZG?wAz;yajjIrLjxat;y|10cMJ)~R*3g+-%a0Qb;vHNW2Oq|0v8Qn%m>6lzGlV7oc-P8)Amj znPh|pBj04>HyHy+XjD!rek*yf6KVP;l$A z`RE@$*w4?6SjiKthAT$9Z>eE9rA$rVrk-3nvFc@=_04wy@SFA03wd;Ty08N4uk zK#~Og`t~kxbE7o2pfeD@sLVD;rW*hkKg0hJ1RuN3kW=BU5iiH4^+c=jPNbf$iw2Yv zaH+xI2wXQ=BFkCeYJrT#Eg!?(seswn#t7M7<4d=|Z~Ff}HrGX@$L2vI6GYTKXo|#D zRY0?XFA<|`agdi*idd~43a8_D!g(jeNRKyBtS#e;|62N<@U#*bNd4IfnWE)ib zBU!dxqQMKFd?`n}lQ#gkWh0<@KLNd~dY-4CyZIh5K!s=Nsub-Q%o)zJ0rc^3`FmO0 zKJ$TNN$WUrF)ztB;OW}4B^^o}OK7kzset-}{#F9?Zl+zfN+zUEp;D%>u`a+MP&7Si-D{xK66@&C*+(Y1)*m9Gz z@n6_y@(1>vd8&q02ekz8aky4*cyWU&Pe&rlPkbb6`{o6%f%gPj4RDY!!^KG#kiv{XaOvEuI7N!fx%Ogd#Oo7yLTl!%>Q0rP?rx>7;J?!Y_FnQ6r%1ZvaD8}$UCyG+Zom&6>GnD!>NTUfh8vK9sIGeNhHgz zho6N!ViKu)WK8cYCHP-vK#&y)NQ3x}xEU+n2kS^>YFPD8V(3a1&DqTmZ&JyAD)!8P zznv;$mWfU#0@+w5WJbyu7kxNHX$hDEj>ey4aZ5ZsAJe^~7B8H)$CYdPL@G+D!JylN zH)+^Ut?lc*DzYxE*UssZaSk%})S%Y_m^r(%mtnA~biyb(5bk{ z#(}^F9JcQTEc7^DbHSz2A*7X>y@)K8%zN=nle%1a+L{oHSJ&YE>D-V!}-)yrkm>N zo!IX?aVFtQEd|~>5fdfHFY1W^WfbH6DcB-&W~$U8ae&)9eZojE^4janc__?)HM2G6 z5ov0XZU>~}r34P#Ej#cr(h4CPxz7z)r#qA8+I(;!KM%^Gu3JXh)do0YflD6ah5>7u ze{B(87Es7l3Bq|QwnD>)ZgRMYQAR4)dJX1hQ9J$0cKJ~Ivu{b|UhPWcftBBSgWMEs znt)wDtWdmBa-1AGcM5p?&Fr6JHx%O~=GT8isG#fZlP@!-Ea5SQCrt81`Kgj(nXvT> z9=Hu8B^Dh=4mq5C=yr5oVDydypo$;c7a!82ui1oB-@=ehnhssSOiWdn0V*!CR73>W zC3Lz_>Yt=P6}K#Er(JqGH+%5%kZic!`;W5WvL4Drn;gagVaV}Kg()Mpz`_o_eQz6T zaaV8ATg}>F-_EJHoltV0>vx4YDRXKiRiu9S?|JVK%5ZIyt>j~66Ip6g7hLbWZ=E-F z(rDl%)30^OYF1>U%ydaBoS|Xg%9$`f^`@Y48?ar!E9;JOe;^Nq+QE7-M+z5i!|GjM zpP<>)eEa}l8q`nV2gQBK)V}=e)IFq^D%~dky*z2JH|zI?qghS)h1rp)(H>7F&Uqh4 z$~`Kqh!tA6$HmBuzG=?#_8w$3MG>p}#PB=VDh+EMek#7Pm_52EWjf*^dGu7e_Hx^` z+BI*jpF5w+5q{#XT>BLe13x(+CHpA0<%nhME&1jY2SbAIaZsovlRW8b$fs z84v2xU*pQUKH}n5Qq%7OTdQ+o{vOg3DCQPk=TRRjTSpRTz{f^iA!cN*^pO;Zn zBv*k)px9H4TRk)#{$uob2#GyS;6H?S-jvx7~z_nc$xvxh=gwJ5!rB zxR(>28{W)}i<@a$3VOUjn-h1%pG>c2`pTPfM||;q(T`k|MxqxM=+CdyIp{nSRiYpJ zd>o&oyNnO!WHWK`Zn61qP-)Cq+Q@JRWy7ZOZ)UiG+;NA^nX z-27VZ+T~>5-Qbj*-MaLG^j?WbN??SVfOD@_!@po zA*ILB+tbo{wLeCl)C4G6hIaQle5c3a#|XvU2`J%=pKJOIJzwTc%`$3!n)n!SKIFzx zE7qptn}`*$t8#vid;kKoo7>G~y?ovBC>9=iKq5zr8X>jDnyG4ES^=bDERnl`!O6lUn^Zs+o&UC%wA~&i2cmCa&-Syix1wia%`A_WJP! zeJ428(I!8F z6;wcTa~^g4k@N$yiUeF4R40O*wtD5`KPkD>_VcMI232~<$RgM6aR)hLMCB`JPr8EM zfnunhra)H|>f4b_n?)!CZOllp< zv|a;8o%w1pc)|Y7sXJT&FG$x2Z1w&3rG4Wptifa+`qdS5;RN+LcYWhHo&O5?hdqw| z4QtCQEjj<_LhHmyk|9kqMg~Ptm;T z5lIb5m=WvG7i*1J^Mbm5@(lr>jBJ!lo5c%IiV1R*`%lUb4FbvOs5ovv-lE`+#HhW- zVjZ@L=s4e7$2ie7kABV2ez2DlbG9+o_SyKy8hZgJ{;aF`Dgehbi^UOIk&eX5x!Ee2 zuAf|WE8fv=v#|y7JI-~@$Ekf!I-#spK6?p}!)FtJ3PVWHvyqp-qIqIva!1!9{?{S?G)CdCw9SY~niu(tDJs0NU z3m>fKsAfw#l?1MR3>h8qIH4EF>VB3b?Gag<0C-rg$Ib`9&1@WLJHDn*5|f%FL_ayK zkeApT_kRxqrItA}OEiDLZQve=jxFl3QJ}7O14ST#%N}ySA-R%HO}j~{$#9;ocs!I^ z2b>pKf3$s}X=;MDP|AgPzl;AlX#Ss2$zH;|I-G1kJ+97k1=^*cWIg^C&h^BA?l@)#bZR{6B7%|V+<+H|;~pBG3gFZ`QH=%Bj}o%DZ>|Cs8(a4oJyrHfW>)G5jOKC{ zj;s=L*S>qaMKqD=N(%3^lI+nQ76gp|Q}Dl)ZvTkuf6FO32>2ktkF6*g zM87l{Jvrsq&jtmr)zS43e_Cq}T76Q><*P;Ju}&)@O2PV3i&tE>$fpOW)2vW_`%mCi z5>ttF?8_mPopynbHO+@BUP=Gw@Z!Hh#q)sG?MAWGbV)amgaK}LN9!D5&R+c{DA%$M z{DET3H{PhI?r?nc7JsY`A-lXV^7nYkB7hT1nFivpmDCnas?&cCVgD6k{SCnCClMfJ zC?p*A%9{W<%ti7Sxo8S3|NjKWiSk;#+&CGb7p@hprPG)oE(4?wz|Mb>tfoAW=%t`F zSpvSf6(54O%-nAjI9^-Cp4NB&J+xT;Cln_!oH|Gt>EmWv0z_wsY8S0p??DT)3brAj zKlK6}=xJ!GI$GZ(j|hlf`%Uvn06dLRW(S0j0K&(06 z)SHY7QoIQkDE0QJ_@b&pw+P2xFcjxd<8%NZ*8dZDi?Tx9VpUcjA&gg&Pq5~Dx19aY zq4__d*{&?0fnnWe1i@fTG+Zd>01{Q3Z)-pMp98>w|6aD9_2L7f{E%T~!1@f{pMJ6w zH<{2C5V(AZ@k$3@`1tI>`N<;D7R)GIL(sS}VkcH5(#N>;eOb&#jV8gha>J<)L`1-; z(BSt1X<6RCj4zC2{{oY3adZt2+@(AIn;2-@P*qIx*`X_*GI+SoBn)P6s7Xg2WSWZ*nA5Vh5lV< z&09HwpP5HD0kPwk1*dc*2^;=V?0r3JAGWbS<@W8S?JFponNO5~{?mXWy8p-+tC>`y z%!=U$dCB+$BdEO3r{nBaxP|`b>&rLk(-w%NNRd@du&=~rTO6xY7#@+ORzDcDFDf;G z!RXDi8j3OfuD8eD40*wix5EjZ4@g+oZpY(_EK9B_rDN(Q(NwE?9~==vc?Cst@@i3u z++D^7Y~~oO!PyfhJfoZc%f4uGJu6AD&v~DY699ZPo%qf;pMo?@e^%QmU9WZOym(^9 zJnPE~X!dvhJj-1d+fYz@Lbp|*?enHQV6}`-X~Z3FXj#X+LsM6>=L=#;*<_l2Yj=`o zH9q8)c!f)>{wcwNqUkWV)$_|E&YE+XH^w)7)4cmBn)2%n=XGk+Hz3vv zW8J>|^XD?nq-xCf6_I#qg!s%aNxC6_+5-F9HpZdM>uX<8D$gQ?3zX@uFF{ z=nm1ILE`(v>o^jx0Jw)|)`-hRDV*k?8?;-ZHK!f_iK~*4NL$&-fnSIPw{OqD^LrK{ zWT54v*@$w*j?<>p(WS^?q**uqa0oId8}Dh1?4M(J1|uI=!=2YVMM0eW)zn`=MmjEu zy1_I7UtrJNSt)iHlJ`GDvdtB9*${IScPIbTKDcuj^LRbhqU~rR(XJt_5^82bQ3g~u z*!8+amnqSRWAqICtH*$yNPf5H{Hx?hyjbreHuL84$!U zxpmv%iYwr|t%Po43x$K4FCg8u)+Q1klGqm(D})Q!~pyqYnWCBL{bE;omd5l`U+&0O|lc3 zc)=^cKO0v)ZGz;5z=hFG9B9vXDKK4a9oPGaf;0 zW(R_?CB^#Zp6AVS{#xn|SF{8)i2?7u(j|%nc8qWjU*Y~IAj^;S{~plv zM%R1^Wjb1^PYH+p#7VJCV_5yGwX;CJ0Ii+tFwviH`1%Y0wSVwi`x4|eh?e^&n!Y~~ zl0zkUMe5WVPEbQ;>mSHh`0HJ+2G$!@@?CI8KSHYux~oDR1sXl?0fPU$;cKC)F}4k5 zhUfmh9f`&I0i_-IX#8EF40|^=7}eipn;=(FzE_Fs?&(hG{t2{o0P2<1zQ6^lX)=Ho z5=|lB{|^DAg3sU5fJzUgW}h!DMSOv-xR1ku-uO280e&{7MJUQ$N$~&V48Il1Nz({e zeZ*S@`zOCtp#QMSc1sb;2CcY}2cgpb#MXx|s%fJX1oM9!FE!$Kha`3ahPR zY0kWj7wpbWIyJ`iL3Mb_xUKM8z!#~Cto!)cI@5Fh zNID~;Y#d)K@V*Ca19Ekro4NyB;0jGnV@$3+a|wzg7n!X6ULcQcn@~apc)5p?fjp0` zkp}6yKVtSD19Hth!6VazqOb(8-ixZPr-RP~YNPTTe&W|R=?WsXkETFYCL7_mKqCm` zi&EDUpq4#=<|Q&)WY3#IEe1eM#=>|38Q2d1%t&~X+4|)Y>*+ivUA=1^@UzC{A3`aS z)g7E?gDdB~ZRw>9B}-gjeh~v3B45Ovk1}Aba45#KX2AJBYE@xvIrc+BhOzxKO!#u< zTLEBvn`KOO?1@x+qclZ8Cv&UTUIRm5ec%B~l=i>8fwS%fwSH7?Ujv54CyWx6*o_1M zMytg8@W2k{L>QpV&SKY_VoVw+ja5vr?gQ1lRBpEwvT=NU`vF(0{c&2S@E>zm>8#}|Qi>^1zZLrt?iTelU``Prk&|;zJNR}%a#q*nCevne1{j4IaJl(mkj3Be z0pl$x9Eh|4$|Ju4r+YGC!+BDwuuV^D(?y&8E0|ILPkZMb)kOOLdlLw~SZIQR4Hc=P z6f2+t3JdHiO#u@`7L^tiL@6ntu!1YD3Mf?+MFAm7T|qL+DmIFufFck?q^Su#KuGTA z3Fx}t{r%4Go^#LrUJhKYR7XBr_4_ z!%y`IAZ$AQO6vcrRW9S#KJJ}FPZ_+XTJIGOt9}uG$UJ@kz`jK6o{y$!Ds ztk`ajfId(NTrwB59g+%QPhC{} zd|qQ0zjnZ)i=7G$``%yp32M!wvz=~I6O!n5bzC74C=@W^BN#ep&d&ab;|CjZ`u*fD z)7^3}IW`8C?|bzpr6qekI1Y8qvUnaJyq>jdLL;r=`GkLqkryx5h+#J%q#Q44bR*=k zs+u`DUy{S7(^_PCHfqZN^$S)pmwUz299@Hv@!l^DYVn69ZKqo>J-JEx1i zarhqPcG4w4Iaf1ARtMgKYjKe6RGK%!zNP#ju7Ig)a(w;I<9UK`pq8Pmi36p^}Ihyuu~TUb0RiCaoGOy~I28n!;sk$+ne%jr7jxYuXr|Ng?(I zY5Yb7kuJDKj8ZJS(~;qmpm4qY1R*SS#jQ?WWlV6`s=1_sQ?e_^DE2V3~1fp zc?`}Rvgu=&F&MH*Bc2o3>CH-dzpP5~3s(7u_p8G8wLz9a;}jAxUM|FSe1rU^TBego z`Nx>?(nF-Aj?VUxppzz7sz)l4sMRKl?wRtef=Q)eBFZ2+~u$Sh-w`I{VgQw*Y$S` zx;gQm!yc@;nM;f7zcZhE9^A9fvX+XM6Ca)ZObkYXO8LVYEMdHGJ~zh=rfvKnvoK;5 z#Dk%ZkgZ1y$-#26VwJADU<%8Cj4ks|0|&kYq9zE~I!Lz=N-_UZil zHSsD7W~8tuE%h;V4^4_QLUKEsGC>vcQ>;k%APCdxup$aWHa1*y_xYlJ=e)sV5Z*Y8 z6}!rlgpnF#3sQSA#7ejWkQZWpNEk7(AKGm%XY{QrP5R)+oOR$)77#df>X3eCo^ffU z{~Ad~4k9eFECJ~HJ-164oT6Cp!I6ST82CCg*gt?-FSlwpCLF%rJg76t3ZrY(dpRvRRG9AUl)p z#*3rFK$^r5+hX3m8eyzcTg)YCtXeE!L|+W`u(+N%xjd+xx*8b83du}x=-aa>Y9aP6GABT5Ow?$Jf941q`t9(M+*28yU zOeno2lLYy;q!oh7tMsI_Zsw`a*0w!ttB?AZ1w;;qs&6V7^U7ZFN$1W<$|+u{+Vr{~ z?Eq#V*}3auyNmA@1jUtU{0^^4X2~IOlN>5Cj($h`FG`L6|X!j{HK-=`i3Gq*{DiW(*VIZAirO`4RI)lo5DkBnYuB z>Jm1CxlEUbiv?r0MGg*G^tcsQ29^bN@M>e)!_d-^)(V)HBrg%4KT&!|0$y&T7h!8= zFp*mt%q5Q7d|43m_WNQ{G2F3hqQ^$rzA9z;NI}bQzkyzat(igBp7z8J>h)UhZyytL zp89f@!Xj37pz3An7etr&fqEotAj}NzOH4Lx4u3HJ2}Q0C{rw6*vke^<{3!!QA(ePV zG;yNo5a|{I)217@QR4te54Wq7)988KS8Bg^r97M5@Now?iGh<_j(|DTFrFsoo-s>0 zr0dK}j{e*YUmoTd+AHZ^I{w%gyHGZ70OZK%@H?O79hU^&+?8Xn*g&+8KULlsf=Z%&W9IP$PV&t&RwC0{i24tK)m-_+2p( z-?cD#eEv#e_s!Tyx`0X#o;LLo~Fzg{SO zqvIl6I0jvgmtATf(~=XZIEucb;{Cd=rJut)wOOC{kA~v)zyvs7P@OR>q=NPgX0II) zS_A{$@}@zf1bHG=!x=1vtB)yOuawU{p6jGkz?9DolDDZGqgqDDg&yJ?EY=Tqbt z$Qy|4ozJ|lYx&WkGyYx0{D)U+xd(*98cCSP^ZEQz6f2yFg=Iyw>XU7Wv#JjYl z0qTM$K$)Bu4=1FiEkTYzN-5rc^7^Byf;5X3dkcq}I%k$2?cl^U(TcqWI!rfQU^V}I zLxa4&E^EXlRDttu%^Z*mqLzqOECFj@2OuPw>-RLyS{tQn_Q5_z1guP#;ydGtPagQs*nYi%OAbiHm z*J?efLtZ6)oX2eW_&&>sJfQU?oJ)L}9<_EngTHtgwfIEwcro|RiG9B($q7o787`FM zwymGJ*7zF5#E?C^3Mz0YZ3%7o z^cvIJ3jm@_Xy?*A94s2w++0Szf1*m}oL^_b*4EC%T|Ma&PH6Skv7wtI{hmW)NyO z!U^-Ws8cg`#tz$6esTNF&^(nED2Z#T*3f44D6Pd)a%iRg@4>;xzOG@mL6I}OaR(Uu z1ogQf0f?r4NTj#lQ!r3u?B`Wg_HCFdZ3&3JdZW4`QABGBc{s9?RU8c= zH13bo_zFSu8i!*TDU8P_`br3!HsV>jtaU5Oo+@87Ytp+fT~^miWdC+Ues-O|h0h9& zV2ar#RO}B97xcOOvKfqOP@^X`%@8^^NC6-hzvu4y55L`YS{&c5tINAeU^AdlR6Qbd z@0t9Z{gYgxcc@;nU{#d~wX#nFIpy4NJ{8F{?P=fKoWru*UzN$%@wLc37p~3v@Zn)c zO!|kcaSP4;LtNti(EMb?uH5vSIx5;pUlc!7@d}#D ztiGS;MDZnc3qTxL%GyV1&o-6e)S_SE6|)%;pM_Hl*H&70sYBP#zqTu%y5-wWw9wHtCmBid>vd)ET7?CA;{?Xe@swA)I_C7<#z9Kl`4B3e}%Yy9-lz%wIGanzV4<4MkT464oO|E*q zn6(e79Ago&RkPTs(Cr31&g7>w(Cc>wU%PE%rq%IQRD_yy=dFqrb z$O34zcYwF{>T`+!Q{+qtT7W< zk{3y;Wu9C*DmpiBg13w)z2tA7{e6xBt{svMKR>;o+FU9B6#QsO^UDsRxmC-&L!pY^ zB<^ASZ8J73-t-ryQP|_hYHBtBbR&Ar@Ht{r!8$W2E;sTZJY%qb!aq6PR3JA#GOzx} zi=kz#mK3S70M=uVn*e|Ca_9@AH5~c1&R0jHMb(uj%Ds5On%H* z)qI$XJX%peU0C;qJd59m6IM~v-}>1^eBGMf6)fcz)2gRjtkS+}!BrqRE|@I?<|3Fo zR)4mSuSoAlDV!v+R?!Yta@qPvp!OTEj5 zL4+03hw-)kZx0Exf7n|u(dGT#{yIId?Hpa+|Lt@tP@v@tZ`M2y$Z5F0;WZQ)t*0R- zL(gAWz;P&CwX%=4antKt+9swqX?Rfar^)$Hs3;IcTq17mMCBN5>`?dCh zw+GfRUR>Id#ggWS1|Vo)uUMj5;U^LbI&{GvVe5U)wwnhpC!!$Vq$?f|LVa!=O&8(> z@>kzrR=$X!C^Nlf`QSI87qHmf4|>3`b%nlvr$K}l6j@fo!cChc*Q0>Co0YnKr})tZ zNFSnU{{|~FRRT_*%?x6mZ&cm-4v<4jPok* zSdBovD8))c&?XC(}1i9x|_J;fM@c4}3lS_Vl^+H`kRQNW*tKwWzvu zwk@@yTAQlCR`2eh2FD-eXM82W=skE${>J?{8L2KC*El7gg&FwcGnQ~J3JzxlH z*ud>H)E!quMlw~t%k#C}f`jvSQC^KVwMFaoKzfqUmbNT?q?87CRJzOnN zvpya^nm1m$dwP}=p{%q&dP5u;hY85pWz3`_8llD?L!0 zTECL}8UlgTUd8YV(i)Pf$Zl6tC74*JH{NcVJy2L^4%&+vY;5-x(#pj@P#L!ol>xS@ zX)2?jQlbQcaahPwr?%W@Z&gOp!O_X4XGM)f>Mu7=aEba2se<1Pbu~7h0+^sI4`>S6 z29dzuA0rxI#8)@+`h=0vgPl`*;z+kBuxdwU-;(A}P-1!`WFlu062a_)k%j5jS186! z-%PbQie}$KXvkx@41tzbWQRfmMV#R4vS1fXeWm#|?1V^l>_rg9Cz(J6Gq z<$@y)5(xSLGq{ctl=^`D0P7H7Xb_ZYt-{M)!)OG1nD12X+HVabXaFgoR)24KQSG$l z?O&xlAQ}%~vFB59^u^X8Q%&TBu53_wNBBj(?bbwfsE64#hc)S?lB{IY>08S{5AA-ZA6C1z* zhxzSMU_xd*T<^X$+E7ErZ>ZpzkOph^94@Gk^dE$q3d@0}^qX!#4}%3yFyt9?Z9!J8 zV&tXj2UFEd<2E#$kfF;Gnc-w))Ly{lN3-*+=PC6ic-QwQOBAueN zt&Oxq<4j%PNnO!qKI$C{zm7zVzqC=5)FG}7`f#7QOn``SxCCJsxoG^pP}coB##bKM z9mqwu114M!K(?5$aR?d&v4GKno1&0WB?Ldb!Tx0=1}q3_IH86g$ya30OMpU@MXI($ zQN1-8Z%+A=J~nJduKhjA;F@n=wAqoJkb(m>@FJVVoMSiG}@A8JZXx^lQuZF`6JO%5)SKC zw8gG?lW`YkdK!OQ@7MO0#&HGb@0bKgD=c>Zu&j zd_`)h@xhsixi+jr zu!-AtexNN{y_*OO|A#}=`UWT@eiz|ydI0j|n+7Zab#vssMArw7v{N8qW@gP@HmL(VoX+tIG^kT zJ~%cJ5}}Ib-!G?i53j_rO#;C!#>94GNt-)+Gn;?rHn7L zlOi|a^(Vc*Zc*#2QH6WToD9ckfzuzfrYb?sLu|z7++eR@*JZkQ-J%_$JaNsZO`@q0 z{I`;2lnY@OXati?Y0--G>$hqBRe^IEB(5D^>7$8c*d#e`9Y@X!tcVVOW{6wr6 zg4vg9jMkd9t8$&63z8kNPVqkr{AJza1kw<<6xFW}b}uRO(xI3>>7DNu*nE#V;UeP|DMt%CZqlRpDWL48lCXsM7;QGcQ- zT9LXmTH`BTeY2%QKkz!wNQ?26x2vWI747$M_%@a0V56c$OCEne%R-TSk(Oi`qtPmL zoOhbmL08#R4`E9~Tlds!NXrWbl{vZuspCeWqTv)-6&#aB6LG7TP-&A-pk=46EWyj& z6RX%`L8C3zX1(cwJK1fIma?{(;-j0a^hpA#;}N5OT6&1=Z8OiKMdjo4bIHn_QJ#_x z>!Tj)i+^6)UJ?6JZ$$~Ne*tcVsEpe-e0s(y4fdK5B@QpnbO}3>?|Pi;a*no@9a*Wn z;wqNH2!M@eZ)yXFT?j7tgz|9T*iYM}O#AeDidrL<5W?eb)3$z%Y-3MJs5oy&`MwET zu*d>HE3#%YSh*0`4-b^#f*s3J3MY*w|1mRp**pdZO+F;*E`Z5KU?&uU8SukVu;jg7 zgdlScc?4!ix;bO#`Zj|*b{?(%`po1-B-f?4rqhppa9If>fkEy;K}bBI0gH1t%K{sj zv1Jv|N)gEcqK}NIj4yB)Z4e^0gf*~g8Quyb&z~8wlr;x#<9Dpuu7`IyIFFoQh5P=d zKu$2m#T}SU{@oepazKfEYUb@%;ZWLgoQpY@B#whO6t&QfRxn4Bc*0L7aIxzi0<`p8Et}c1u+0Q;Z@eC#JMuuL4$& z$LGVBv!REBSHU&S{09480O!@8zZ5=YMyhG0@Z2=e&Q*c&XA$!$vrY+uioCN;f!_4_ zxsZOnDt}>kpsGc}ebcOp*P*G|LMZdkxNx)kk0zCIE~uN0 zC^os?CWYobc3M;k1Iv&f7eEd-Nu3w*TC_A9l7;k=*ML{V)m^a?c^(6yKPXGelMk`m zJ;qwspn^WLcD`##oRt!0hnD?AX!JtMM%a*c%-iISf%QLdUvaC1Zf$9+#BJ5er_9*! z3)E9n^k#f>=+d>N=2hFCJCQnkw&l{j%WvHmO_T#dB|r;yWi^3eE2)#yv31H}-s zvmK6{FBcOVHr)ycf*NRQMhv7lL9?lq0pXm-)8G*L-I?!N8W)XpuJ5peum^LI(4XW2 z%;R%~uA_-HH*2xJ@HDy|+QCMwZ(=>VV(uh1dnqt}CF~K&jlXM(E@#y#SE#yT&hDhM zW5BBT?=-(c4F*ogSH^DvObHfiY-;*Lw&x(1tE_6E;(9+$Y{Dgw6mdbn9xme52DkEB zUY~4y!^uQ=!!a=s$1dudK!Q7LwMz3~2gFvWLZ7Bv{7F@Msy)QpbX~hjq2cS9`~Z9f zi;XHAA3V*YnWM|x9~8DxwB!-Aq*chm=0;60GuC1yNUifBc~O3&*4Q1`I!D8qZ?An* zyZOMF7V&C(bcgM5i(xph^MjfbMwGJHd;D_s%56-p^4XUv%@uJrBfcA-?UyR)T5$_X zFa0+KM#LPVe;6N!G`cPY7ZY-eWKjU?2Jap{B}S;K;kwug3;WwsvLvjSKbr@T!jgW1 zcyQH4H+X5CQP0}fncd{24c&j~Y|q%tdGC%n#Y*yT^bB|I;Ky} z?jZ@B<4DhlD=+Mq3h-O==pxgWvVFY#uWHozX}tX7h9u_Ed}rpTc=BOzLY=e!(n7)8 zWy>9V>HsebfPY9EbokOT@z&->dx`pAz)0K>Z(Bg8FC49prMSr#I=&3-=V(v(t5YnQ zHBjsBsZnv~=eL=sQo5b8mQGHjnm7Crh4Q_lT4SjDiFG_MGliGLzF`|e>nic)g;h@E z$#ln^j0VqRIiwig`N-C2jjZ-{?QTqyH2T8L+p!jA^Gfi3-bYd%a2EOwPRt9DYE6H@Get<%V_V|X;DLnZS?yK?We9?9?!JA7r-;Il zlB{x|e1Gh85de*EPYN z>il-GZh;4x-(GDmFcXy*XhaPOv`hsVUfBX7*e0WEhroD19?BsmTfmoK_6lN_E{GAtTbq6oG|V|WE(kx_ zJ}h`XM@7YcH7ZdDREyx3(sajE%j;;!=edCLd9ql$rY2T$XR^2d$5AU)YJp_*gBb5( zl)arfoxR<1V`UGDWahVDJP;_yW6zbV612>f8>b>jf(?*_!0aMfg>RNfej><*@dEd& z5fg&zqGHh^d9nvA=e_#~#kCWAO9%L$bNLV>^zB9ad+UE5cU>Y{WDie!E!ZJei3sa+ z?;QR;`(0s^)Hes82~yU7LN^Y+r)~>dDgPY&dmP9*wB>@>DVKG$7DNC=Vkxb zV_-+mL{YUhA;Vh}#bF1dIBayKFssod6TlAOM73!n{{}mz^RfRu>%lEmC#`WjDYQYJ zV3m2J34+ys1$&?*>^F(Xhh+P&+4MCH&TQwUFm1e8_ozcn2V~uqKnk#cT2WkW)TSSdaFZ2jwe9(n^jI<%;9$OaR zAfNDsmRJrX$QVtXh@v@F&174zYF|XE8CDa`Q>_F_1SUI;i{X54z*rfXyC8c!mlI93 zZ{jS+^L>lK3PjGtrRn<}*rE)6pNA96gMJ*0{^#KT*3_TosVY^7^30_0`FlHSh*{-Q z?qH5#ZlNlw;9Nck6IUU4)xdqu9a?EvTp_IiyQ}1b%bqGQhv3>E@kZ0Wi_)$EtXB(N zkMhs(H|IfPeE6m?$OarK6O#;^5K?P2F_O7w&ope}9eQK!bXK)-r+^zZAzbA71EWkt zo`^fA3kekQMhe@Zkk%yz5KDrFN8j{n?#g_viX_~kH5{`ZgsLlsUP^0v8#0>%X%@W$ zN}K>Tyij<+S$qJZ!XaFI>kY77 zjDrA>;EHu}1&4fC6C0LMM%nCELW+h9wo`R40P5O2sst<=BepLsGm0;+tW?B=jFi)WiF6#DC|VMTV>`TT!^1A=5jNV@6cMh9Zmik$GuT2F=i%k%ar~EV zr{V(R^9{hAGTY!q^C}i!v|`U_=dz{@TAq$1~2j}&^` zkLzLu@-a1=0(|=51_kndLPdr;5myk?UP{}Qq}$W_P=fnW?|`UG{Nv>;3?;(+j4OA+ zcAZexM}z&ny|&oVcrH%2#|^V{&2>}QLRb(idptoypLNa`g9oTOSZK4jU`^W4IPI2Y+hc%=wMPrf1KOc5G&6v_52(eO$rpr{MOEoEijq3x9)Uq~%mBO`sA4Y$(cp|VMiRk=XtE?hDKHrjrYLu4 z&eFfp~6bqs~1An5-s7TaMx0i2^HR9`i43jvUjUq!SbSk~7V?r)ok zWPG*L6Vqeo*5DIg%){$F7Dh}1-$E)|_8%*s&G-rGQEj=^U?u^!-bmHe2+CPi(Db37 zm%!mcKLcpu_jhB0w0z!Y)%;Z(=EF%1(!!vR_>_p%1BaNRz&IiCx zhQQ(i;U){d1tx=m~$iluDz)7M+)%A^!l&^XGxF;lznC0L$J&ySfhkWN$ zkSzlI3qQ=%KogTz*Ccy~99qlxygHMN@{mUs-%x^p<{2}LwG!4hxB1*ZKo-9z^2jk` zz&|AkVoBp{JyI?`SUGg9Sfj>1@T6SS=s^L?u z6l0`4z#H@qMfMC2qXkLEJCP94u#eQi-@leIxcpWplmY$E2j?=#vPvSkf#+jU+{^FQu5U28X(k52 zF+#)0cS`18V;sh%LtlXYarKQ_Gx@?r#|N?*+v22-aZkEAGIss5Oc&Pc|0vwQkJ-hK zR08$rj&-lBUzp0GyF8D^^H+yAbRl3y{uNt6m|KjVpM_G7QRE3CYOeh-Lp;UebPbl{ zVdG2cH+n}F$cgX%#Olz$Ut;x@G{zH|ocJie17MnkfP;Gs$$#!o27gFhFSteeIWt2| zFjuI(fF3cRpX#-maU^Ny{N{U1sdx2yF-n%|$LdWZxG(zY!U^P6evq5umYF-d*+!xm>g1kCWp% z@T_wE*fQBx(%qcM2%XDkPmIbyeY{}FPV%*<@QAGRL=@~%FMlhCB%XBLOZ?gnz*-C} z31RC7v$mdz08r-P8AF*u_A#So;9E#bez}T@dGU`x#r!}2kHHa$e9T(6%iCG)I_!*Q zkfm$Te$OE9^ Date: Sun, 15 Mar 2026 14:35:25 +0100 Subject: [PATCH 17/18] Refactor atcoord structure --- src/v/ac3_draw.c | 4 ++-- src/v/ac3_print.c | 4 ++-- src/v/ac3_read.c | 43 +++++++++++++++++-------------------------- src/v/bonds.c | 37 +++++++++++++++++-------------------- src/v/evr.c | 10 +++++----- src/v/headless.c | 2 +- src/v/v.h | 23 ++++++++++++++--------- 7 files changed, 58 insertions(+), 65 deletions(-) diff --git a/src/v/ac3_draw.c b/src/v/ac3_draw.c index 31eaac5..657dcbe 100644 --- a/src/v/ac3_draw.c +++ b/src/v/ac3_draw.c @@ -88,7 +88,7 @@ void ac3_draw(atcoord * ac, rendpars rend){ if(rend.bonds>0){ for(int j=k*BONDS_MAX; j<(k+1)*BONDS_MAX; j++){ - int k1 = ac->bond_a[j]; + int k1 = ac->bonds.a[j]; if(k1 == -1 ){ break; } @@ -107,7 +107,7 @@ void ac3_draw(atcoord * ac, rendpars rend){ XDrawLine(dis, canv, gc_black, x+dd*dx, y+dd*dy, x1, y1); if(rend.bonds==2){ char text[16]; - snprintf(text, sizeof(text), "%.3lf", ac->bond_r[j]); + snprintf(text, sizeof(text), "%.3lf", ac->bonds.r[j]); myDrawString(dis, canv, gc_black, x+dx/2, y+dy/2, text, strlen(text)); } } diff --git a/src/v/ac3_print.c b/src/v/ac3_print.c index f0c5ac5..470783e 100644 --- a/src/v/ac3_print.c +++ b/src/v/ac3_print.c @@ -10,7 +10,7 @@ void ac3_print(atcoord * ac, rendpars rend){ ac->r[k*3+2]); if(rend.bonds>0){ for(int j=0; jbond_a[k*BONDS_MAX+j]; + int k1 = ac->bonds.a[k*BONDS_MAX+j]; if(k1 == -1 ){ break; } @@ -64,7 +64,7 @@ void ac3_print2fig(atcoord * ac, rendpars rend, double * v){ if(rend.bonds>0){ for(int k=0; kbond_a[k*BONDS_MAX+j]; + int k1 = ac->bonds.a[k*BONDS_MAX+j]; if(k1 == -1 ){ break; } diff --git a/src/v/ac3_read.c b/src/v/ac3_read.c index 6b89461..daf57e9 100644 --- a/src/v/ac3_read.c +++ b/src/v/ac3_read.c @@ -3,44 +3,35 @@ #include "vec3.h" atcoord * atcoord_fill(mol * m0, int b, const geompars geom){ - int n = m0->n; size_t q_size = sizeof(int ) * n; size_t r_size = sizeof(double) * n*3; - atcoord * m; - if(b != -1){ - size_t bond_a_size = sizeof(int ) * n*BONDS_MAX; - size_t bond_r_size = sizeof(double) * n*BONDS_MAX; - size_t size = sizeof(atcoord) + q_size + r_size + bond_a_size + bond_r_size; - m = malloc(size); - m->n = n; - m->r = (double *) (m + 1); - m->bond_r = (double *) MEM_END(m,r); - m->q = (int *) MEM_END(m,bond_r); - m->bond_a = (int *) MEM_END(m,q); - m->bond_flag = 0; - m->bond_rl = 0.0; + struct {size_t r_size; size_t a_size;} bonds = {0, 0}; + if(b!=-1){ + bonds.a_size = sizeof(int ) * n*BONDS_MAX; + bonds.r_size = sizeof(double) * n*BONDS_MAX; + } + size_t size = sizeof(atcoord) + q_size + r_size + bonds.a_size + bonds.r_size; + atcoord * m = calloc(size, 1); + + if(b==-1){ + m->r = (double *) (m + 1); + m->q = (int *) MEM_END(m,r); } else{ - size_t size = sizeof(atcoord) + q_size + r_size; - m = malloc(size); - m->n = n; - m->r = (double *) (m + 1); - m->bond_r = NULL; - m->q = (int *) MEM_END(m,r); - m->bond_a = NULL; - m->bond_flag = -1; - m->bond_rl = 0.0; + m->r = (double *) (m + 1); + m->bonds.r = (double *) MEM_END(m,r); + m->q = (int *) MEM_END(m,bonds.r); + m->bonds.a = (int *) MEM_END(m,q); } - memset(m->sym, 0, sizeof(m->sym)); - + m->n = n; + m->fname = m0->name; for(int i=0; iq[i] = m0->q[i]; r3cp(m->r+i*3, m0->r+i*3); } - m->fname = m0->name; if(geom.bohr){ vecscal(n*3, m->r, BA); diff --git a/src/v/bonds.c b/src/v/bonds.c index 68084e4..a092168 100644 --- a/src/v/bonds.c +++ b/src/v/bonds.c @@ -88,8 +88,8 @@ static void bonds_add(bondpars bond, atcoord * ac){ for(int ii=0; iibond_rl > 0.0 && ac->bond_a[k1*BONDS_MAX+BONDS_MAX-1] != -1){ - // at the 1st time ac->bond_rl==0.0 + if(ac->bonds.rl > 0.0 && ac->bonds.a[k1*BONDS_MAX+BONDS_MAX-1] != -1){ + // at the 1st time ac->bond->rl==0.0 if(!warned){ PRINT_WARN("too many bonds (>= %d)\n", BONDS_MAX); warned = 1; @@ -98,7 +98,7 @@ static void bonds_add(bondpars bond, atcoord * ac){ } for(int l=0; lbond_a[k1*BONDS_MAX+l] = -1; + ac->bonds.a[k1*BONDS_MAX+l] = -1; } int nb = 0; @@ -124,7 +124,7 @@ static void bonds_add(bondpars bond, atcoord * ac){ continue; } if(nbbond_a[k1*BONDS_MAX+nb] = k2; + ac->bonds.a[k1*BONDS_MAX+nb] = k2; nb++; } else{ @@ -139,10 +139,10 @@ static void bonds_add(bondpars bond, atcoord * ac){ } } toomany: - qsort(ac->bond_a+k1*BONDS_MAX, nb, sizeof(int), cmpint); + qsort(ac->bonds.a+k1*BONDS_MAX, nb, sizeof(int), cmpint); for(int l=0; lbond_a[k1*BONDS_MAX+l]; - ac->bond_r[k1*BONDS_MAX+l] = sqrt(r3d2(ac->r+k1*3, ac->r+k2*3)); + int k2 = ac->bonds.a[k1*BONDS_MAX+l]; + ac->bonds.r[k1*BONDS_MAX+l] = sqrt(r3d2(ac->r+k1*3, ac->r+k2*3)); } } @@ -160,32 +160,29 @@ static void bonds_reduce(bondpars bond, atcoord * ac){ double r1 = getradius(ac->q[k1]); int nb = 0; for(int j=0; jbond_a[k1*BONDS_MAX+j]; + int k2 = ac->bonds.a[k1*BONDS_MAX+j]; if(k2==-1) break; - double r = ac->bond_r[k1*BONDS_MAX+j]; + double r = ac->bonds.r[k1*BONDS_MAX+j]; double r2 = getradius(ac->q[k2]); if( r < bond.rl*(r1+r2) ){ - ac->bond_a[k1*BONDS_MAX+nb] = k2; - ac->bond_r[k1*BONDS_MAX+nb] = r; + ac->bonds.a[k1*BONDS_MAX+nb] = k2; + ac->bonds.r[k1*BONDS_MAX+nb] = r; nb++; } } for(int j=nb; jbond_a[k1*BONDS_MAX+j] = -1; + ac->bonds.a[k1*BONDS_MAX+j] = -1; } } return; } void bonds_fill(bondpars bond, atcoord * ac){ - if(bond.rl > ac->bond_rl){ - bonds_add (bond, ac); + if(ac->bonds.flag){ + return; } - else{ - bonds_reduce(bond, ac); - } - ac->bond_rl = bond.rl; - ac->bond_flag = 1; + (bond.rl > ac->bonds.rl) ? bonds_add(bond, ac) : bonds_reduce(bond, ac); + ac->bonds.rl = bond.rl; + ac->bonds.flag = 1; return; } - diff --git a/src/v/evr.c b/src/v/evr.c index 4d076c0..04d15ce 100644 --- a/src/v/evr.c +++ b/src/v/evr.c @@ -16,7 +16,7 @@ static const double vibration_amplitude = 0.1; static void redraw_ac3(object * ent, drawpars * dp){ atcoord * ac = ent->m[dp->n]; - if(dp->rend.bonds>0 && !ac->bond_flag){ + if(dp->rend.bonds>0){ bonds_fill(dp->bond, ac); } @@ -43,7 +43,7 @@ static void redraw_vibro(object * ent, drawpars * dp){ double * r0 = ent->vib->r0; double * dr = ent->vib->disp + dp->n * m->n*3; - if(dp->rend.bonds>0 && !m->bond_flag){ + if(dp->rend.bonds>0){ bonds_fill(dp->bond, m); } @@ -138,7 +138,7 @@ void kp_print2fig(object * ent, drawpars * dp){ static void rl_changed(object * ent, drawpars * dp){ for(int i=0; in; i++){ - ent->m[i]->bond_flag = 0; + ent->m[i]->bonds.flag = 0; } exp_redraw(ent, dp); return; @@ -304,8 +304,8 @@ static void move_pbc(object * acs, drawpars * dp, int dir, double d){ mol2cell(r, dp); } if(dp->rend.bonds>0){ - acs->m[i]->bond_flag = 0; - acs->m[i]->bond_rl *= rl_move_pbc_scale; + acs->m[i]->bonds.flag = 0; + acs->m[i]->bonds.rl *= rl_move_pbc_scale; } } return; diff --git a/src/v/headless.c b/src/v/headless.c index c934486..485ad84 100644 --- a/src/v/headless.c +++ b/src/v/headless.c @@ -49,7 +49,7 @@ void run_commands(FILE * f, char * command, drawpars * dp, object * ent){ int headless(drawpars * dp, object * ent){ atcoord * ac = ent->m[dp->n]; - if(dp->rend.bonds>0 && !ac->bond_flag){ + if(dp->rend.bonds>0){ bonds_fill(dp->bond, ac); } run_commands(stdin, dp->ui.com, dp, ent); diff --git a/src/v/v.h b/src/v/v.h index 4c4edba..6c33107 100644 --- a/src/v/v.h +++ b/src/v/v.h @@ -19,24 +19,29 @@ typedef enum { OUT, } format_t; +typedef struct { + int flag; // whether bonds are up-to-date. 0: no, 1: yes + double rl; // the last used bond length scale factor + int * a; // lists of bonded atoms + double * r; // distances to the bonded atoms +} bondstr; + typedef struct { int n; // number of atoms - int bond_flag; // whether bonds are up-to-date. 0: no, 1: yes, -1: disabled - double bond_rl; // the last used bond length scale factor int * q; // charges of atoms double * r; // coordinates of atoms - styp sym; // point group - int * bond_a; // lists of bounded atoms - double * bond_r; // distances to the bonded atoms const char * fname; // file name + int nf[2]; // number of molecule in file, file size + styp sym; // point group + bondstr bonds; } atcoord; typedef struct { - int n; - double * freq; - double * disp; - double * r0; + int n; // number of modes + double * freq; // frequencies + double * disp; // displacements + double * r0; // atom configuration at the central point } vibr_t; typedef struct { From c5ab180c9c50068229a8e67d81853b89599b923e Mon Sep 17 00:00:00 2001 From: Ksenia Date: Sun, 15 Mar 2026 15:22:14 +0100 Subject: [PATCH 18/18] Update changelog --- .github/CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index 221c3b3..6d492e8 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -15,15 +15,17 @@ * Add CLI option to disable centering of molecules (#14) * Disable default rotation wrt inertia axis for z-matrix input and add a CLI option to force it (#14) * Read molecules from the standard input +* Improve data structures and code readability ### Fixes * Exit correctly when window closed (#10) * Fix chiral z-matrix input (#14) * Fix NaNs when compute dihedrals (#14) +* Fix the "readagain" (`r`) and "readmore" (`tab`) bugs (#31) +* Fix z-matrix input with unit=bohr (8554864) ### Coming in the next version: * extended xyz (#16, #17) -* fix `readmore` bug (#7) * high-symmetry determination bugs (#21) * how to build on mac