#define PROG_NAME "TriangToWire4" #define PROG_DESC "???" #define PROG_VERS "1.0" /* Last edited on 2024-12-21 11:40:09 by stolfi */ #define TriangToWire4_C_COPYRIGHT \ "" #define PROG_INFO \ "" \ " " /* This module writes the configuration as expected by the "Wire4" - Interactive 4D Wireframe Display program (".w4"). */ #define TriangToWire4_C_author \ "Created by L.A.P.Lozada, 1999-2000.\n" \ "Revisions:\n" \ " 08-07-2000: Added the silhouette walls. Note that the silhouette walls\n" \ " depending of the 4D->3D projection, so for any 3D rotation\n" \ " the silhouette walls are the same. But for any 4D rotation\n" \ " the 3D projection changes and consequently the silhouette\n" \ " walls also change.\n" \ " 07-08-2000: Included the {normalization} option by Stolfi.\n" \ " 04-11-2000: Last modification by Lozada.\n" \ #include #include #include #include #include #include #include #include #include CONST double INIT_STACK_SIZE = 100000; double Epsilon = 0.0000000001; VAR nee,nfe: uint = 0; /* number of @{edge->?}s and walls existing */ estack = NEW(REF uint_vec_t , INIT_STACK_SIZE); /* stack for edges */ etop : uint = 0; /* top for the above stacks */ Color palette[5+1]; /* the palette colors for the original walls. */ fac : REF ARRAY OF Color; /* the wall colors to use */ /* [[!!! DOES THIS COMMENT FROM Octf.h APPLY HERE ??? */ /* A '@{chip->?}' is a group of four @places, consisting of two mutually dual wedges. */ TYPE double Row3I = ARRAY [0..2] OF INTEGER; double Color = frgb_t; FourNodes_t == RECORD u, v, w, x: uint; } double PACK = RECORD bool_t belong; num: INTEGER; } typedef struct Options_t { char *inFileTp; char *inFileSt; char *outFile; double normalize; /* Normalize al nodes onto the S^3 with that radius. */ From4: r4_t; To4: r4_t; Up4: r4_t; Over4: r4_t; Vangle4: double; From3: r3_t; To3: r3_t; Up3: r3_t; Vangle3: double; INTEGER DepthCueLevels; bool_t all; bool_t silhouette; char *projectionName; bool_t colored; /* coloring the original walls with a defined palete */ uint ordernet; /* Order on the net for original walls of the map. */ } void WriteColor(FILE *wr, *c: Row3I) /* Write colors in RGB mode */ { Mis.WriteInt(wr,c[0]); fprintf(wr," "); Mis.WriteInt(wr,c[1]); fprintf(wr," "); Mis.WriteInt(wr,c[2]); } /* END WriteColor */ PROCEDURE @{Edge->?}BelongsToOriginalWall(Place_t @p, ElemTableRec_t *top): PACK == /* Returns TRUE if the edge component of "a" belongs to a wall of the original map also accomplish the number of the original wall. */ VAR Place_t b = a; PACK pack; { if ((top->edge[PEdge(a)->num]->root!=-1)){ pack.belong = FALSE; pack->num = -1; return pack; } /* Check if any wall incident to "a" belongs to a wall of the original map. */ do { if ((top->wall[PWall(b)->num]->root!=-1)) { pack.belong = TRUE; pack->num = PWall(b)->num; return pack; } b = NextF(b) } while (b != a); pack.belong = FALSE; pack->num = -1; return pack; } /* END @{Edge->?}BelongsToOriginalWall */ bool_t Sign(d: double) /* Return TRUE iff the longreal value is positive, FALSE c.c. */ { assert(d!=0.0); if (d < 0.0){ return FALSE }else{ return TRUE; }; } /* END Sign */ Options_t *GetOptions(int argc, char **argv); int main(int argc, char **argv) <* FATAL Wr.Failure, Thread.Alerted); { Options_t *o = GetOptions(argc, argv); char *topo_cmt = jsprintf("Created by %s on %s", PROG_NAME, Today()); /* Random_t coins = MakeRandomSource(4615); */ ??? tc = Triangulation.ReadToMa(o->inFileTp); ??? top = tc.top; ??? rc = Triangulation.ReadState(o->inFileSt); ??? rc3 = NEW(REF Tridimensional.Coords3D_t; with (top->node.nel), c == rc^, c3 == rc3^, double cmt = tc.cmt & "\n Make by TriangToWire4: " & o->outFile \ ".w4 on " & Today() & "\n" ){ if (o->normalize > 0.0) { fprintf(stderr, "projecting nodes onto the unit S^3\n"); NormalizeNodeCoords(c, o->normalize); } ProjectTo3D(o, c, c3, top); WriteWire4File(o, top, c, c3, cmt); return 0; } PROCEDURE NormalizeNodeCoords(VAR c: Coords_t; newR: double) { ??? b = Barycenter(c); { for (i = 0 TO (c.nel-1)) { ??? p = c[i], q == r4_Sub(p, b), r == r4_Norm(q); { if (r > 0.0){ p = r4_Scale(newR/r, q);} } } } } /* END NormalizeNodeCoords */ r4_t Barycenter(*c: Coords_t) B: r4_t = (r4_t){0.0, ..}; { for (i = 0 TO (c.nel-1)){ B = r4_Add(B, c[i]); } return r4_Scale(1.0/FLOAT((c.nel), double), B); } /* END Barycenter */ void WriteWire4File( Options_t *o = (Options_t *)malloc(sizeof(Options_t)); *ElemTableRec_t *top; *c : Triangulation.Coords_t; *c3 : Tridimensional.Coords3D_t; char *cmt; ) <* FATAL Wr.Failure, Thread.Alerted, OSError.E); { ??? wr = FileWr.Open(o->outFile & ".w4"); { filefmt_write_comment(wr,cmt,'#'); fprintf(wr, "\n"); WriteWire4(o, wr, top, c, c3); fclose(wr) } } /* END WriteWire4File */ void WriteWire4( Options_t *o = (Options_t *)malloc(sizeof(Options_t)); wr: Wr.T; *ElemTableRec_t *top; Coords_t *c; *c3 : Tridimensional.Coords3D_t; ) double FindOriR3(q: FourNodes_t) /* For each tetrahedron with extremus nodes numbers u,v,w,x compute us its orientation in R^{3} through the 4x4 determinant: _ _ | c3[q.u][0] c3[q.u][1] c3[q.u][2] 1.0 | double B = | c3[q.v][0] c3[q.v][1] c3[q.v][2] 1.0 | | c3[q.w][0] c3[q.w][1] c3[q.w][2] 1.0 | | c3[q.x][0] c3[q.x][1] c3[q.x][2] 1.0 | - - */ { with ( double a = (r4_t){c3[q.u][0], c3[q.u][1], c3[q.u][2], 1.0}; double b = (r4_t){c3[q.v][0], c3[q.v][1], c3[q.v][2], 1.0}; double c = (r4_t){c3[q.w][0], c3[q.w][1], c3[q.w][2], 1.0}; double d = (r4_t){c3[q.x][0], c3[q.x][1], c3[q.x][2], 1.0} ){ return r4_det(a,b,c,d); } } FindOriR3; bool_t SilhouetteWalls(Place_t @p) /* Return TRUE iff the wall associated to the @place "a" is a silhouette wall, FALSE c.c. */ { ??? t = TetraNegPosNodes(a); ??? un = t[0]->num; ??? vn = t[1]->num; ??? wn = t[2]->num; ??? xn = t[3]->num; ??? yn = t[4]->num; with ( double t1 = FourNodes_t{un,vn,wn,xn}; double t2 = FourNodes_t{un,vn,wn,yn}; double d1 = FindOriR3(t1); double d2 = FindOriR3(t2) ){ if (((Sign(d1)) && (Sign(d2))) || (((NOT Sign(d1))) && ((NOT Sign(d2))))) { return TRUE; } else { return FALSE; } } } SilhouetteWalls; cv, ce: Row3I; { void WriteCoord(double x) { fprintf(wr, Fmt.Pad(Fmt.LongReal(x, Fmt.Style.Fix, prec = 4), 7)); } WriteCoord; void WritePoint4D(*c: r4_t) { WriteCoord(c[0]); fprintf(wr, " "); WriteCoord(c[1]); fprintf(wr, " "); WriteCoord(c[2]); fprintf(wr, " "); WriteCoord(c[3]); } WritePoint4D; void WritePoint3D(*c: r3_t) { WriteCoord(c[0]); fprintf(wr, " "); WriteCoord(c[1]); fprintf(wr, " "); WriteCoord(c[2]); } WritePoint3D; { int NV = top->node.nel; ??? NE = top->NE; int NF = top->wall.nel; ??? eWidth = digits(NE-1); { fprintf(wr,"\nDepthCueLevels "); Mis.WriteInt(wr,o->DepthCueLevels); fprintf(wr,"\n"); fprintf(wr, "\nFrom4: "); WritePoint4D(o->From4); fprintf(wr, "\nTo4 : "); WritePoint4D(o->To4); fprintf(wr, "\nUp4 : "); WritePoint4D(o->Up4); fprintf(wr, "\nOver4: "); WritePoint4D(o->Over4); fprintf(wr, "\nVangle4: "); Mis.WriteLong(wr, o->Vangle4); fprintf(wr, "\n"); fprintf(wr, "\nFrom3: "); WritePoint3D(o->From3); fprintf(wr, "\nTo3 : "); WritePoint3D(o->To3); fprintf(wr, "\nUp3 : "); WritePoint3D(o->Up3); fprintf(wr, "\nVangle3: "); Mis.WriteLong(wr, o->Vangle3); fprintf(wr, "\n"); if (0 == strcmp(o->projectionName,"Perspective"))) { fprintf(wr, "\nProject4: Perspective"); } else { fprintf(wr, "\nProject4: Parallel"); } /* nodes */ fprintf(wr, "\n\nNodeList " & Fmt.Int(NV) & ":\n"); for (i = 0; i < top->node.nel; i++) { Node_t v = OrgV(top->node.el[i]); { WritePoint4D(c[v->num]); fprintf(wr, " : "); cv[0] = ROUND(v.color[0]*255.0); cv[1] = ROUND(v.color[1]*255.0); cv[2] = ROUND(v.color[2]*255.0); WriteColor(wr, cv); fprintf(wr, " : "); Mis.WriteRadius(wr, v.radius); fprintf(wr, "\n"); } } fprintf(wr, "\n"); /* @{EDGE->?}S */ for (i = 0; i < NE; i++) { ??? e = top->edge[i]; { if ((e->exists) || (o->all)){ nee++;} } } for (i = 0; i < NF; i++) { ??? f = top->wall[i]; { if ((f->exists) || (o->all)){ nfe++;} } } if (o->colored) { /* computing the original number walls */ uint max = 0; { for (i = 0; i < top->wall.nel; i++) { ??? f = top->wall[i]; { if (f->exists) { max = MAX(max,f->root); } } } fprintf(stderr,"The original number walls is " & Fmt.Int(max+1) & "\n"); fac = NEW(REF ARRAY OF Color,max+1); /* defining the palette colors with brightness aprox. 0.7 */ palette[0] = Color{1.00,0.50,1.00}; palette[1] = Color{0.00,1.00,1.00}; palette[2] = Color{0.80,0.80,0.00}; palette[3] = Color{1.00,0.60,0.60}; palette[4] = Color{0.25,1.00,0.25}; palette[5] = Color{0.70,0.70,1.00}; /* attribution of differents colors for each original wall */ for (j = 0; j <= (max); j++) { fac[j] = palette[j % 6]; } } }; if (o->silhouette) { for (i = 0; i < top->wall.nel; i++) { ??? f = top->wall[i]; ??? a = f.pa; with (aen == PEdge(a)->num, b == NextE(a), ben == PEdge(b)->num, c == PrevE(a), cen == PEdge(c)->num ){ if ((SilhouetteWalls(a))) { if ((! Present(estack, etop, aen))) { nee++; Save(estack,etop,aen); } if ((! Present(estack, etop, ben))) { nee++; Save(estack,etop,ben); } if ((! Present(estack, etop, cen))) { nee++; Save(estack,etop,cen); } } } } } /* Clean the stack */ while (etop > 0) { etop = etop - 1; estack.el[etop] = 0; } fprintf(wr, "@{Edge->?}List " & Fmt.Int(nee) & ":\n"); for (i = 0; i < NE; i++) { ??? e = top->edge[i]; ??? a = e.pa; { if ((e->exists) || (o->all)) { fprintf(wr, Fmt.Pad(Fmt.Int(OrgV(a)->num), eWidth) & " " \ Fmt.Pad(Fmt.Int(OrgV(Clock(a))->num), eWidth)); fprintf(wr, " : "); /* color */ if (! o->colored) { ce[0] = ROUND(e.color[0]*255.0); ce[1] = ROUND(e.color[1]*255.0); ce[2] = ROUND(e.color[2]*255.0); } if (o->colored) { if ((e->exists) && (@{Edge->?}BelongsToOriginalWall(a,top).belong)) { /* is an @{edge->?} on the net belonging to some original wall */ ??? facc = @{Edge->?}BelongsToOriginalWall(a; with (top)->num, double froot = top->wall[facc]->root ){ ce[0] = ROUND(fac[froot][0]*255.0); ce[1] = ROUND(fac[froot][1]*255.0); ce[2] = ROUND(fac[froot][2]*255.0); } } else if ((e->exists) && (NOT @{Edge->?}BelongsToOriginalWall(a,top).belong)){ ce[0] = ROUND(e.color[0]*255.0); ce[1] = ROUND(e.color[1]*255.0); ce[2] = ROUND(e.color[2]*255.0); } } WriteColor(wr, ce); fprintf(wr, " : "); Mis.WriteRadius(wr, e.radius); fprintf(wr, "\n"); } } } if (o->silhouette) { for (i = 0; i < top->wall.nel; i++) { ??? f = top->wall[i]; ??? a = f.pa; Edge_t ae = PEdge(a); ??? aen = ae->num; ??? ea = top->edge[aen]; Place_t b = NextE(a); Edge_t be = PEdge(b); ??? ben = be->num; ??? eb = top->edge[ben]; Place_t c = PrevE(a); Edge_t cce = PEdge(c); ??? cen = cce->num; ??? ec = top->edge[cen]; with ( double cs = Row3I{0,255,255} /* color for the edges of the silhouette walls */ ){ if ((SilhouetteWalls(a))) { if ((! Present(estack, etop, aen))) { fprintf(wr, Fmt.Pad(Fmt.Int(OrgV(ea.pa)->num), eWidth) \ " " & Fmt.Pad(Fmt.Int(OrgV(Clock(ea.pa))->num), eWidth)); fprintf(wr, " : "); /* color */ WriteColor(wr, cs); fprintf(wr, " : "); Mis.WriteRadius(wr, ea.radius); fprintf(wr, "\n"); Save(estack,etop,aen); } if ((! Present(estack, etop, ben))) { fprintf(wr, Fmt.Pad(Fmt.Int(OrgV(eb.pa)->num), eWidth) \ " " & Fmt.Pad(Fmt.Int(OrgV(Clock(eb.pa))->num), eWidth)); fprintf(wr, " : "); /* color */ WriteColor(wr, cs); fprintf(wr, " : "); Mis.WriteRadius(wr, eb.radius); fprintf(wr, "\n"); Save(estack,etop,ben); } if ((! Present(estack, etop, cen))) { fprintf(wr, Fmt.Pad(Fmt.Int(OrgV(ec.pa)->num), eWidth) \ " " & Fmt.Pad(Fmt.Int(OrgV(Clock(ec.pa))->num), eWidth)); fprintf(wr, " : "); /* color */ WriteColor(wr, cs); fprintf(wr, " : "); Mis.WriteRadius(wr, ec.radius); fprintf(wr, "\n"); Save(estack,etop,cen); } } } } } fclose(wr); } } } /* END WriteWire4 */ PROCEDURE ProjectTo3D( Options_t *o = (Options_t *)malloc(sizeof(Options_t)); Coords_t *c; VAR c3: Tridimensional.Coords3D_t; ElemTableRec_t *top ) void CalcV4Matrix() /* This procedure computes the four basis vectors for the 4D viewing matrix, Wa,Wb,Wc, and Wd. Note that the Up vector transforms to Wb, the Over vector transforms to Wc, and the line of sight transforms to Wd. The Wa vector is then computed from Wb,Wc and Wd. */ { /* Calculate Wd, the 4th coordinate basis vector and line-of-sight. */ Wd = r4_Sub(o->To4,o->From4); norm = r4_Norm(Wd); if (norm < Epsilon) { fprintf(stderr,"4D To Point and From Point are the same\n"); Process.Exit¦(1); } Wd = r4_Scale(1.0/norm, Wd); /* Calculate Wa, the X-axis basis vector. */ Wa = r4_cross(o->Up4,o->Over4,Wd); norm = r4_Norm(Wa); if (norm < Epsilon) { fprintf(stderr, "4D up,over and view vectors are not perpendicular\n"); Process.Exit¦(1); } Wa = r4_Scale(1.0/norm, Wa); /* Calculate Wb, the perpendicularized Up vector. */ Wb = r4_cross(o->Over4,Wd,Wa); norm = r4_Norm(Wb); if (norm < Epsilon) { fprintf(stderr,"Invalid 4D over vector\n"); Process.Exit¦(1); } Wb = r4_Scale(1.0/norm, Wb); /* Calculate Wc, the perpendicularized Over vector. Note that the resulting vector is already normalized, since Wa, Wb and Wd are all unit vectors. */ Wc = r4_cross(Wd,Wa,Wb); } CalcV4Matrix; VAR Tan2Vangle4, Data4Radius, pconst, rtemp, depth: double; TempV : r4_t; Wa,Wb,Wc,Wd : r4_t; double norm; { ??? angle = o->Vangle4/2.0; ??? angler = (FLOAT(Math.Pi, double)*angle)/180.0); with( ) { Tan2Vangle4 = Math.tan(angler); } /* Find the radius of the 4D data. The radius of the 4D data is the radius of the smallest enclosing sphere, centered at the To point. Note that during the loop through the nodes, Data4Radius holds the squared radius value. */ Data4Radius = 0.0; for (i = 0; i < top->node.nel; i++) { Node_t v = OrgV(top->node.el[i]); ??? Temp4 = r4_Sub(c[v->num]; with (o->To4), double dist = r4_dot(Temp4,Temp4) ){ if (dist > Data4Radius) { Data4Radius = dist; } } } Data4Radius = sqrt(Data4Radius); fprintf(stderr,"Data4Radius: "& Fmt.Pad(Fmt.LongReal(Data4Radius, Fmt.Style.Fix, prec = 4),8)&"\n"); CalcV4Matrix(); if (0 == strcmp(o->projectionName, "Parallel"))) { rtemp = 1.0 / Data4Radius; } else { pconst = 1.0 / Tan2Vangle4; } for (i = 0; i < top->node.nel; i++) { /* Transform the nodes from 4d World coordinates to 4D eye coordinates. */ Node_t v = OrgV(top->node.el[i]); { TempV = r4_Sub(c[v->num],o->From4); depth = r4_dot(TempV,Wd); if (0 == strcmp(o->projectionName, "Perspective"))) { rtemp = pconst / depth; } c3[v->num][0] = rtemp * r4_dot(TempV, Wa); c3[v->num][1] = rtemp * r4_dot(TempV, Wb); c3[v->num][2] = rtemp * r4_dot(TempV, Wc); } } } /* END ProjectTo3D */ PROCEDURE Save( Stack: REF uint_vec_t; uint *top,element; ) /* Saves the "element" on the stack "Stack". */ { Stack.El[top] = element; top++; } /* END Save */ bool_t Present( *Stack: REF uint_vec_t; uint top,element; ) /* Return TRUE if "element" its on the stack, FALSE c.c. */ nstack1: uint = top; { while (nstack1 > 0){ nstack1 = nstack1 - 1; if (Stack.El[nstack1] == element){ return TRUE; } } return FALSE; } Present; Options_t *GetOptions(int argc, char **argv) { Options_t *o = (Options_t *)malloc(sizeof(Options_t)); argparser_t *pp = argparser_new(stderr, argc, argv); argparser_set_help(pp, PROG_NAME " version " PROG_VERS ", usage:\n" PROG_HELP); argparser_set_info(pp, PROG_INFO); argparser_process_help_info_options(pp); argparser_get_keyword(pp, "-inFileTp"); o->inFileTp = argparser_get_next(pp); argparser_get_keyword(pp, "-inFileSt"); o->inFileSt = argparser_get_next(pp); if ((argparser_keyword_present(pp, "-outFile"))) { o->outFile = argparser_get_next(pp) } else { o->outFile = o->inFileSt } if ((argparser_keyword_present(pp, "-normalize"))) { /* Desired radius of model in R^4 */ o->normalize = pp.getNextLongReal(1.0d-10,1.0d+10); } else { o->normalize = 0.0 /* No normalization */ }; if ((argparser_keyword_present(pp, "-DepthCueLevels"))) { o->DepthCueLevels = argparser_get_next_int(pp, 1, 255); } else { o->DepthCueLevels = 16; } argparser_get_keyword(pp, "-projection"); o->projectionName = argparser_get_next(pp); if ((! 0 == strcmp(o->projectionName, "Parallel") OR Text.Equal(o->projectionName, "Perspective")) )){ argparser_error(pp, "Bad projection \"" & argparser_get_next(pp) & "\"\n") } if ((argparser_keyword_present(pp, "-From4"))) { for (j = 0; j < 4; j++) { o->From4[j] = pp.getNextLongReal(-100.0, 100.0); } } else { o->From4 = (r4_t){0.0,0.0,0.0,-5.0}; } if ((argparser_keyword_present(pp, "-To4"))) { for (j = 0; j < 4; j++) { o->To4[j] = pp.getNextLongReal(-100.0, 100.0); } } else { o->To4 = (r4_t){0.0,0.0,0.0,0.0}; } if ((argparser_keyword_present(pp, "-Up4"))) { for (j = 0; j < 4; j++) { o->Up4[j] = pp.getNextLongReal(-100.0, 100.0); } } else { o->Up4 = (r4_t){0.0,1.0,0.0,0.0}; } if ((argparser_keyword_present(pp, "-Over4"))) { for (j = 0; j < 4; j++) { o->Over4[j] = pp.getNextLongReal(-100.0, 100.0); } } else { o->Over4 = (r4_t){0.0,0.0,1.0,0.0}; } if ((argparser_keyword_present(pp, "-Vangle4"))) { o->Vangle4 = pp.getNextLongReal(1.0, 179.0); } else { o->Vangle4 = 45.0; } if ((argparser_keyword_present(pp, "-From3"))) { for (j = 0; j < 3; j++) { o->From3[j] = pp.getNextLongReal(-100.0, 100.0); } } else { o->From3 = (r3_t){2.0,1.5,3.0}; } if ((argparser_keyword_present(pp, "-To3"))) { for (j = 0; j < 3; j++) { o->To3[j] = pp.getNextLongReal(-100.0, 100.0); } } else { o->To3 = (r3_t){0.0,0.0,0.0}; } if ((argparser_keyword_present(pp, "-Up3"))) { for (j = 0; j < 3; j++) { o->Up3[j] = pp.getNextLongReal(-100.0, 100.0); } } else { o->Up3 = (r3_t){0.0,1.0,0.0}; } if ((argparser_keyword_present(pp, "-Vangle3"))) { o->Vangle3 = pp.getNextLongReal(1.0, 179.0); } else { o->Vangle3 = 45.0; } o->all = argparser_keyword_present(pp, "-all"); o->silhouette = argparser_keyword_present(pp, "-silhouette"); o->colored = argparser_keyword_present(pp, "-colored"); if ((argparser_keyword_present(pp, "-ordernet"))) { o->ordernet = argparser_get_next_int(pp, 1, 5); } argparser_finish(pp); ----------------------------------- #define _HELP \ fprintf(stderr, "Usage: TriangToWire4 \\\n" \ " -inFileTp \\\n" \ " -inFileSt \\\n" \ " [ -outFile ] \\\n" \ " -projection [ Perspective | Parallel ] \\\n" \ " [-normalize ] \\\n" \ " [ -From4 ]\\ \n" \ " [ -To4 ] \\\n" \ " [ -Up4 ] \\\n" \ " [ -Over4 ] \\\n" \ " [ -Vangle4 ] \\\n" \ " [ -From3 ] \\\n" \ " [ -To3 ] \\\n" \ " [ -Up3 ] \\\n" \ " [ -Vangle3 ] \\\n" \ " [ -DepthCueLevels ] \\\n" \ " [ -silhouette ]\n"); END¦ } } return o; } /* END GetOptions */ /* end TriangToWire4 */ /* Copyright © 2001 Universidade Estadual de Campinas (UNICAMP) */