/* * 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); }
int hit(register struct application *ap, struct partition *PartHeadp, struct seg *segp) { register struct partition *pp; register struct soltab *stp; struct curvature cur; fastf_t out; point_t inpt, outpt; vect_t inormal, onormal; if ( (pp=PartHeadp->pt_forw) == PartHeadp ) return(0); /* Nothing hit?? */ if ( overlap_claimant_handling == 1 ) rt_rebuild_overlaps( PartHeadp, ap, 1 ); else if ( overlap_claimant_handling == 2 ) rt_rebuild_overlaps( PartHeadp, ap, 0 ); /* First, plot ray start to inhit */ if ( R_DEBUG&RDEBUG_RAYPLOT ) { if ( pp->pt_inhit->hit_dist > 0.0001 ) { VJOIN1( inpt, ap->a_ray.r_pt, pp->pt_inhit->hit_dist, ap->a_ray.r_dir ); pl_color( plotfp, 0, 0, 255 ); pdv_3line( plotfp, ap->a_ray.r_pt, inpt ); } } for (; pp != PartHeadp; pp = pp->pt_forw ) { matp_t inv_mat; Tcl_HashEntry *entry; bu_log("\n--- Hit region %s (in %s, out %s) reg_bit = %d\n", pp->pt_regionp->reg_name, pp->pt_inseg->seg_stp->st_name, pp->pt_outseg->seg_stp->st_name, pp->pt_regionp->reg_bit); entry = Tcl_FindHashEntry( (Tcl_HashTable *)ap->a_rt_i->Orca_hash_tbl, (const char *)(size_t)pp->pt_regionp->reg_bit ); if ( !entry ) { inv_mat = (matp_t)NULL; } else { inv_mat = (matp_t)Tcl_GetHashValue( entry ); bn_mat_print( "inv_mat", inv_mat ); } if ( pp->pt_overlap_reg ) { struct region *pp_reg; int j=-1; bu_log( " Claiming regions:\n" ); while ( (pp_reg=pp->pt_overlap_reg[++j]) ) bu_log( " %s\n", pp_reg->reg_name ); } /* inhit info */ stp = pp->pt_inseg->seg_stp; VJOIN1( inpt, ap->a_ray.r_pt, pp->pt_inhit->hit_dist, ap->a_ray.r_dir ); RT_HIT_NORMAL( inormal, pp->pt_inhit, stp, &(ap->a_ray), pp->pt_inflip ); RT_CURVATURE( &cur, pp->pt_inhit, pp->pt_inflip, stp ); rt_pr_hit( " In", pp->pt_inhit ); VPRINT( " Ipoint", inpt ); VPRINT( " Inormal", inormal ); bu_log( " PDir (%g, %g, %g) c1=%g, c2=%g\n", V3ARGS(cur.crv_pdir), cur.crv_c1, cur.crv_c2); if ( inv_mat ) { point_t in_trans; MAT4X3PNT( in_trans, inv_mat, inpt ); bu_log( "\ttransformed ORCA inhit = (%g %g %g)\n", V3ARGS( in_trans ) ); } /* outhit info */ stp = pp->pt_outseg->seg_stp; VJOIN1( outpt, ap->a_ray.r_pt, pp->pt_outhit->hit_dist, ap->a_ray.r_dir ); RT_HIT_NORMAL( onormal, pp->pt_outhit, stp, &(ap->a_ray), pp->pt_outflip ); RT_CURVATURE( &cur, pp->pt_outhit, pp->pt_outflip, stp ); rt_pr_hit( " Out", pp->pt_outhit ); VPRINT( " Opoint", outpt ); VPRINT( " Onormal", onormal ); bu_log( " PDir (%g, %g, %g) c1=%g, c2=%g\n", V3ARGS(cur.crv_pdir), cur.crv_c1, cur.crv_c2); if ( inv_mat ) { point_t out_trans; vect_t dir_trans; MAT4X3PNT( out_trans, inv_mat, outpt ); MAT4X3VEC( dir_trans, inv_mat, ap->a_ray.r_dir ); VUNITIZE( dir_trans ); bu_log( "\ttranformed ORCA outhit = (%g %g %g)\n", V3ARGS( out_trans ) ); bu_log( "\ttransformed ORCA ray direction = (%g %g %g)\n", V3ARGS( dir_trans ) ); } /* Plot inhit to outhit */ if ( R_DEBUG&RDEBUG_RAYPLOT ) { if ( (out = pp->pt_outhit->hit_dist) >= INFINITY ) out = 10000; /* to imply the direction */ VJOIN1( outpt, ap->a_ray.r_pt, out, ap->a_ray.r_dir ); pl_color( plotfp, 0, 255, 255 ); pdv_3line( plotfp, inpt, outpt ); } { struct region *regp = pp->pt_regionp; int i; if ( ap->attrs ) { bu_log( "\tattribute values:\n" ); i = 0; while ( ap->attrs[i] && regp->attr_values[i] ) { bu_log( "\t\t%s:\n", ap->attrs[i] ); bu_log( "\t\t\tstring rep = %s\n", BU_MRO_GETSTRING(regp->attr_values[i])); bu_log( "\t\t\tlong rep = %d\n", BU_MRO_GETLONG(regp->attr_values[i])); bu_log( "\t\t\tdouble rep = %f\n", BU_MRO_GETDOUBLE(regp->attr_values[i])); i++; } } } } return(1); }