// To write an X3DOM-compatible scene file, we cannot include // LineProperties nodes. void X3DOMDisplayDevice::text(float *pos, float size, float thickness, const char *str) { float textpos[3]; float textsize; hersheyhandle hh; // transform the world coordinates (transMat.top()).multpoint3d(pos, textpos); textsize = size * 1.5f; ResizeArray<int> idxs; ResizeArray<float> pnts; idxs.clear(); pnts.clear(); int idx=0; while (*str != '\0') { float lm, rm, x, y; int draw; x=y=0.0f; draw=0; hersheyDrawInitLetter(&hh, *str, &lm, &rm); textpos[0] -= lm * textsize; while (!hersheyDrawNextLine(&hh, &draw, &x, &y)) { float pt[3]; if (draw) { // add another connected point to the line strip idxs.append(idx); pt[0] = textpos[0] + textsize * x; pt[1] = textpos[1] + textsize * y; pt[2] = textpos[2]; pnts.append(pt[0]); pnts.append(pt[1]); pnts.append(pt[2]); idx++; } else { idxs.append(-1); // pen-up, end of the line strip } } idxs.append(-1); // pen-up, end of the line strip textpos[0] += rm * textsize; str++; } fprintf(outfile, "<Shape>\n"); fprintf(outfile, " "); // // Emit the line properties // fprintf(outfile, "<Appearance><Material "); fprintf(outfile, "ambientIntensity='%g' ", mat_ambient); fprintf(outfile, "diffuseColor='0 0 0' "); const float *rgb = matData[colorIndex]; fprintf(outfile, "emissiveColor='%g %g %g' ", mat_diffuse * rgb[0], mat_diffuse * rgb[1], mat_diffuse * rgb[2]); fprintf(outfile, "/>"); #if 0 // XXX X3DOM v1.1 doesn't handle LineProperties nodes // emit a line thickness directive, if needed if (thickness < 0.99f || thickness > 1.01f) { fprintf(outfile, " <LineProperties linewidthScaleFactor=\"%g\" " "containerField=\"lineProperties\"/>\n", (double) thickness); } #endif fprintf(outfile, "</Appearance>\n"); // // Emit the line set // fprintf(outfile, " <IndexedLineSet coordIndex='"); int i, cnt; cnt = idxs.num(); for (i=0; i<cnt; i++) { fprintf(outfile, "%d ", idxs[i]); } fprintf(outfile, "'>\n"); fprintf(outfile, " <Coordinate point='"); cnt = pnts.num(); for (i=0; i<cnt; i+=3) { fprintf(outfile, "%c%g %g %g", (i==0) ? ' ' : ',', pnts[i], pnts[i+1], pnts[i+2]); } fprintf(outfile, "'/>\n"); fprintf(outfile, " </IndexedLineSet>\n"); fprintf(outfile, "</Shape>\n"); }
// draw a 3-D field lines that follow the volume gradient void DrawMolItem::draw_volume_field_lines (int volid, float seedval, float minlen, float maxlen, float thickness) { const VolumetricData * v = NULL; v = mol->get_volume_data (volid); int printdonemesg = 0; if (v == NULL) { msgInfo << "No volume data loaded at index " << volid << sendmsg; return; } int seedcount = 0; int pointcount = 0; int totalpointcount = 0; int usecolor; ResizeArray<float> seeds; append (DMATERIALOFF); usecolor = draw_volume_get_colorid (); cmdColorIndex.putdata (usecolor, cmdList); seedcount = calcseeds_gradient_magnitude (v, &seeds, seedval * 0.5f, seedval * 1.5f); // Integrate field lines starting with each of the seeds to simulate // particle advection. // Uses Euler's approximation for solving the initial value problem. // We could get a more accurate solution using a fourth order Runge-Kutta // method, but with more math per iteration. We may want to implement // the integrator as a user selected option. // The choice of integration step size is currently arbitrary, // but will become a user-defined parameter, since it affects speed // and accuracy. A good default might be 0.25 times the smallest // grid cell spacing axis. float lx, ly, lz; v->cell_lengths (&lx, &ly, &lz); float mincelllen = lx; mincelllen = (mincelllen < ly) ? mincelllen : ly; mincelllen = (mincelllen < lz) ? mincelllen : lz; float delta = mincelllen * 0.25f; // delta per step (compensates gradient magnitude) // minimum gradient magnitude, before we consider that we've found // a critical point in the dataset. float mingmag = 0.0001f; // max gradient magnitude, before we consider it a source/sink float maxgmag = 5; ResizeArray<float> points; // For each seed point, integrate in both positive and // negative directions for a field line length up to // the maxlen criterion. msgtimer *msgt = msg_timer_create (1); int seed; for (seed = 0; seed < seedcount; seed++) { // emit UI messages as integrator runs, for long calculations... if (!(seed & 7) && msg_timer_timeout (msgt)) { char tmpbuf[128]; sprintf (tmpbuf, "%6.2f %% complete", (100.0f * seed) / (float) seedcount); msgInfo << "integrating " << seedcount << " field lines: " << tmpbuf << sendmsg; printdonemesg = 1; } int direction; for (direction = -1; direction != 1; direction = 1) { float pos[3], comsum[3]; vec_copy (pos, &seeds[seed * 3]); // integration starting point is the seed // init the arrays points.clear (); // main integration loop pointcount = 0; totalpointcount++; float len = 0; int iterations = 0; float dir = (float) direction; vec_zero (comsum); // clear center of mass accumulator while ((len < maxlen) && (totalpointcount < 100000000)) { float grad[3]; // sample gradient at the current position v->voxel_gradient_interpolate_from_coord (pos, grad); // Early-exit if we run out of bounds (gradient returned will // be a vector of NANs), run into a critical point (zero gradient) // or a huge gradient at a source/sink point in the dataset. float gmag = norm (grad); if (gmag < mingmag || gmag > maxgmag) break; // Draw the current point only after the gradient value // has been checked, so we don't end up with out-of-bounds // vertices. // Only emit a fraction of integration points for display since // the integrator stepsize needs to be small for more numerical // accuracy, but the field lines themselves can be well // represented with fewer sample points. if (!(iterations & 1)) { // Add a vertex for this field line points.append (pos[0]); points.append (pos[1]); points.append (pos[2]); vec_incr (comsum, pos); pointcount++; totalpointcount++; } // adjust integration stepsize so we never move more than // the distance specified by delta at each step, to compensate // for varying gradient magnitude vec_scaled_add (pos, dir * delta / gmag, grad); // integrate position len += delta; // accumulate distance iterations++; } int drawfieldline = 1; // only draw the field line for this seed if we have enough points. // If we haven't reached the minimum field line length, we'll // drop the whole field line. if (pointcount < 2 || len < minlen) drawfieldline = 0; // only draw if bounding sphere diameter exceeds minlen if (drawfieldline) { float com[3]; vec_scale (com, 1.0f / (float) pointcount, comsum); float minlen2 = minlen * minlen; drawfieldline = 0; int p; for (p = 0; p < pointcount; p++) { if ((2.0f * distance2 (com, &points[p * 3])) > minlen2) { drawfieldline = 1; break; } } } // only draw the field line if it met all selection criteria if (drawfieldline) { cmdLineType.putdata (SOLIDLINE, cmdList); cmdLineWidth.putdata ((int) thickness, cmdList); cmdColorIndex.putdata (usecolor, cmdList); DispCmdPolyLineArray cmdPolyLineArray; cmdPolyLineArray.putdata (&points[0], pointcount, cmdList); } } } msg_timer_destroy (msgt); if (printdonemesg) msgInfo << "field line integration complete." << sendmsg; }