avtOpenGLStreamlineRenderer.C 72.4 KB
Newer Older
1 2
/*****************************************************************************
*
brugger's avatar
 
brugger committed
3
* Copyright (c) 2000 - 2013, Lawrence Livermore National Security, LLC
4
* Produced at the Lawrence Livermore National Laboratory
brugger's avatar
 
brugger committed
5
* LLNL-CODE-442911
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
* All rights reserved.
*
* This file is  part of VisIt. For  details, see https://visit.llnl.gov/.  The
* full copyright notice is contained in the file COPYRIGHT located at the root
* of the VisIt distribution or at http://www.llnl.gov/visit/copyright.html.
*
* Redistribution  and  use  in  source  and  binary  forms,  with  or  without
* modification, are permitted provided that the following conditions are met:
*
*  - Redistributions of  source code must  retain the above  copyright notice,
*    this list of conditions and the disclaimer below.
*  - Redistributions in binary form must reproduce the above copyright notice,
*    this  list of  conditions  and  the  disclaimer (as noted below)  in  the
*    documentation and/or other materials provided with the distribution.
*  - Neither the name of  the LLNS/LLNL nor the names of  its contributors may
*    be used to endorse or promote products derived from this software without
*    specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT  HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR  IMPLIED WARRANTIES, INCLUDING,  BUT NOT  LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND  FITNESS FOR A PARTICULAR  PURPOSE
* ARE  DISCLAIMED. IN  NO EVENT  SHALL LAWRENCE  LIVERMORE NATIONAL  SECURITY,
* LLC, THE  U.S.  DEPARTMENT OF  ENERGY  OR  CONTRIBUTORS BE  LIABLE  FOR  ANY
* DIRECT,  INDIRECT,   INCIDENTAL,   SPECIAL,   EXEMPLARY,  OR   CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT  LIMITED TO, PROCUREMENT OF  SUBSTITUTE GOODS OR
* SERVICES; LOSS OF  USE, DATA, OR PROFITS; OR  BUSINESS INTERRUPTION) HOWEVER
* CAUSED  AND  ON  ANY  THEORY  OF  LIABILITY,  WHETHER  IN  CONTRACT,  STRICT
* LIABILITY, OR TORT  (INCLUDING NEGLIGENCE OR OTHERWISE)  ARISING IN ANY  WAY
* OUT OF THE  USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
*****************************************************************************/

// ************************************************************************* //
40
//                        avtOpenGLStreamlineRenderer.C                      //
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
// ************************************************************************* //

#include "avtOpenGLStreamlineRenderer.h"
#include <avtStreamlinePolyDataFilter.h>
#include <InvalidColortableException.h>
#include <LightList.h>
#include <StreamlineAttributes.h>
#include <avtCallback.h>
#include <avtColorTables.h>
#include <vtkCellArray.h>
#include <vtkCellData.h>
#include <vtkDataArray.h>
#include <vtkDataSet.h>
#include <vtkLookupTable.h>
#include <vtkMath.h>
#include <vtkPointData.h>
#include <vtkPolyData.h>
#include <vtkTubeFilter.h>
#include <vtkRibbonFilter.h>
60
#include <vtkDepthSortPolyData.h>
61 62 63 64 65 66
#include <vtkPolyLine.h>
#include <vtkFloatArray.h>
#include <vtkAppendPolyData.h>
#include <ImproperUseException.h>
#include <LineAttributes.h>
#include <avtLookupTable.h>
bonnell's avatar
bonnell committed
67
#include <avtOpenGLExtensionManager.h>
68
#include <avtGLSLProgram.h>
69 70
#include <vtkCamera.h>
#include <vtkStripper.h>
71
#include <avtVector.h>
pugmire's avatar
pugmire committed
72 73
#include <vtkTriangleFilter.h>
#include <vtkVisItSTLWriter.h>
74

75 76 77
#include <string>
#include <vector>

78 79 80 81
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif

82 83
static int sphereQualityLevels[4][2] = {
    {12,6},
84 85 86 87 88
    {24,12},
    {48,24},
    {96,48}
};

89 90 91 92 93 94 95 96
static int cylinder_quality_levels[4] = {
    17,
    35,
    71,
    143
};


97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
static const char *GLSL_illuminated_lines_vertex_program_source = 
"varying vec3 L, T, V;"
"void main(void)"
"{"
"    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;"
"    L = vec3(gl_LightSource[0].position) - vec3(gl_ModelViewMatrix * gl_Vertex);"
"    T = normalize( gl_NormalMatrix * gl_Normal );"
"    V = vec3(gl_ModelViewMatrix * gl_Vertex);"
"    gl_FrontColor = gl_Color;"
"}";

static const char *GLSL_illuminated_lines_fragment_program_source = 
"varying vec3 L, T, V;"
"void main(void)"
"{"
"    float LT =  dot( normalize(L), normalize(T) );"
"    float VT = -dot( normalize(V), normalize(T) );"
""
"    float kd = sqrt( 1. - LT*LT );"
"    float ks = pow( max( 0., kd * sqrt(1. - VT*VT) ), gl_FrontMaterial.shininess );"
""
"    gl_FragColor = gl_FrontMaterial.ambient  * gl_LightSource[0].ambient +" 
"                   gl_Color                  * gl_LightSource[0].diffuse * kd +" 
"                   gl_FrontMaterial.specular * gl_LightSource[0].specular * ks;"
"    gl_FragColor.a = gl_Color.a;"
"}";
123

124

125 126 127 128 129 130 131 132 133 134 135
// ****************************************************************************
//  Constructor: avtOpenGLStreamlineRenderer::avtOpenGLStreamlineRenderer
//
//  Purpose:
//    Constructor
//
//  Programmer:  Dave Pugmire
//  Creation:    December 29, 2009
//
//  Modifications:
//
136 137 138
//   Dave Pugmire (for Christoph Garth), Wed Jan 20 09:28:59 EST 2010
//   Add illuminated lighting model for lines.
//
139 140 141
//   Dave Pugmire, Tue Feb 16 09:08:32 EST 2010
//   Add display head geom as cone.
//
142 143 144 145 146 147 148 149 150
// ****************************************************************************

avtOpenGLStreamlineRenderer::avtOpenGLStreamlineRenderer()
{
    displaylistid = 0;

    colorTableName = "";
    colorTable.resize(0);
    levelsLUT = NULL;
151 152
    appendForTranspPolys = NULL;
    
153
    for (int i = 0; i < MAX_DETAIL_LEVELS; i++)
154
    {
155
        spherePts[i] = NULL;
156 157
        cylPts[i] = NULL;
    }
158
        
159 160 161 162 163
    shader = new avtGLSLProgram("Illuminated Lines");
    shader->AttachShaderFromString(GL_VERTEX_SHADER,
                                   GLSL_illuminated_lines_vertex_program_source);
    shader->AttachShaderFromString(GL_FRAGMENT_SHADER,
                                   GLSL_illuminated_lines_fragment_program_source);
164 165 166 167 168 169 170 171 172 173 174
}


// ****************************************************************************
//  Destructor: avtOpenGLStreamlineRenderer::~avtOpenGLStreamlineRenderer
//
//  Programmer:  Dave Pugmire
//  Creation:    December 29, 2009
//
//  Modifications:
//
175 176 177
//   Dave Pugmire, Tue Feb 16 09:08:32 EST 2010
//   Add display head geom as cone.
//
178 179 180 181 182 183 184 185 186 187 188
// ****************************************************************************

avtOpenGLStreamlineRenderer::~avtOpenGLStreamlineRenderer()
{
    if (displaylistid != 0)
    {
        glDeleteLists(displaylistid, 1);
        displaylistid = 0;
    }

    for (int i = 0; i < MAX_DETAIL_LEVELS; i++)
189
    {
190 191 192 193 194
        if (spherePts[i])
        {
            delete [] spherePts[i];
            spherePts[i] = NULL;
        }
195 196 197 198 199 200
        if (cylPts[i])
        {
            delete [] cylPts[i];
            cylPts[i] = NULL;
        }
    }
201 202
        
    delete shader;
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257
}



// ****************************************************************************
//  Method:  avtOpenGLStreamlineRenderer::InvalidateColors
//
//  Purpose:
//    
//  Programmer:  Dave Pugmire
//  Creation:    December 29, 2009
//
// ****************************************************************************
void
avtOpenGLStreamlineRenderer::InvalidateColors()
{
    colorTableName = "";
    colorTable.resize(0);

    if (displaylistid != 0)
    {
        glDeleteLists(displaylistid, 1);
        displaylistid = 0;
    }
}

// ****************************************************************************
// Method: avtOpenGLStreamlineRenderer::SetLevelsLUT
//
// Purpose: 
//   Sets a lookup table to be used for resseq coloring.
//
//  Programmer:  Dave Pugmire
//  Creation:    December 29, 2009
//
// Modifications:
//   
// ****************************************************************************

void
avtOpenGLStreamlineRenderer::SetLevelsLUT(avtLookupTable *lut)
{
    levelsLUT = lut;
}

// ****************************************************************************
//  Method:  avtOpenGLStreamlineRenderer::Render
//
//  Purpose:
//    Render one image
//
//  Programmer:  Dave Pugmire
//  Creation:    December 29, 2009
//
//  Modifications:
258 259 260 261
//    
//    Christoph Garth, Tue Jan 20 10:17:39 PST 2010 
//    Removed the display list generation since Mesa 7.5 does not support
//    shader calls inside display lists.
262
//
263 264
//    Dave Pugmire, Fri Feb 12 14:02:57 EST 2010
//    Pass in camera to do transparency sorting.
265
//
266 267 268 269 270 271 272
// ****************************************************************************

void
avtOpenGLStreamlineRenderer::Render(vtkPolyData *data,
                                    const StreamlineAttributes &a,
                                    bool immediateModeRendering,
                                    double vMin, double vMax,
273
                                    vtkCamera *cam,
274 275 276 277 278
                                    float _ambient_coeff,
                                    float _spec_coeff, float _spec_power,
                                    float _spec_r, float _spec_g, float _spec_b,
                                    const int *winsize)
{
279
    camera = cam;
280
#if 0
281 282 283 284 285 286 287 288 289 290 291 292 293 294
    // If nothing changed, and we have display lists, just replay it.
    if (immediateModeRendering &&
        displaylistid != 0 &&
        atts == a &&
        ambient_coeff == _ambient_coeff &&
        spec_coeff == _spec_coeff &&
        spec_power == _spec_power &&
        spec_r == _spec_r &&
        spec_g == _spec_g &&
        spec_b == _spec_b)
    {
        glCallList(displaylistid);
        return;
    }
295
#endif
296 297 298 299

    //Otherwise, we need to regenerate.
    atts = a;

300
#if 0
301 302 303 304 305 306 307 308 309
    //Make a new display list, if needed.
    if (immediateModeRendering)
    {
        if (displaylistid != 0)
            glDeleteLists(displaylistid, 1);
        
        displaylistid = glGenLists(1);
        glNewList(displaylistid, GL_COMPILE);
    }
310
#endif
311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366

    varMin = vMin;
    varMax = vMax;
    varDiff = varMax-varMin;

    ambient_coeff = _ambient_coeff;
    spec_coeff    = _spec_coeff;
    spec_power    = _spec_power;
    spec_r        = _spec_r;
    spec_g        = _spec_g;
    spec_b        = _spec_b;

    glPushAttrib(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_ENABLE_BIT);
    if (!atts.GetLightingFlag())
        ambient_coeff = 1.0;

    float diff[] = {1,1,1,1};
    glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diff);
    if (ambient_coeff == 0)
    {
        float amb[] = {ambient_coeff, ambient_coeff, ambient_coeff, 1};
        glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, amb);
        glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE);
    }
    else
    {
        float amb[] = {ambient_coeff, ambient_coeff, ambient_coeff, 1};
        glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, amb);
        glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
    }
    glEnable(GL_COLOR_MATERIAL);
    float spec[] = {spec_r * spec_coeff,
                    spec_g * spec_coeff,
                    spec_b * spec_coeff,
                    1};
    glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, spec);
    glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, &spec_power);
    glShadeModel(GL_SMOOTH);
    
    glEnable(GL_BLEND);
    glEnable(GL_ALPHA_TEST);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    TRY
    {
        InitColors();
        DrawStreamlines(data);
    }
    CATCH2(VisItException, e)
    {
        avtCallback::IssueWarning(e.Message().c_str());
    }
    ENDTRY

    glPopAttrib();

367
#if 0
368 369 370 371 372
    if (immediateModeRendering)
    {
        glEndList();
        glCallList(displaylistid);
    }
373
#endif
374 375 376
}

// ****************************************************************************
377
//  Method:  avtOpenGLStreamlineRenderer::DrawStreamlines
378 379 380 381 382 383 384 385 386
//
//  Purpose:
//    Call the appropriate rendering method, then display seeds if needed.
//
//  Programmer:  Dave Pugmire
//  Creation:    December 29, 2009
//
//  Modifications:
//
387 388 389
//   Dave Pugmire, Wed Jan 20 09:28:59 EST 2010
//   Add drawHead geom.
//
390 391 392 393 394
//   Dave Pugmire, Fri Feb 12 14:02:57 EST 2010
//   Support for transparency sorting.
//
//   Hank Childs, Wed Sep 29 19:12:39 PDT 2010
//   Rename None to FullyOpaque.
395
//
396 397 398 399 400
// ****************************************************************************

void
avtOpenGLStreamlineRenderer::DrawStreamlines(vtkPolyData *data)
{
401
    if (atts.GetOpacityType() != StreamlineAttributes::FullyOpaque)
402
        appendForTranspPolys = vtkAppendPolyData::New();
pugmire's avatar
pugmire committed
403 404
    if (atts.GetOpacityType() == StreamlineAttributes::VariableRange)
        InitVarOpacity(data);
405 406 407 408 409
    
    if (atts.GetShowSeeds())
        DrawSeedPoints(data);
    if (atts.GetShowHeads())
        DrawHeadGeom(data);
410 411 412 413 414 415 416
    if (atts.GetDisplayMethod() == StreamlineAttributes::Lines)
        DrawAsLines(data);
    else if (atts.GetDisplayMethod() == StreamlineAttributes::Tubes)
        DrawAsTubes(data);
    else if (atts.GetDisplayMethod() == StreamlineAttributes::Ribbons)
        DrawAsRibbons(data);

417 418 419 420 421 422 423
    if (appendForTranspPolys)
    {
        appendForTranspPolys->Update();
        DrawPolyData(appendForTranspPolys->GetOutput());
        appendForTranspPolys->Delete();
        appendForTranspPolys = NULL;
    }
424 425 426 427 428 429 430 431 432 433 434 435 436
}

// ****************************************************************************
//  Method:  avtOpenGLStreamlineRenderer::DrawAsLines
//
//  Purpose:
//    Display streamlines as lines.
//
//  Programmer:  Dave Pugmire
//  Creation:    December 29, 2009
//
//  Modifications:
//
437
//   Dave Pugmire (for Christoph Garth), Wed Jan 20 09:28:59 EST 2010
pugmire's avatar
pugmire committed
438 439 440 441
//   Illuminated lighting model for lines.
//
//   Dave Pugmire, Thu Mar 25 16:34:23 EDT 2010
//   Fixed indexing problem.
442
//
443 444 445 446
//   Hank Childs, Sun Oct 31 13:04:54 PST 2010
//   Add support for the end points being outside the range for a given
//   streamline.
//
447 448 449
//   Hank Childs, Mon Nov  8 19:54:09 PST 2010
//   Fix some indexing errors with previous change.
//
450 451 452 453 454
// ****************************************************************************

void
avtOpenGLStreamlineRenderer::DrawAsLines(vtkPolyData *data)
{
455 456
    bool illuminated = atts.GetLightingFlag() && shader->Enable();

457 458 459 460 461 462 463 464
    //Turn off lighting for lines.
    glDisable(GL_LIGHTING);
    glLineWidth(Int2LineWidth(atts.GetLineWidth()));
    
    vtkPoints *points = data->GetPoints();
    vtkCellArray *lines = data->GetLines();
    vtkIdType *segments = lines->GetPointer();
    float *scalar = (float *)data->GetPointData()->GetArray(avtStreamlinePolyDataFilter::colorvarArrayName.c_str())->GetVoidPointer(0);
465
    float *param = (float *)data->GetPointData()->GetArray(avtStreamlinePolyDataFilter::paramArrayName.c_str())->GetVoidPointer(0);
466 467 468
    float *opacity = NULL;
    if (data->GetPointData()->GetArray(avtStreamlinePolyDataFilter::opacityArrayName.c_str()))
        opacity = (float *)data->GetPointData()->GetArray(avtStreamlinePolyDataFilter::opacityArrayName.c_str())->GetVoidPointer(0);
469 470 471
    float *tangents = NULL;
    if (illuminated && data->GetPointData()->GetArray(avtStreamlinePolyDataFilter::tangentsArrayName.c_str()))
        tangents = (float *)data->GetPointData()->GetArray(avtStreamlinePolyDataFilter::tangentsArrayName.c_str())->GetVoidPointer(0);
472

473
    vtkIdType *segptr = segments;
474 475 476 477 478
    double pt[3];

    for (int i=0; i<data->GetNumberOfLines(); i++)
    {
        int nPts = *segptr;
479
        int idx0 = 0, idx1 = nPts-1;
480 481 482 483 484

        segptr++; //Now segptr points at vtx0.
        
        double t0=0.0, t1=0.0;
        GetEndPoints(data, segptr, nPts, idx0, idx1, t0, t1);
485 486 487 488 489 490 491
        if (idx0 > nPts || idx1 < 0)
        {
            // The display range doesn't overlap with the current streamline,
            // so do some bookkeeping and return.
            segptr += nPts;
            continue;
        }
492 493 494

        //cout<<"   Draw: "<<idx0<<" to "<<idx1<<" ["<<t0<<" "<<t1<<"]"<<endl;

495
        float o = 1.0, v[3];
496 497 498
        glBegin(GL_LINE_STRIP);

        //If we have an interpolated start point, calculate it.
499
        if (idx0 > 0 && idx0 < nPts)
500 501 502 503 504 505 506 507 508 509 510 511 512 513
        {
            double prev[3];
            points->GetPoint(segptr[idx0-1], prev);
            points->GetPoint(segptr[idx0], pt);
            
            float p[3];
            p[0] = prev[0] + t0*(pt[0]-prev[0]);
            p[1] = prev[1] + t0*(pt[1]-prev[1]);
            p[2] = prev[2] + t0*(pt[2]-prev[2]);
            
            float  s0, s1, s, o;
            s0 = scalar[segptr[idx0-1]];
            s1 = scalar[segptr[idx0]];
            s = s0 + t0*(s1-s0);
514 515 516 517
            
            if (atts.GetOpacityType() == StreamlineAttributes::Ramp)
                o = 0.0;
            else if (opacity)
518 519 520 521 522 523 524
            {
                s0 = scalar[segptr[idx0-1]];
                s1 = scalar[segptr[idx0]];
                o = s0 + t0*(s1-s0);
            }
            SetColor(s, o);

525 526 527 528 529 530 531 532 533
            if (tangents)
            {
                float* v0 = tangents + segptr[idx0-1];
                float* v1 = tangents + segptr[idx0];

                v[0] = v0[0] + t0*(v1[0]-v0[0]);
                v[1] = v0[1] + t0*(v1[1]-v0[1]);
                v[2] = v0[2] + t0*(v1[2]-v0[2]);

534
                glNormal3fv(v);
535 536
            }

537 538 539
            glVertex3fv(p);
        }
        
540
        for (int j = idx0; j <= idx1; j++)
541 542 543 544 545
        {
            points->GetPoint(segptr[j], pt);
            float p[3] = {pt[0], pt[1], pt[2]};
            
            float s = scalar[segptr[j]];
546 547 548 549 550
            if (atts.GetOpacityType() == StreamlineAttributes::Ramp)
            {
                o = ComputeRampOpacity(param[segptr[j]]);
            }
            else if (opacity)
551 552 553 554
            {
                o = opacity[segptr[j]];
            }
            SetColor(s, o);
555 556
            if (tangents)
                glNormal3fv(tangents+3*segptr[j]);
557 558 559 560
            glVertex3fv(p);
        }

        //If we have an interpolated end point, calculate it.
561
        if (idx1 < nPts-1 && idx1 > 0)
562 563
        {
            double next[3];
564 565
            points->GetPoint(segptr[idx1], pt);
            points->GetPoint(segptr[idx1+1], next);
566 567 568 569 570 571 572
            
            float p[3];
            p[0] = pt[0] + t1*(next[0]-pt[0]);
            p[1] = pt[1] + t1*(next[1]-pt[1]);
            p[2] = pt[2] + t1*(next[2]-pt[2]);

            float  s0, s1, s, o;
573 574
            s0 = scalar[segptr[idx1]];
            s1 = scalar[segptr[idx1+1]];
575
            s = s0 + t1*(s1-s0);
576 577 578 579
            
            if (atts.GetOpacityType() == StreamlineAttributes::Ramp)
                o = 1.0;
            else if (opacity)
580
            {
581 582
                s0 = scalar[segptr[idx1]];
                s1 = scalar[segptr[idx1+1]];
583 584 585 586 587
                o = s0 + t1*(s1-s0);
            }
            
            SetColor(s, o);
            
588 589
            if (tangents)
            {
590 591
                float* v0 = tangents + segptr[idx1];
                float* v1 = tangents + segptr[idx1+1];
592 593 594 595 596
            
                v[0] = v0[0] + t1*(v1[0]-v0[0]);
                v[1] = v0[1] + t1*(v1[1]-v0[1]);
                v[2] = v0[2] + t1*(v1[2]-v0[2]);
                
597
                glNormal3fv(v);
598 599
            }
            
600 601 602 603 604 605 606 607 608
            glVertex3fv(p);
        }

        glEnd();

        segptr += nPts;
    }

    glEnable(GL_LIGHTING);
609

610
    if (illuminated)
611
        shader->Disable();
612 613
}

614

615 616 617 618 619 620 621 622 623 624 625 626
// ****************************************************************************
//  Method:  avtOpenGLStreamlineRenderer::DrawAsTubes
//
//  Purpose:
//    Display each streamline as a tube. Run a vtkTubeFilter on each streamline,
//    then render the polygons.
//
//  Programmer:  Dave Pugmire
//  Creation:    December 29, 2009
//
//  Modifications:
//
627 628 629
//   Dave Pugmire, Wed Jan 20 09:28:59 EST 2010
//   Changed some attribute names.
//
630 631 632 633 634
//   Dave Pugmire, Fri Feb 12 14:02:57 EST 2010
//   Support for transparency sorting.
//
//   Hank Childs, Thu Sep 30 01:11:03 PDT 2010
//   Add an option for sizing based on a fraction of the bounding box.
635
//
636 637 638 639
//   Hank Childs, Sun Oct 31 13:04:54 PST 2010
//   Add support for the end points being outside the range for a given
//   streamline.
//
640 641 642
//   Dave Pugmire, Fri Jan 28 14:49:50 EST 2011
//   Add vary tube radius by variable.
//
643 644 645 646 647 648
// ****************************************************************************

void
avtOpenGLStreamlineRenderer::DrawAsTubes(vtkPolyData *data)
{
    vtkTubeFilter *tube = vtkTubeFilter::New();
649 650 651 652
    double tubeRadius = atts.GetTubeRadiusAbsolute();
    if (atts.GetTubeSizeType() == StreamlineAttributes::FractionOfBBox)
        tubeRadius = atts.GetTubeRadiusBBox() * GetBBoxSize();
    tube->SetRadius(tubeRadius);
653 654 655 656

    tube->SetNumberOfSides(atts.GetTubeDisplayDensity());
    tube->SetCapping(1);
    tube->ReleaseDataFlagOn();
657 658 659 660 661 662 663 664 665
    
    vtkDataArray *activeScalars = data->GetPointData()->GetScalars();
    if (atts.GetVaryTubeRadius() != StreamlineAttributes::None &&
        data->GetPointData()->GetArray(avtStreamlinePolyDataFilter::scaleRadiusArrayName.c_str()))
    {
        data->GetPointData()->SetActiveScalars(avtStreamlinePolyDataFilter::scaleRadiusArrayName.c_str());
        tube->SetVaryRadiusToVaryRadiusByScalar();
        tube->SetRadiusFactor(atts.GetVaryTubeRadiusFactor());
    }
666 667

    //Easy case, make tubes and we're done.
668
    if (!atts.GetDisplayBeginFlag() && !atts.GetDisplayEndFlag())
bonnell's avatar
bonnell committed
669
        tube->SetInputData(data);
670 671 672 673 674 675 676 677 678
    else
    {
        // If we need to trim either end, create a new trimmed polyline
        // and run the tube on this geometry.
        
        vtkPoints *points = data->GetPoints();
        vtkCellArray *lines = data->GetLines();
        vtkIdType *segments = lines->GetPointer();
    
679
        vtkIdType *segptr = segments;
680 681 682 683 684 685
        vtkAppendPolyData *append = vtkAppendPolyData::New();
        
        for (int i=0; i<data->GetNumberOfLines(); i++)
        {
            vtkPolyData *pd = MakeNewPolyline(data, segptr);

686 687
            if (pd != NULL)
            {
bonnell's avatar
bonnell committed
688
                append->AddInputData(pd);
689 690
                pd->Delete();
            }
691
        }
bonnell's avatar
bonnell committed
692 693 694
        //VTK-6.0 FIX ME -- ksb, is this update necessary?
        //append->Update();
        tube->SetInputConnection(append->GetOutputPort());
695 696 697 698 699
        append->Delete();
    }
    
    //Create the tube polydata, and draw.
    tube->Update();
700 701 702
    
    if (activeScalars)
        data->GetPointData()->SetActiveScalars(activeScalars->GetName());
703 704

    if (appendForTranspPolys)
bonnell's avatar
bonnell committed
705
        appendForTranspPolys->AddInputConnection(tube->GetOutputPort());
706 707 708
    else
        DrawPolyData(tube->GetOutput());
    
709 710 711 712 713 714 715 716 717 718 719 720 721 722 723
    tube->Delete();
}

// ****************************************************************************
//  Method:  avtOpenGLStreamlineRenderer::DrawAsRibbons
//
//  Purpose:
//    Display each streamline as a ribbon. Run a vtkRibbonFilter on each streamline,
//    then render the polygons.
//
//  Programmer:  Dave Pugmire
//  Creation:    December 29, 2009
//
//  Modifications:
//
724 725 726
//   Dave Pugmire, Wed Jan 20 09:28:59 EST 2010
//   Changed some attribute names.
//
727 728 729 730 731
//   Dave Pugmire, Fri Feb 12 14:02:57 EST 2010
//   Support for transparency sorting.
//
//   Hank Childs, Thu Sep 30 01:11:03 PDT 2010
//   Add an option for sizing based on a fraction of the bounding box.
732
//
733 734 735 736
//   Hank Childs, Sun Oct 31 13:04:54 PST 2010
//   Add support for the end points being outside the range for a given
//   streamline.
//
737 738 739 740 741 742 743 744 745 746 747 748 749 750
// ****************************************************************************

void
avtOpenGLStreamlineRenderer::DrawAsRibbons(vtkPolyData *data)
{
    vtkPoints *points = data->GetPoints();
    vtkCellArray *lines = data->GetLines();
    vtkIdType *segments = lines->GetPointer();
    float *t = NULL;
    if (data->GetPointData()->GetArray(avtStreamlinePolyDataFilter::thetaArrayName.c_str()))
        t = (float *)data->GetPointData()->GetArray(avtStreamlinePolyDataFilter::thetaArrayName.c_str())->GetVoidPointer(0);
    else
        EXCEPTION1(ImproperUseException, "Expected a vorticity values for ribbon display.");
    
751
    vtkIdType *segptr = segments;
752 753 754 755 756

    //Create new polylines and add the normals.
    for (int i=0; i<data->GetNumberOfLines(); i++)
    {
        vtkPolyData *pd = MakeNewPolyline(data, segptr);
757 758
        if (pd == NULL)
            continue;
759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803
        int nPts = pd->GetPointData()->GetNumberOfTuples();

        vtkIdList *ids = vtkIdList::New();
        vtkPoints *pts = vtkPoints::New();
        vtkCellArray *lines = vtkCellArray::New();
        for (int i = 0; i < nPts; i++)
        {
            vtkIdType id = pts->InsertNextPoint(pd->GetPoints()->GetPoint(i));
            ids->InsertNextId(id);
        }

        lines->InsertNextCell(ids);
        vtkFloatArray *normals = vtkFloatArray::New();
        normals->SetNumberOfComponents(3);
        normals->SetNumberOfTuples(nPts);

        vtkPolyLine *lineNormalGenerator = vtkPolyLine::New();
        lineNormalGenerator->GenerateSlidingNormals(pts, lines, normals);
        
        //Now, rotate the normals according to the vorticity..
        //double normal[3], local1[3], local2[3],length,costheta, sintheta;
        double normal[3], tan[3], biNormal[3], p0[3], p1[3];
        for (int i = 0; i < nPts; i++)
        {
            double theta = t[i];
            pts->GetPoint(i, p0);
            if (i < nPts-1)
                pts->GetPoint(i+1, p1);
            else
            {
                pts->GetPoint(i-1, p0);
                pts->GetPoint(i, p1);
            }
            for (int j = 0; j < 3; j++)
                tan[j] = p1[j]-p0[j];
            
            normals->GetTuple(i, normal);
            vtkMath::Normalize(tan);
            vtkMath::Normalize(normal);
            
            vtkMath::Cross(normal, tan, biNormal);
            double cosTheta = cos(theta);
            double sinTheta = sin(theta);
            for (int j = 0; j < 3; j++)
                normal[j] = cosTheta*normal[j] + sinTheta*biNormal[j];
hrchilds's avatar
hrchilds committed
804 805 806
            normal[0] = 1;
            normal[1] = 0;
            normal[2] = 0;
807 808 809 810 811 812 813 814 815 816 817 818
            normals->SetTuple(i,normal);
        }
        
        ids->Delete();
        pts->Delete();
        lines->Delete();
        
        pd->GetPointData()->SetNormals(normals);
        normals->Delete();
        lineNormalGenerator->Delete();
        
        vtkRibbonFilter *ribbons = vtkRibbonFilter::New();
819 820 821 822
        double ribbonWidth = atts.GetRibbonWidthAbsolute();
        if (atts.GetRibbonWidthSizeType() == StreamlineAttributes::FractionOfBBox)
            ribbonWidth = atts.GetRibbonWidthBBox() * GetBBoxSize();
        ribbons->SetWidth(ribbonWidth);
bonnell's avatar
bonnell committed
823
        ribbons->SetInputData(pd);
824 825
        ribbons->Update();
        
826
        if (appendForTranspPolys)
bonnell's avatar
bonnell committed
827
            appendForTranspPolys->AddInputConnection(ribbons->GetOutputPort());
828 829 830
        else
            DrawPolyData(ribbons->GetOutput());
        
831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847
        pd->Delete();
        ribbons->Delete();
    }
}


// ****************************************************************************
//  Method:  avtOpenGLStreamlineRenderer::DrawSeedPoints
//
//  Purpose:
//    Draw each seed point as a sphere.
//
//  Programmer:  Dave Pugmire
//  Creation:    December 29, 2009
//
//  Modifications:
//
848 849 850
//   Dave Pugmire, Wed Jan 20 09:28:59 EST 2010
//   Changed some attribute names.
//
851 852 853 854 855
//   Dave Pugmire, Fri Feb 12 14:02:57 EST 2010
//   Support for transparency sorting.
//
//   Hank Childs, Thu Sep 30 01:11:03 PDT 2010
//   Add an option for sizing based on a fraction of the bounding box.
856
//
857 858 859 860 861 862
// ****************************************************************************

void
avtOpenGLStreamlineRenderer::DrawSeedPoints(vtkPolyData *data)
{
    CalculateSpherePts();
863 864 865
    double rad = atts.GetSeedRadiusAbsolute();
    if (atts.GetSeedRadiusSizeType() == StreamlineAttributes::FractionOfBBox)
        rad = atts.GetSeedRadiusBBox() * GetBBoxSize();
866
    int quality = (int)(atts.GetGeomDisplayQuality());
867 868 869 870 871 872 873 874 875

    vtkPoints *points = data->GetPoints();
    vtkCellArray *lines = data->GetLines();
    vtkIdType *segments = lines->GetPointer();
    float *s = (float *)data->GetPointData()->GetArray(avtStreamlinePolyDataFilter::colorvarArrayName.c_str())->GetVoidPointer(0);
    float *o = NULL;
    if (data->GetPointData()->GetArray(avtStreamlinePolyDataFilter::opacityArrayName.c_str()))
        o = (float *)data->GetPointData()->GetArray(avtStreamlinePolyDataFilter::opacityArrayName.c_str())->GetVoidPointer(0);
    
876
    vtkIdType *segptr = segments;
877 878 879 880 881 882 883 884
    double pt[3];
    
    for (int i=0; i<data->GetNumberOfLines(); i++)
    {
        int nPts = *segptr;
        segptr++; //Now segptr points at vtx0.

        points->GetPoint(segptr[0], pt);
885 886 887 888

        if (appendForTranspPolys)
        {
            vtkPolyData *pd = GenerateSpherePolys(pt[0], pt[1], pt[2], rad, quality,
889
                                                  s[*segptr], 0.0);
bonnell's avatar
bonnell committed
890
            appendForTranspPolys->AddInputData(pd);
891 892 893 894 895 896
            pd->Delete();
        }
        else
        {
            glBegin(GL_QUADS);
            SetColor(s[*segptr], (o?o[*segptr]:1.0));
897
            DrawSphere(pt[0],pt[1], pt[2], rad, quality);
898 899
            glEnd();
        }
900 901 902
        segptr += nPts;
    }
}
903 904 905 906 907 908 909 910 911 912 913
// ****************************************************************************
//  Method:  avtOpenGLStreamlineRenderer::DrawHeadGeom
//
//  Purpose:
//    Draw each seed point as a sphere.
//
//  Programmer:  Dave Pugmire
//  Creation:    January 20, 2010
//
//  Modifications:
//
914 915
//   Dave Pugmire, Fri Feb 12 14:02:57 EST 2010
//   Support for transparency sorting.
916
//
917 918 919
//   Dave Pugmire, Tue Feb 16 09:08:32 EST 2010
//   Add display head geom as cone.
//
pugmire's avatar
pugmire committed
920 921 922
//   Dave Pugmire, Thu Mar 25 16:34:23 EDT 2010
//   Fixed indexing problem.
//
923 924 925
//   Hank Childs, Thu Sep 30 01:11:03 PDT 2010
//   Add an option for sizing based on a fraction of the bounding box.
//
926 927 928
//   Hank Childs, Fri Oct  8 23:30:27 PDT 2010
//   Allow for display begin/end to be from distance/time/steps.
//
929 930 931 932
//   Hank Childs, Sun Oct 31 13:04:54 PST 2010
//   Add support for the end points being outside the range for a given
//   streamline.  Also re-arrange indexing.
//
933 934 935 936 937 938
// ****************************************************************************

void
avtOpenGLStreamlineRenderer::DrawHeadGeom(vtkPolyData *data)
{
    CalculateSpherePts();
939 940 941 942
    double rad = atts.GetHeadRadiusAbsolute();
    if (atts.GetHeadRadiusSizeType() == StreamlineAttributes::FractionOfBBox)
        rad = atts.GetHeadRadiusBBox() * GetBBoxSize();
    double height = rad*atts.GetHeadHeightRatio();
943 944 945 946 947 948 949 950 951 952
    int quality = (int)(atts.GetGeomDisplayQuality());

    vtkPoints *points = data->GetPoints();
    vtkCellArray *lines = data->GetLines();
    vtkIdType *segments = lines->GetPointer();
    float *s = (float *)data->GetPointData()->GetArray(avtStreamlinePolyDataFilter::colorvarArrayName.c_str())->GetVoidPointer(0);
    float *o = NULL;
    if (data->GetPointData()->GetArray(avtStreamlinePolyDataFilter::opacityArrayName.c_str()))
        o = (float *)data->GetPointData()->GetArray(avtStreamlinePolyDataFilter::opacityArrayName.c_str())->GetVoidPointer(0);
    
953
    vtkIdType *segptr = segments;
954 955
    double endPt[3], endPtPrev[3];
    float scalar, opacity=1.0;
956 957 958 959 960 961

    for (int i=0; i<data->GetNumberOfLines(); i++)
    {
        int nPts = *segptr;
        segptr++; //Now segptr points at vtx0.

962
        int idx0 = 0, idx1 = nPts-1;
963 964
        double t0=0.0, t1=0.0;
        GetEndPoints(data, segptr, nPts, idx0, idx1, t0, t1);
965 966 967 968 969 970 971
        if (idx0 > nPts || idx1 < 0)
        {
            // The display range doesn't overlap with the current streamline,
            // so do some bookkeeping and return.
            segptr += nPts;
            continue;
        }
allens's avatar
allens committed
972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991

        // Just incase everything is cropped away
        if (idx1 == 0 )
        {
            double pt[3], next[3];

            points->GetPoint(segptr[0], endPt);
            points->GetPoint(segptr[1], next);

            // For cones fake a previous point using the next point.
            endPtPrev[0] = endPt[0] - .1*(next[0]-endPt[0]);
            endPtPrev[1] = endPt[1] - .1*(next[1]-endPt[1]);
            endPtPrev[2] = endPt[2] - .1*(next[2]-endPt[2]);

            scalar = s[*(segptr)];
            if (o)
              opacity = o[*(segptr)];
        }
        // If we have an interpolated end point, calculate it.
        else if (0 < idx1 && idx1 < nPts-1)
992
        {
allens's avatar
allens committed
993
            double pt[3], next[3];
994
            points->GetPoint(segptr[idx1-1], endPtPrev);
allens's avatar
allens committed
995 996
            points->GetPoint(segptr[idx1], pt);
            points->GetPoint(segptr[idx1+1], next);
997
            
allens's avatar
allens committed
998 999 1000 1001
            endPt[0] = pt[0] + t1*(next[0]-pt[0]);
            endPt[1] = pt[1] + t1*(next[1]-pt[1]);
            endPt[2] = pt[2] + t1*(next[2]-pt[2]);

1002
            float  s0, s1;
allens's avatar
allens committed
1003 1004
            s0 = s[segptr[idx1]];
            s1 = s[segptr[idx1+1]];
1005
            scalar = s0 + t1*(s1-s0);
allens's avatar
allens committed
1006 1007 1008 1009
            
            if (atts.GetOpacityType() == StreamlineAttributes::Ramp)
                opacity = 1.0;
            else if (o)
1010
            {
allens's avatar
allens committed
1011 1012
                s0 = o[segptr[idx1]];
                s1 = o[segptr[idx1+1]];
1013 1014
                opacity = s0 + t1*(s1-s0);
            }
1015
        }
allens's avatar
allens committed
1016
        // No points are cropped.
1017 1018 1019
        else
        {
            points->GetPoint(segptr[nPts-1], endPt);
1020
            points->GetPoint(segptr[nPts-2], endPtPrev);
1021
            scalar = s[*(segptr+nPts-1)];
1022 1023
            if (o)
                opacity = o[*(segptr+nPts-1)];
1024 1025
        }
        
1026 1027
        if (appendForTranspPolys)
        {
1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042
            float param = 1000000;
            switch (atts.GetReferenceTypeForDisplay())
            {
               case StreamlineAttributes::Distance:
                 if (atts.GetTerminateByDistance())
                     param = atts.GetTermDistance();
                 break;
               case StreamlineAttributes::Time:
                 if (atts.GetTerminateByTime())
                     param = atts.GetTermTime();
                 break;
               case StreamlineAttributes::Step:
                 param = atts.GetMaxSteps();
                 break;
            }
1043 1044
            if (atts.GetDisplayEndFlag())
                param = atts.GetDisplayEnd();
1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059
            vtkPolyData *pd = NULL;
            if (atts.GetHeadDisplayType() == StreamlineAttributes::Sphere)
                pd = GenerateSpherePolys(endPt[0], endPt[1], endPt[2], rad, quality,
                                         scalar, param);
            else if (atts.GetHeadDisplayType() == StreamlineAttributes::Cone)
            {
                float dir[3] = {endPt[0]-endPtPrev[0], endPt[1]-endPtPrev[1], endPt[2]-endPtPrev[2]};
                pd = GenerateConePolys(endPt[0], endPt[1], endPt[2],
                                                    dir[0], dir[1], dir[2],
                                                    rad, height, quality,
                                                    scalar, param);
            }
            
            if (pd)
            {
bonnell's avatar
bonnell committed
1060
                appendForTranspPolys->AddInputData(pd);
1061 1062
                pd->Delete();
            }
1063 1064 1065 1066
        }
        else
        {
            SetColor(scalar, (o?o[*segptr]:1.0));
1067 1068 1069 1070 1071 1072 1073
            if (atts.GetHeadDisplayType() == StreamlineAttributes::Sphere)
                DrawSphere(endPt[0],endPt[1], endPt[2], rad, quality);
            else if (atts.GetHeadDisplayType() == StreamlineAttributes::Cone)
            {
                float dir[3] = {endPt[0]-endPtPrev[0], endPt[1]-endPtPrev[1], endPt[2]-endPtPrev[2]};
                DrawCone(endPt[0],endPt[1],endPt[2], dir[0],dir[1],dir[2], rad, height, quality);
            }
1074
        }
1075 1076 1077
        segptr += nPts;
    }
}
1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090

// ****************************************************************************
//  Method:  avtOpenGLStreamlineRenderer::MakeNewPolyline
//
//  Purpose:
//    Helper function that creates a new polyline, trimmed at both ends as needed
//    and all arrays attached.
//
//  Programmer:  Dave Pugmire
//  Creation:    December 29, 2009
//
//  Modifications:
//
1091 1092 1093
//   Dave Pugmire, Wed Jan 20 09:28:59 EST 2010
//   Copy over the param array.
//
pugmire's avatar
pugmire committed
1094 1095 1096
//   Dave Pugmire, Thu Mar 25 16:34:23 EDT 2010
//   Fixed indexing problem.
//
1097 1098 1099 1100 1101
//   Hank Childs, Sun Oct 31 13:04:54 PST 2010
//   Add support for the end points being outside the range for a given
//   streamline.  Also fix indexing problem (include last step) and make sure
//   that we don't have points so close together that we can't tube.
//
1102 1103 1104
//   Hank Childs, Mon Nov  8 19:54:09 PST 2010
//   Fix some indexing errors with previous change.
//
1105 1106 1107 1108
// ****************************************************************************

vtkPolyData *
avtOpenGLStreamlineRenderer::MakeNewPolyline(vtkPolyData *data,
1109
                                             vtkIdType *&segptr)
1110 1111 1112
{
    vtkPoints *points = data->GetPoints();
    float *s = (float *)data->GetPointData()->GetArray(avtStreamlinePolyDataFilter::colorvarArrayName.c_str())->GetVoidPointer(0);
1113
    float *p = (float *)data->GetPointData()->GetArray(avtStreamlinePolyDataFilter::paramArrayName.c_str())->GetVoidPointer(0);
1114 1115 1116 1117 1118 1119 1120
    float *t = NULL, *o = NULL;
    if (data->GetPointData()->GetArray(avtStreamlinePolyDataFilter::thetaArrayName.c_str()))
        t = (float *)data->GetPointData()->GetArray(avtStreamlinePolyDataFilter::thetaArrayName.c_str())->GetVoidPointer(0);
    if (data->GetPointData()->GetArray(avtStreamlinePolyDataFilter::opacityArrayName.c_str()))
        o = (float *)data->GetPointData()->GetArray(avtStreamlinePolyDataFilter::opacityArrayName.c_str())->GetVoidPointer(0);
    
    int nPts = *segptr;
1121
    segptr++; //Now segptr points at vertex0.
1122
    
1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133
    double t0, t1, pt[3];
    int idx0 = 0, idx1 = nPts-1;
    GetEndPoints(data, segptr, nPts, idx0, idx1, t0, t1);
    if (idx0 > nPts || idx1 < 0)
    {
        // The display range doesn't overlap with the current streamline,
        // so do some bookkeeping and return.
        segptr += nPts;
        return NULL;
    }

1134 1135 1136 1137
    vtkPoints *pts = vtkPoints::New();
    vtkCellArray *cells = vtkCellArray::New();
    vtkFloatArray *scalars = vtkFloatArray::New();
    scalars->SetName(avtStreamlinePolyDataFilter::colorvarArrayName.c_str());
1138 1139
    vtkFloatArray *params = vtkFloatArray::New();
    params->SetName(avtStreamlinePolyDataFilter::paramArrayName.c_str());
1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153

    vtkFloatArray *thetas = NULL;
    if (t)
    {
        thetas = vtkFloatArray::New();
        thetas->SetName(avtStreamlinePolyDataFilter::thetaArrayName.c_str());
    }
    vtkFloatArray *opacity = NULL;
    if (o)
    {
        opacity = vtkFloatArray::New();
        opacity->SetName(avtStreamlinePolyDataFilter::opacityArrayName.c_str());
    }

1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185
    int nNewPts = (idx1-idx0)+1;
    bool makeStartPoint = false;
    bool makeEndPoint = false;
    
    // Check to see if we need to (1) split the initial segment and (2) if
    // the resulting segment is big enough to not hose the tube filter.
    if (idx0 > 0)
    {
        double pt[3];
        double prev[3];
        points->GetPoint(segptr[idx0], pt);
        points->GetPoint(segptr[idx0-1], prev);
        
        double pi[3];
        pi[0] = prev[0] + t0*(pt[0]-prev[0]);
        pi[1] = prev[1] + t0*(pt[1]-prev[1]);
        pi[2] = prev[2] + t0*(pt[2]-prev[2]);
        double dist = sqrt((pi[0]-pt[0])*(pi[0]-pt[0])+
                           (pi[1]-pt[1])*(pi[1]-pt[1])+
                           (pi[2]-pt[2])*(pi[2]-pt[2]));
        makeStartPoint = true;
        double tubeRadius = atts.GetTubeRadiusAbsolute();
        if (atts.GetTubeSizeType() == StreamlineAttributes::FractionOfBBox)
            tubeRadius = atts.GetTubeRadiusBBox() * GetBBoxSize();
        if (atts.GetDisplayMethod() == StreamlineAttributes::Tubes &&
            dist < 0.1*tubeRadius)
            makeStartPoint = false;
        if (makeStartPoint)
            nNewPts++;
    }
    // Check to see if we need to (1) split the final segment and (2) if
    // the resulting segment is big enough to not hose the tube filter.
1186
    if (idx1 < nPts-1)
1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209
    {
        double pt[3];
        double next[3];
        points->GetPoint(segptr[idx1], pt);
        points->GetPoint(segptr[idx1+1], next);
        
        double pi[3];
        pi[0] = pt[0] + t1*(next[0]-pt[0]);
        pi[1] = pt[1] + t1*(next[1]-pt[1]);
        pi[2] = pt[2] + t1*(next[2]-pt[2]);
        double dist = sqrt((pi[0]-pt[0])*(pi[0]-pt[0])+
                           (pi[1]-pt[1])*(pi[1]-pt[1])+
                           (pi[2]-pt[2])*(pi[2]-pt[2]));
        makeEndPoint = true;
        double tubeRadius = atts.GetTubeRadiusAbsolute();
        if (atts.GetTubeSizeType() == StreamlineAttributes::FractionOfBBox)
            tubeRadius = atts.GetTubeRadiusBBox() * GetBBoxSize();
        if (atts.GetDisplayMethod() == StreamlineAttributes::Tubes &&
            dist < 0.1*tubeRadius)
            makeEndPoint = false;
        if (makeEndPoint)
            nNewPts++;
    }
1210 1211 1212 1213
    //cout<<"   Draw: "<<idx0<<" to "<<idx1<<" ["<<t0<<" "<<t1<<"]"<<" pts= "<<nNewPts<<endl;

    pts->Allocate(nNewPts);
    scalars->Allocate(nNewPts);
1214
    params->Allocate(nNewPts);
1215 1216 1217 1218
    cells->InsertNextCell(nNewPts);

    int idx = 0;
    //If we have an interpolated start point, calculate it.
1219
    if (makeStartPoint)
1220 1221 1222 1223 1224
    {
        double prev