/** *@brief * Draw a vector between points "from" and "to", with the option of * having an arrowhead on either or both ends. * * The fromheadfract and toheadfract values indicate the length of the * arrowheads relative to the length of the vector to-from. A typical * value is 0.1, making the head 10% of the size of the vector. The * sign of the "fract" values indicates the pointing direction. * Positive points towards the "to" point, negative points towards * "from". Upon return, the virtual pen is left at the "to" position. */ void tp_3vector(FILE *plotfp, fastf_t *from, fastf_t *to, double fromheadfract, double toheadfract) { register fastf_t len; register fastf_t hooklen; vect_t diff; vect_t c1, c2; vect_t h1, h2; vect_t backup; point_t tip; pdv_3line(plotfp, from, to); /* "pen" is left at "to" position */ VSUB2(diff, to, from); if ((len = MAGNITUDE(diff)) < SMALL) return; VSCALE(diff, diff, 1/len); bn_vec_ortho(c1, diff); VCROSS(c2, c1, diff); if (!ZERO(fromheadfract)) { hooklen = fromheadfract*len; VSCALE(backup, diff, -hooklen); VSCALE(h1, c1, hooklen); VADD3(tip, from, h1, backup); pdv_3move(plotfp, from); pdv_3cont(plotfp, tip); VSCALE(h2, c2, hooklen); VADD3(tip, from, h2, backup); pdv_3move(plotfp, tip); } if (!ZERO(toheadfract)) { hooklen = toheadfract*len; VSCALE(backup, diff, -hooklen); VSCALE(h1, c1, hooklen); VADD3(tip, to, h1, backup); pdv_3move(plotfp, to); pdv_3cont(plotfp, tip); VSCALE(h2, c2, hooklen); VADD3(tip, to, h2, backup); pdv_3move(plotfp, tip); } /* Be certain "pen" is left at "to" position */ if (!ZERO(fromheadfract) || !ZERO(toheadfract)) pdv_3cont(plotfp, to); }
void rt_vlist_to_uplot(FILE *fp, const struct bu_list *vhead) { register struct bn_vlist *vp; for (BU_LIST_FOR(vp, bn_vlist, vhead)) { register int i; register int nused = vp->nused; register const int *cmd = vp->cmd; register point_t *pt = vp->pt; for (i = 0; i < nused; i++, cmd++, pt++) { switch (*cmd) { case BN_VLIST_POLY_START: case BN_VLIST_TRI_START: break; case BN_VLIST_POLY_MOVE: case BN_VLIST_LINE_MOVE: case BN_VLIST_TRI_MOVE: pdv_3move(fp, *pt); break; case BN_VLIST_POLY_DRAW: case BN_VLIST_POLY_END: case BN_VLIST_LINE_DRAW: case BN_VLIST_TRI_DRAW: case BN_VLIST_TRI_END: pdv_3cont(fp, *pt); break; default: bu_log("rt_vlist_to_uplot: unknown vlist cmd x%x\n", *cmd); } } } }
int main(int argc, char *argv) { double xyz[3]; int i; int first = 1; for (;;) { xyz[0] = xyz[1] = xyz[2] = 0.0; buf[0] = '\0'; bu_fgets( buf, sizeof(buf), stdin ); if ( feof(stdin) ) break; i = sscanf( buf, "%lf %lf %lf", &xyz[0], &xyz[1], &xyz[2] ); if (debug) { fprintf(stderr, "buf=%s", buf); fprintf(stderr, "%d: %f\t%f\t%f\n", i, xyz[0], xyz[1], xyz[2] ); } if ( i <= 0 ) break; if ( first ) { first = 0; pdv_3move( stdout, xyz ); } else { pdv_3cont( stdout, xyz ); } } return 0; }
void plot_face(ON_3dPoint *pt1, ON_3dPoint *pt2, ON_3dPoint *pt3, int r, int g, int b, FILE *c_plot) { point_t p1, p2, p3; VSET(p1, pt1->x, pt1->y, pt1->z); VSET(p2, pt2->x, pt2->y, pt2->z); VSET(p3, pt3->x, pt3->y, pt3->z); pl_color(c_plot, r, g, b); pdv_3move(c_plot, p1); pdv_3cont(c_plot, p2); pdv_3move(c_plot, p1); pdv_3cont(c_plot, p3); pdv_3move(c_plot, p2); pdv_3cont(c_plot, p3); }
/** * Draw a ray */ void pdv_3ray(FILE *fp, const fastf_t *pt, const fastf_t *dir, double t) { point_t tip; VJOIN1( tip, pt, t, dir ); pdv_3move( fp, pt ); pdv_3cont( fp, tip ); }
HIDDEN void plot_obr(int test_num, const point_t *pnt_array, int pnt_cnt) { int i = 0; struct bu_vls name; FILE *plot_file = NULL; bu_vls_init(&name); bu_vls_printf(&name, "obr_test_%.3d.pl", test_num); plot_file = fopen(bu_vls_addr(&name), "w"); pl_color(plot_file, 0, 255, 0); for (i = 0; i < pnt_cnt; i++) { pdv_3move(plot_file, pnt_array[i]); if (i < pnt_cnt - 1) { pdv_3cont(plot_file, pnt_array[i+1]); } else { pdv_3cont(plot_file, pnt_array[0]); } } fclose(plot_file); bu_vls_free(&name); }
void view_eol(struct application *UNUSED(ap)) { struct cell *posp; size_t i; int cont; /* continue flag */ posp = &(cellp[0]); cont = 0; /* Plot the starting point and set cont to 0. Then * march along the entire array and continue to plot the * hit points based on their distance from the emanation * plane. When consecutive hit-points with identical distances * are found, cont is set to one so that the entire sequence * of like-distanced hit-points can be plotted together. */ pdv_3move( outfp, posp->c_hit ); for ( i = 0; i < width-1; i++, posp++ ) { if (EQUAL(posp->c_dist, (posp+1)->c_dist)) { cont = 1; continue; } else { if (cont) { pdv_3cont(outfp, posp->c_hit); cont = 0; } pdv_3cont(outfp, (posp+1)->c_hit); } } /* Catch the boundary condition if the last couple of cells * heve the same distance. */ pdv_3cont(outfp, posp->c_hit); }
/* * R A Y H I T * * Rayhit() is called by rt_shootray() when the ray hits one or more objects. * A per-shotline header record is written, followed by information about * each object hit. * * Note that the GIFT-3 format uses a different convention for the "zero" * distance along the ray. RT has zero at the ray origin (emanation plain), * while GIFT has zero at the screen plain translated so that it contains * the model origin. This difference is compensated for by adding the * 'dcorrection' distance correction factor. * * Also note that the GIFT-3 format requires information about the start * point of the ray in two formats. First, the h, v coordinates of the * grid cell CENTERS (in screen space coordinates) are needed. * Second, the ACTUAL h, v coordinates fired from are needed. * * An optional rtg3.pl UnixPlot file is written, permitting a * color vector display of ray-model intersections. */ int rayhit(struct application *ap, register struct partition *PartHeadp, struct seg *segp) { register struct partition *pp = PartHeadp->pt_forw; int comp_count; /* component count */ fastf_t dfirst, dlast; /* ray distances */ static fastf_t dcorrection = 0; /* RT to GIFT dist corr */ int card_count; /* # comp. on this card */ const char *fmt; /* printf() format string */ struct bu_vls str; char buf[128]; /* temp. sprintf() buffer */ point_t hv; /* GIFT h, v coords, in inches */ point_t hvcen; int prev_id=-1; point_t first_hit; int first; if ( pp == PartHeadp ) return(0); /* nothing was actually hit?? */ if ( ap->a_rt_i->rti_save_overlaps ) rt_rebuild_overlaps( PartHeadp, ap, 1 ); part_compact(ap, PartHeadp, TOL); /* count components in partitions */ comp_count = 0; for ( pp=PartHeadp->pt_forw; pp!=PartHeadp; pp=pp->pt_forw ) { if ( pp->pt_regionp->reg_regionid > 0 ) { prev_id = pp->pt_regionp->reg_regionid; comp_count++; } else if ( prev_id <= 0 ) { /* normally air would be output along with a solid partition, but this will require a '111' partition */ prev_id = pp->pt_regionp->reg_regionid; comp_count++; } else prev_id = pp->pt_regionp->reg_regionid; } pp = PartHeadp->pt_back; if ( pp!=PartHeadp && pp->pt_regionp->reg_regionid <= 0 ) comp_count++; /* a trailing '111' ident */ if ( comp_count == 0 ) return( 0 ); /* Set up variable length string, to buffer this shotline in. * Note that there is one component per card, and that each card * (line) is 80 characters long. Hence the parameters given to * rt-vls-extend(). */ bu_vls_init( &str ); bu_vls_extend( &str, 80 * (comp_count+1) ); /* * Find the H, V coordinates of the grid cell center. * RT uses the lower left corner of each cell. */ { point_t center; fastf_t dx; fastf_t dy; dx = ap->a_x + 0.5; dy = ap->a_y + 0.5; VJOIN2( center, viewbase_model, dx, dx_model, dy, dy_model ); MAT4X3PNT( hvcen, model2hv, center ); } /* * Find exact h, v coordinates of actual ray start by * projecting start point into GIFT h, v coordinates. */ MAT4X3PNT( hv, model2hv, ap->a_ray.r_pt ); /* * In RT, rays are launched from the plane of the screen, * and ray distances are relative to the start point. * In GIFT-3 output files, ray distances are relative to * the (H, V) plane translated so that it contains the origin. * A distance correction is required to convert between the two. * Since this really should be computed only once, not every time, * the trip_count flag was added. */ { static int trip_count; vect_t tmp; vect_t viewZdir; if ( trip_count == 0) { VSET( tmp, 0, 0, -1 ); /* viewing direction */ MAT4X3VEC( viewZdir, view2model, tmp ); VUNITIZE( viewZdir ); /* dcorrection will typically be negative */ dcorrection = VDOT( ap->a_ray.r_pt, viewZdir ); trip_count = 1; } } /* This code is for diagnostics. * bu_log("dcorrection=%g\n", dcorrection); */ /* dfirst and dlast have been made negative to account for GIFT looking * in the opposite direction of RT. */ dfirst = -(PartHeadp->pt_forw->pt_inhit->hit_dist + dcorrection); dlast = -(PartHeadp->pt_back->pt_outhit->hit_dist + dcorrection); #if 0 /* This code is to note any occurances of negative distances. */ if ( PartHeadp->pt_forw->pt_inhit->hit_dist < 0) { bu_log("ERROR: dfirst=%g at partition x%x\n", dfirst, PartHeadp->pt_forw ); bu_log("\tdcorrection = %f\n", dcorrection ); bu_log("\tray start point is ( %f %f %f ) in direction ( %f %f %f )\n", V3ARGS( ap->a_ray.r_pt ), V3ARGS( ap->a_ray.r_dir ) ); VJOIN1( PartHeadp->pt_forw->pt_inhit->hit_point, ap->a_ray.r_pt, PartHeadp->pt_forw->pt_inhit->hit_dist, ap->a_ray.r_dir ); VJOIN1( PartHeadp->pt_back->pt_outhit->hit_point, ap->a_ray.r_pt, PartHeadp->pt_forw->pt_outhit->hit_dist, ap->a_ray.r_dir ); rt_pr_partitions(ap->a_rt_i, PartHeadp, "Defective partion:"); } /* End of bug trap. */ #endif /* * Output the ray header. The GIFT statements that * would have generated this are: * 410 write(1, 411) hcen, vcen, h, v, ncomp, dfirst, dlast, a, e * 411 format(2f7.1, 2f9.3, i3, 2f8.2,' A', f6.1,' E', f6.1) */ #define SHOT_FMT "%7.1f%7.1f%9.3f%9.3f%3d%8.2f%8.2f A%6.1f E%6.1f" if ( rt_perspective > 0 ) { bn_ae_vec( &azimuth, &elevation, ap->a_ray.r_dir ); } bu_vls_printf( &str, SHOT_FMT, hvcen[0], hvcen[1], hv[0], hv[1], comp_count, dfirst * MM2IN, dlast * MM2IN, azimuth, elevation ); /* * As an aid to debugging, take advantage of the fact that * there are more than 80 columns on UNIX "cards", and * add debugging information to the end of the line to * allow this shotline to be reproduced offline. * -b gives the shotline x, y coordinates when re-running RTG3, * -p and -d are used with RTSHOT * The easy way to activate this is with the harmless -!1 option * when running RTG3. */ if ( R_DEBUG || bu_debug || RT_G_DEBUG ) { bu_vls_printf( &str, " -b%d,%d -p %26.20e %26.20e %26.20e -d %26.20e %26.20e %26.20e\n", ap->a_x, ap->a_y, V3ARGS(ap->a_ray.r_pt), V3ARGS(ap->a_ray.r_dir) ); } else { bu_vls_putc( &str, '\n' ); } /* loop here to deal with individual components */ card_count = 0; prev_id = -1; first = 1; for ( pp=PartHeadp->pt_forw; pp!=PartHeadp; pp=pp->pt_forw ) { /* * The GIFT statements that would have produced * this output are: * do 632 i=icomp, iend * if (clos(icomp).gt.999.99.or.slos(i).gt.999.9) goto 635 * 632 continue * write(1, 633)(item(i), clos(i), cangi(i), cango(i), * & kspac(i), slos(i), i=icomp, iend) * 633 format(1x, 3(i4, f6.2, 2f5.1, i1, f5.1)) * goto 670 * 635 write(1, 636)(item(i), clos(i), cangi(i), cango(i), * & kspac(i), slos(i), i=icomp, iend) * 636 format(1x, 3(i4, f6.1, 2f5.1, i1, f5.0)) */ fastf_t comp_thickness; /* component line of sight thickness */ fastf_t in_obliq; /* in obliquity angle */ fastf_t out_obliq; /* out obliquity angle */ int region_id; /* solid region's id */ int air_id; /* air id */ fastf_t dot_prod; /* dot product of normal and ray dir */ fastf_t air_thickness; /* air line of sight thickness */ vect_t normal; /* surface normal */ register struct partition *nextpp = pp->pt_forw; region_id = pp->pt_regionp->reg_regionid; if ( region_id <= 0 && prev_id > 0 ) { /* air region output with previous partition */ prev_id = region_id; continue; } comp_thickness = pp->pt_outhit->hit_dist - pp->pt_inhit->hit_dist; /* The below code is meant to catch components with zero or * negative thicknesses. This is not supposed to be possible, * but the condition has been seen. */ #if 0 if ( comp_thickness <= 0 ) { VJOIN1( pp->pt_inhit->hit_point, ap->a_ray.r_pt, pp->pt_inhit->hit_dist, ap->a_ray.r_dir ); VJOIN1( pp->pt_outhit->hit_point, ap->a_ray.r_pt, pp->pt_outhit->hit_dist, ap->a_ray.r_dir ); bu_log("ERROR: comp_thickness=%g for region id = %d at h=%g, v=%g (x=%d, y=%d), partition at x%x\n", comp_thickness, region_id, hv[0], hv[1], ap->a_x, ap->a_y, pp ); rt_pr_partitions(ap->a_rt_i, PartHeadp, "Defective partion:"); bu_log("Send this output to the BRL-CAD Developers ([email protected])\n"); if ( ! (RT_G_DEBUG & DEBUG_ARB8)) { rt_g.debug |= DEBUG_ARB8; rt_shootray(ap); rt_g.debug &= ~DEBUG_ARB8; } } #endif if ( nextpp == PartHeadp ) { if ( region_id <= 0 ) { /* last partition is air, need a 111 'phantom armor' before AND after */ bu_log( "WARNING: adding 'phantom armor' (id=111) with zero thickness before and after air region %s\n", pp->pt_regionp->reg_name ); region_id = 111; air_id = pp->pt_regionp->reg_aircode; air_thickness = comp_thickness; comp_thickness = 0.0; } else { /* Last partition, no air follows, use code 9 */ air_id = 9; air_thickness = 0.0; } } else if ( region_id <= 0 ) { /* air region, need a 111 'phantom armor' */ bu_log( "WARNING: adding 'phantom armor' (id=111) with zero thickness before air region %s\n", pp->pt_regionp->reg_name ); prev_id = region_id; region_id = 111; air_id = pp->pt_regionp->reg_aircode; air_thickness = comp_thickness; comp_thickness = 0.0; } else if ( nextpp->pt_regionp->reg_regionid <= 0 && nextpp->pt_regionp->reg_aircode != 0 ) { /* Next partition is air region */ air_id = nextpp->pt_regionp->reg_aircode; air_thickness = nextpp->pt_outhit->hit_dist - nextpp->pt_inhit->hit_dist; prev_id = air_id; } else { /* 2 solid regions, maybe with gap */ air_id = 0; air_thickness = nextpp->pt_inhit->hit_dist - pp->pt_outhit->hit_dist; if ( air_thickness < 0.0 ) air_thickness = 0.0; if ( !NEAR_ZERO( air_thickness, 0.1 ) ) { air_id = 1; /* air gap */ if ( R_DEBUG & RDEBUG_HITS ) bu_log("air gap added\n"); } else { air_thickness = 0.0; } prev_id = region_id; } /* * Compute the obliquity angles in degrees, ie, * the "declension" angle down off the normal vector. * RT normals always point outwards; * the "inhit" normal points opposite the ray direction, * the "outhit" normal points along the ray direction. * Hence the one sign change. * XXX this should probably be done with atan2() */ if ( first ) { first = 0; VJOIN1( first_hit, ap->a_ray.r_pt, pp->pt_inhit->hit_dist, ap->a_ray.r_dir ); } out: RT_HIT_NORMAL( normal, pp->pt_inhit, pp->pt_inseg->seg_stp, &(ap->a_ray), pp->pt_inflip ); dot_prod = VDOT( ap->a_ray.r_dir, normal ); if ( dot_prod > 1.0 ) dot_prod = 1.0; if ( dot_prod < -1.0 ) dot_prod = (-1.0); in_obliq = acos( -dot_prod ) * bn_radtodeg; RT_HIT_NORMAL( normal, pp->pt_outhit, pp->pt_outseg->seg_stp, &(ap->a_ray), pp->pt_outflip ); dot_prod = VDOT( ap->a_ray.r_dir, normal ); if ( dot_prod > 1.0 ) dot_prod = 1.0; if ( dot_prod < -1.0 ) dot_prod = (-1.0); out_obliq = acos( dot_prod ) * bn_radtodeg; /* Check for exit obliquties greater than 90 degrees. */ #if 0 if ( in_obliq > 90 || in_obliq < 0 ) { bu_log("ERROR: in_obliquity=%g\n", in_obliq); rt_pr_partitions(ap->a_rt_i, PartHeadp, "Defective partion:"); } if ( out_obliq > 90 || out_obliq < 0 ) { bu_log("ERROR: out_obliquity=%g\n", out_obliq); VPRINT(" r_dir", ap->a_ray.r_dir); VPRINT("normal", normal); bu_log("dot=%g, acos(dot)=%g\n", VDOT( ap->a_ray.r_dir, normal ), acos( VDOT( ap->a_ray.r_dir, normal ) ) ); /* Print the defective one */ rt_pr_pt( ap->a_rt_i, pp ); /* Print the whole ray's partition list */ rt_pr_partitions(ap->a_rt_i, PartHeadp, "Defective partion:"); } #endif if ( in_obliq > 90.0 ) in_obliq = 90.0; if ( in_obliq < 0.0 ) in_obliq = 0.0; if ( out_obliq > 90.0 ) out_obliq = 90.0; if ( out_obliq < 0.0 ) out_obliq = 0.0; /* * Handle 3-components per card output format, with * a leading space in front of the first component. */ if ( card_count == 0 ) { bu_vls_strcat( &str, " " ); } comp_thickness *= MM2IN; /* Check thickness fields for format overflow */ if ( comp_thickness > 999.99 || air_thickness*MM2IN > 999.9 ) fmt = "%4d%6.1f%5.1f%5.1f%1d%5.0f"; else fmt = "%4d%6.2f%5.1f%5.1f%1d%5.1f"; #ifdef SPRINTF_NOT_PARALLEL bu_semaphore_acquire( BU_SEM_SYSCALL ); #endif snprintf(buf, 128, fmt, region_id, comp_thickness, in_obliq, out_obliq, air_id, air_thickness*MM2IN ); #ifdef SPRINTF_NOT_PARALLEL bu_semaphore_release( BU_SEM_SYSCALL ); #endif bu_vls_strcat( &str, buf ); card_count++; if ( card_count >= 3 ) { bu_vls_strcat( &str, "\n" ); card_count = 0; } /* A color rtg3.pl UnixPlot file of output commands * is generated. This is processed by plot(1) * plotting filters such as pl-fb or pl-sgi. * Portions of a ray passing through air within the * model are represented in blue, while portions * passing through a solid are assigned green. * This will always be done single CPU, * to prevent output garbling. (See view_init). */ if (R_DEBUG & RDEBUG_RAYPLOT) { vect_t inpt; vect_t outpt; VJOIN1(inpt, ap->a_ray.r_pt, pp->pt_inhit->hit_dist, ap->a_ray.r_dir); VJOIN1(outpt, ap->a_ray.r_pt, pp->pt_outhit->hit_dist, ap->a_ray.r_dir); pl_color(plotfp, 0, 255, 0); /* green */ pdv_3line(plotfp, inpt, outpt); if (air_thickness > 0) { vect_t air_end; VJOIN1(air_end, ap->a_ray.r_pt, pp->pt_outhit->hit_dist + air_thickness, ap->a_ray.r_dir); pl_color(plotfp, 0, 0, 255); /* blue */ pdv_3cont(plotfp, air_end); } } if ( nextpp == PartHeadp && air_id != 9 ) { /* need to output a 111 'phantom armor' at end of shotline */ air_id = 9; air_thickness = 0.0; region_id = 111; comp_thickness = 0.0; goto out; } } /* If partway through building the line, add a newline */ if ( card_count > 0 ) { /* * Note that GIFT zero-fills the unused component slots, * but neither COVART II nor COVART III require it, * so just end the line here. */ bu_vls_strcat( &str, "\n" ); } /* Single-thread through file output. * COVART will accept non-sequential ray data provided the * ray header and its associated data are not separated. CAVEAT: * COVART will not accept headers out of sequence. */ bu_semaphore_acquire( BU_SEM_SYSCALL ); fputs( bu_vls_addr( &str ), outfp ); if ( shot_fp ) { fprintf( shot_fp, "%.5f %.5f %.5f %.5f %.5f %.5f %.5f %.5f %ld %.5f %.5f %.5f\n", azimuth, elevation, V3ARGS( ap->a_ray.r_pt ), V3ARGS( ap->a_ray.r_dir ), line_num, V3ARGS( first_hit) ); line_num += 1 + (comp_count / 3 ); if ( comp_count % 3 ) line_num++; } /* End of single-thread region */ bu_semaphore_release( BU_SEM_SYSCALL ); /* Release vls storage */ bu_vls_free( &str ); return(0); }
/* * T P _ 3 S Y M B O L */ void tp_3symbol(FILE *fp, char *string, fastf_t *origin, fastf_t *rot, double scale) /* string of chars to be plotted */ /* lower left corner of 1st char */ /* Transform matrix (WARNING: may xlate) */ /* scale factor to change 1x1 char sz */ { register unsigned char *cp; double offset; /* offset of char from given x, y */ int ysign; /* sign of y motion, either +1 or -1 */ vect_t temp; vect_t loc; mat_t xlate_to_origin; mat_t mat; if ( string == NULL || *string == '\0' ) return; /* done before begun! */ /* * The point "origin" will be the center of the axis rotation. * The text is located in a local coordinate system with the * lower left corner of the first character at (0, 0, 0), with * the text proceeding onward towards +X. * We need to rotate the text around its local (0, 0, 0), * and then translate to the user's designated "origin". * If the user provided translation or * scaling in his matrix, it will *also* be applied. */ MAT_IDN( xlate_to_origin ); MAT_DELTAS_VEC( xlate_to_origin, origin ); bn_mat_mul( mat, xlate_to_origin, rot ); /* Check to see if initialization is needed */ if ( tp_cindex[040] == 0 ) tp_setup(); /* Draw each character in the input string */ offset = 0; for ( cp = (unsigned char *)string; *cp; cp++, offset += scale ) { register int *p; /* pointer to stroke table */ register int stroke; VSET( temp, offset, 0, 0 ); MAT4X3PNT( loc, mat, temp ); pdv_3move( fp, loc ); for ( p = tp_cindex[*cp]; (stroke= *p) != LAST; p++ ) { int draw; if ( stroke==NEGY ) { ysign = (-1); stroke = *++p; } else ysign = 1; /* Detect & process pen control */ if ( stroke < 0 ) { stroke = -stroke; draw = 0; } else draw = 1; /* stroke co-ordinates in string coord system */ VSET( temp, (stroke/11) * 0.1 * scale + offset, (ysign * (stroke%11)) * 0.1 * scale, 0 ); MAT4X3PNT( loc, mat, temp ); if ( draw ) pdv_3cont( fp, loc ); else pdv_3move( fp, loc ); } } }