void QgsDetailedItemDelegate::drawHighlight( const QStyleOptionViewItem &theOption,
    QPainter * thepPainter,
    int theHeight ) const
{
  QColor myColor1 = theOption.palette.highlight().color();
  QColor myColor2 = myColor1;
  myColor2 = myColor2.lighter( 110 ); //10% lighter
  QLinearGradient myGradient( QPointF( 0, theOption.rect.y() ),
                              QPointF( 0, theOption.rect.y() + theHeight ) );
  myGradient.setColorAt( 0, myColor1 );
  myGradient.setColorAt( 0.1, myColor2 );
  myGradient.setColorAt( 0.5, myColor1 );
  myGradient.setColorAt( 0.9, myColor2 );
  myGradient.setColorAt( 1, myColor2 );
  thepPainter->fillRect( theOption.rect, QBrush( myGradient ) );
}
void TrafficGraph::drawBeams(QPainter *p, int top, int w, int h, int horizontalScale)
{
  Q_ASSERT(mNiceRange != 0); if(mNiceRange == 0) mNiceRange = 1;
  double scaleFac = (h-1) / mNiceRange;

  int xPos = 0;
  QLinkedList< QList<double> >::Iterator it = mBeamData.begin();

  p->setPen(Qt::NoPen);
  /* In autoRange mode we determine the range and plot the values in
   * one go. This is more efficiently than running through the
   * buffers twice but we do react on recently discarded samples as
   * well as new samples one plot too late. So the range is not
   * correct if the recently discarded samples are larger or smaller
   * than the current extreme values. But we can probably live with
   * this.
   *
   * These values aren't used directly anywhere.  Instead we call
   * calculateNiceRange()  which massages these values into a nicer 
   * values.  Rounding etc.  This means it's safe to change these values
   * without affecting any other drawings
   * */
  if ( mUseAutoRange )
    mMinValue = mMaxValue = 0.0;

  /* mBezierCurveOffset is how many points we have at the start.
   * All the bezier curves are in groups of 3, with the first of the next group being the last point
   * of the previous group->
   *
   * Example, when mBezierCurveOffset == 0, and we have data, then just plot a normal bezier curve 
   * (we will have at least 3 points in this case)
   * When mBezierCurveOffset == 1, then we want a bezier curve that uses the first data point and 
   * the second data point.  Then the next group starts from the second data point.
   * When mBezierCurveOffset == 2, then we want a bezier curve that uses the first, second and third data
   *
   */
  for (unsigned int i = 0; it != mBeamData.end() && i < mSamples; ++i) {
    QPen pen;
    pen.setWidth(1);
    pen.setCapStyle(Qt::FlatCap);

    /**
     * We will plot 1 bezier curve for every 3 points, with the 4th point being the end
     * of one bezier curve and the start of the second.
     * This does means the bezier curves will not join nicely,
     * but it should be better than nothing.
     */

    QList<double> datapoints = *it;
    QList<double> prev_datapoints = datapoints;
    QList<double> prev_prev_datapoints = datapoints;
    QList<double> prev_prev_prev_datapoints = datapoints;

    if (i == 0 && mBezierCurveOffset>0) {
      /**
       * We are plotting an incomplete bezier curve - we don't have all the data we want.
       * Try to cope
       */
      xPos += horizontalScale*mBezierCurveOffset;
      if (mBezierCurveOffset == 1) {
        prev_datapoints = *it;
        ++it; //Now we are on the first element of the next group, if it exists
        if (it != mBeamData.end()) {
          prev_prev_prev_datapoints = prev_prev_datapoints = *it;
        } else {
          prev_prev_prev_datapoints = prev_prev_datapoints = prev_datapoints;
        }
      } else {
        // mBezierCurveOffset must be 2 now
        prev_datapoints = *it;
        Q_ASSERT(it != mBeamData.end());
        ++it;
        prev_prev_datapoints = *it;
        Q_ASSERT(it != mBeamData.end());
        ++it; //Now we are on the first element of the next group, if it exists
        if (it != mBeamData.end()) {
          prev_prev_prev_datapoints = *it;
        } else {
          prev_prev_prev_datapoints = prev_prev_datapoints;
        }
      }
    } else {
      /**
       * We have a group of 3 points at least.  That's 1 start point and 2 control points.
       */
      xPos += horizontalScale*3;
      it++;
      if (it != mBeamData.end()) {
        prev_datapoints = *it;
        it++;
        if (it != mBeamData.end()) {
          prev_prev_datapoints = *it;
          it++;  //We are now on the next set of data points
          if (it != mBeamData.end()) {
            // We have this datapoint, so use it for our finish point
            prev_prev_prev_datapoints = *it;
          } else {
            // We don't have the next set, so use our last control point as our finish point
            prev_prev_prev_datapoints = prev_prev_datapoints;
          }
        } else {
          prev_prev_prev_datapoints = prev_prev_datapoints = prev_datapoints;
        }
      } else {
          prev_prev_prev_datapoints = prev_prev_datapoints = prev_datapoints = datapoints;
      }
    }


    float x0 = w - xPos + 3.0*horizontalScale;
    float x1 = w - xPos + 2.0*horizontalScale;
    float x2 = w - xPos + 1.0*horizontalScale;
    float x3 = w - xPos;
    float y0 = h -1 + top;
    float y1 = y0;
    float y2 = y0;
    float y3 = y0;

    int offset = 0; //Our line is 2 pixels thick.  This means that when we draw the area, we need to offset 
    double max_y=0;
    double min_y=0;
    for (int j =  qMin(datapoints.size(), mBeamColors.size())-1; j >=0 ; --j) {
      if ( mUseAutoRange) {
        //If we use autorange, then we need to prepare the min and max values for _next_ time we paint
	//if we are stacking the beams, then we need to add the maximums together
	double current_maxvalue = qMax(datapoints[j], qMax(prev_datapoints[j], qMax(prev_prev_datapoints[j], prev_prev_prev_datapoints[j])));
	double current_minvalue = qMin(datapoints[j], qMin(prev_datapoints[j], qMin(prev_prev_datapoints[j], prev_prev_prev_datapoints[j])));
	mMaxValue = qMax(mMaxValue, current_maxvalue);
	mMinValue = qMin(mMinValue, current_maxvalue);
	if( mStackBeams ) {
	  max_y += current_maxvalue;
	  min_y += current_minvalue;
	}
      }

      /*
       * Draw polygon only if enough data points are available.
       */
      if ( j < prev_prev_prev_datapoints.count() &&
           j < prev_prev_datapoints.count() &&
           j < prev_datapoints.count() ) {

        QPolygon curve( 4 );


        /* The height of the whole widget is h+top->  The height of the area we are plotting in is just h.
	 * The y coordinate system starts from the top, so at the bottom the y coordinate is h+top
	 * So to draw a point at value y', we need to put this at  h+top-y'
	 */
	float delta_y0;
        delta_y0 = (datapoints[j] - mNiceMinValue)*scaleFac;

	float delta_y1;
	delta_y1 = (prev_datapoints[j] - mNiceMinValue)*scaleFac;
	
	float delta_y2;
	delta_y2 = (prev_prev_datapoints[j] - mNiceMinValue)*scaleFac;
	
	float delta_y3;
	delta_y3 = (prev_prev_prev_datapoints[j] - mNiceMinValue)*scaleFac;

        QPainterPath path;
	if(mStackBeams && offset) {
		//we don't want the lines to overdraw each other.  This isn't a great solution though :(
	  if(delta_y0 < 3) delta_y0=3;
	  if(delta_y1 < 3) delta_y1=3;
	  if(delta_y2 < 3) delta_y2=3;
	  if(delta_y3 < 3) delta_y3=3;
	}
	path.moveTo( x0,y0-delta_y0);
        path.cubicTo( x1,y1-delta_y1,x2,y2-delta_y2,x3,y3-delta_y3 );
        
	if(mFillBeams) {
	  QPainterPath path2(path);
          QLinearGradient myGradient(0,(h-1+top),0,(h-1+top)/5);
	  Q_ASSERT(mBeamColorsDark.size() >= j);
	  Q_ASSERT(mBeamColors.size() >= j);
	  QColor c0(mBeamColorsDark[j]);
	  QColor c1(mBeamColors[j]);
	  c0.setAlpha(150);
	  c1.setAlpha(150);
	  myGradient.setColorAt(0, c0);
          myGradient.setColorAt(1, c1);

          path2.lineTo( x3,y3-offset);
	  if(mStackBeams)
	    path2.cubicTo( x2,y2-offset,x1,y1-offset,x0,y0-offset); //offset is set to 1 after the first beam is drawn, so we don't trample on top of the 2pt thick line
	  else
	    path2.lineTo(x0,y0-1);
          p->setBrush(myGradient);
          p->setPen(Qt::NoPen);
          p->drawPath( path2 );
	}
	p->setBrush(Qt::NoBrush);
	Q_ASSERT(mBeamColors.size() >= j);
	pen.setColor(mBeamColors[j]);
	p->setPen(pen);
        p->drawPath( path );

	if(mStackBeams) {
          //We can draw the beams stacked on top of each other.  This means that say beam 0 has the value 2 and beam
	  // 1 has the value 3, then we plot beam 0 at 2 and beam 1 at 2+3 = 5.
	  y0-=delta_y0;
	  y1-=delta_y1;
	  y2-=delta_y2;
	  y3-=delta_y3;
	  offset = 1;  //see the comment further up for int offset;
	}
      }
      if ( mUseAutoRange && mStackBeams) {
	mMaxValue = qMax(max_y, mMaxValue);
	mMinValue = qMin(min_y, mMinValue);
      }
    }
  }
}
void VRAY_clusterThis::preProcess(GU_Detail * gdp)

{

   GEO_Point * ppt;

   long int num_points = (long int) gdp->points().entries();
   long int stat_interval = (long int)(num_points * 0.10) + 1;


   for(uint32 i = gdp->points().entries(); i-- > 0;) {
         ppt = gdp->points()(i);

         myPointList.append(i);

      }



   // If the user wants to build grids for pre processing
   // TODO: Should this be an option?  There may be functions/features that will depend on this ... discuss!
   if(!myPreProcess)
      return;

   if(myVerbose > CLUSTER_MSG_INFO)
      cout << "VRAY_clusterThis::preProcess() Pre Processing Voxels" << std::endl;

//                     openvdb::ScalarGrid::Accessor accessor;
//                     openvdb::FloatTree myTree;
   openvdb::FloatTree::ConstPtr myGeoTreePtr;
   openvdb::VectorTree::ConstPtr myGeoGradTreePtr;

   ParticleList paGeoList(gdp, myPreVDBRadiusMult, myPreVDBVelocityMult);
   openvdb::tools::PointSampler myGeoSampler, geoGradSampler;
//                     openvdb::tools::GridSampling<openvdb::FloatTree>  myGridSampler;

   if(myVerbose == CLUSTER_MSG_DEBUG)
      std::cout << "VRAY_clusterThis::preProcess() paGeoList.size() ... "  << paGeoList.size() << std::endl;

   if(paGeoList.size() != 0) {

         hvdb::Interrupter boss("VRAY_clusterThis::preProcess() Converting particles to level set");

         Settings settings;
         settings.mRadiusMin = myPreRadiusMin;
         settings.mRasterizeTrails = myPreRasterType;
         settings.mDx = myPreDx;  // only used for rasterizeTrails()
         settings.mFogVolume = myPreFogVolume;
         settings.mGradientWidth = myPreGradientWidth;  // only used for fog volume

         float background;

         // background in WS units
         if(myPreWSUnits)
            background = myPreBandWidth;
         // background NOT in WS units
         else
            background = myPreVoxelSize * myPreBandWidth;

         // Construct a new scalar grid with the specified background value.
         openvdb::math::Transform::Ptr transform =
            openvdb::math::Transform::createLinearTransform(myPreVoxelSize);

//         openvdb::ScalarGrid::Ptr myGeoGrid = openvdb::ScalarGrid::create(background);

         myGeoGrid = openvdb::ScalarGrid::create(background);

         myGeoGrid->setTransform(transform);
         myGeoGrid->setGridClass(openvdb::GRID_LEVEL_SET);

         // Perform the particle conversion.
         this->convert(myGeoGrid, paGeoList, settings, boss);

         if(myVerbose == CLUSTER_MSG_DEBUG) {
               std::cout << "VRAY_clusterThis::preProcess() - activeVoxelCount(): "
                         << myGeoGrid->activeVoxelCount() << std::endl;
               std::cout << "VRAY_clusterThis::preProcess() - background: "
                         << myGeoGrid->background() << std::endl;
            }

         // Insert the new grid into the ouput detail.
         UT_String gridNameStr = "ClusterGrid";
         myGeoGrid->insertMeta("float type", openvdb::StringMetadata("averaged_velocity"));
         myGeoGrid->insertMeta("name", openvdb::StringMetadata((const char *)gridNameStr));
         myGeoGrid->insertMeta("VoxelSize", openvdb::FloatMetadata(myPreVoxelSize));
         myGeoGrid->insertMeta("background", openvdb::FloatMetadata(background));


         UT_Vector3 pos, seed_pos, currVel;
//         const GA_PointGroup * sourceGroup = NULL;
         long int pt_counter = 0;
         float radius = 5.0f;

         if(myVerbose > CLUSTER_MSG_INFO)
            std::cout << "VRAY_clusterThis::preProcess() - Massaging data ... " << std::endl;

         long int pointsFound = 0;
         GEO_AttributeHandle inst_vel_gah = gdp->getPointAttribute("v");
         GEO_AttributeHandle source_vel_gah = gdp->getPointAttribute("v");
         GEO_AttributeHandle inst_N_gah = gdp->getPointAttribute("N");
         GEO_AttributeHandle source_N_gah = gdp->getPointAttribute("N");
         GEO_AttributeHandle inst_Cd_gah = gdp->getPointAttribute("Cd");
         GEO_AttributeHandle source_Cd_gah = gdp->getPointAttribute("Cd");
         GEO_AttributeHandle inst_Alpha_gah = gdp->getPointAttribute("Alpha");
         GEO_AttributeHandle source_Alpha_gah = gdp->getPointAttribute("Alpha");

         if(!inst_vel_gah.isAttributeValid())
            throw VRAY_clusterThis_Exception("VRAY_clusterThis::preProcess() Instance velocity handle invalid, exiting ...", 1);
         if(!source_vel_gah.isAttributeValid())
            throw VRAY_clusterThis_Exception("VRAY_clusterThis::preProcess() Source velocity handle invalid, exiting ...", 1);
         if(!inst_N_gah.isAttributeValid())
            throw VRAY_clusterThis_Exception("VRAY_clusterThis::preProcess() Instance normal handle invalid, exiting ...", 1);
         if(!source_N_gah.isAttributeValid())
            throw VRAY_clusterThis_Exception("VRAY_clusterThis::preProcess() Source normal handle invalid, exiting ...", 1);
         if(!inst_Cd_gah.isAttributeValid())
            throw VRAY_clusterThis_Exception("VRAY_clusterThis::preProcess() Instance color handle invalid, exiting ...", 1);
         if(!source_Cd_gah.isAttributeValid())
            throw VRAY_clusterThis_Exception("VRAY_clusterThis::preProcess() Source color handle invalid, exiting ...", 1);
         if(!inst_Alpha_gah.isAttributeValid())
            throw VRAY_clusterThis_Exception("VRAY_clusterThis::preProcess() Instance alpha handle invalid, exiting ...", 1);
         if(!source_Alpha_gah.isAttributeValid())
            throw VRAY_clusterThis_Exception("VRAY_clusterThis::preProcess() Source alpha handle invalid, exiting ...", 1);

         openvdb::FloatTree::ValueType sampleResult;
         openvdb::VectorGrid::ValueType gradResult;
         const openvdb::FloatTree aTree;
         openvdb::FloatTree& myGeoTree = myGeoGrid->treeRW();

         openvdb::tools::Filter<openvdb::FloatGrid> preProcessFilter(*myGeoGrid);
//                           openvdb::tools::Filter<openvdb::FloatGrid> barFilter(myGeoGrid);

         if(myPreVDBMedianFilter)
            preProcessFilter.median();

         if(myPreVDBMeanFilter)
            preProcessFilter.mean();

         if(myPreVDBMeanCurvatureFilter)
            preProcessFilter.meanCurvature();

         if(myPreVDBLaplacianFilter)
            preProcessFilter.laplacian();

//                           if(myVDBReNormalizeFilter)
//                              float r = barFilter.renormalize(3, 0.1);

         if(myPreVDBOffsetFilter)
            preProcessFilter.offset(myPreVDBOffsetFilterAmount);


         myGradientGrid = openvdb::VectorGrid::create();
//         openvdb::VectorGrid::Ptr myGradientGrid = openvdb::VectorGrid::create();
         myGradientGrid->setTransform(transform);
//               myGradientGrid->setGridClass(openvdb::GRID_FOG_VOLUME );
         myGradientGrid->setGridClass(openvdb::GRID_LEVEL_SET);

         openvdb::tools::Gradient<openvdb::ScalarGrid> myGradient(*myGeoGrid);
         myGradientGrid = myGradient.process();
         openvdb::VectorTree& myGeoGradTree = myGradientGrid->treeRW();

         gridNameStr = "ClusterGradientGrid";
         myGradientGrid->insertMeta("vector type", openvdb::StringMetadata("covariant (gradient)"));
         myGradientGrid->insertMeta("name", openvdb::StringMetadata((const char *)gridNameStr));
         myGradientGrid->insertMeta("VoxelSize", openvdb::FloatMetadata(myPreVoxelSize));
         myGradientGrid->insertMeta("background", openvdb::FloatMetadata(background));


         GA_FOR_ALL_GPOINTS(gdp, ppt) {
//                              myCurrPtOff = ppt->getMapOffset();
//                              std::cout << "myCurrPtOff: " << myCurrPtOff << std::endl;

            pos = ppt->getPos();

// Vec3d worldToIndex   (  const Vec3d &     xyz    )    const

//                              openvdb::Vec3R theIndex =
//                                 (openvdb::Vec3R(pos[0], pos[1], pos[2]));
            openvdb::Vec3R theIndex =
               myGeoGrid->worldToIndex(openvdb::Vec3R(pos[0], pos[1], pos[2]));

            radius = static_cast<fpreal>(ppt->getValue<fpreal>(myInstAttrRefs.pointVDBRadius, 0));
//                                    std::cout << "radius: " << radius << std::endl;

// static bool    sample (const TreeT &inTree, const Vec3R &inCoord, typename TreeT::ValueType &sampleResult)
            const openvdb::Vec3R  inst_sample_pos(theIndex[0], theIndex[1], theIndex[2]);

            bool success = myGeoSampler.sample(myGeoTree, inst_sample_pos, sampleResult);

            geoGradSampler.sample(myGeoGradTree, inst_sample_pos, gradResult);
//
//                              std::cout << "success: " << success << "\tpos: " << pos
//                                        << "\tinst_sample_pos: " << inst_sample_pos
//                                        << "\tsampleResult: " << sampleResult << std::endl;

//ValueType    sampleWorld (const Vec3R &pt) const
//ValueType    sampleWorld (Real x, Real y, Real z) const

            // if the instanced point is within the vdb volume
            if(success) {
//                                    std::cout << "pos: " << pos << " inst_sample_pos: "
//                                              << inst_sample_pos << " sampleResult: " << sampleResult
//                                              << " gradResult: " << gradResult << std::endl;
//                                    float weight;
                  pointsFound++;

                  inst_vel_gah.setElement(ppt);
                  currVel = inst_vel_gah.getV3();

                  UT_Vector3 gradVect = UT_Vector3(gradResult[0], gradResult[1], gradResult[2]);

                  ppt->setPos(pos + (myPrePosInfluence *(sampleResult * gradVect)));
//                                    ppt->setPos(pos + (sampleResult * myPosInfluence *(currVel / myFPS)));

//                                    inst_vel_gah.setV3(currVel * ((1 / sampleResult) * radius));
                  inst_vel_gah.setV3(currVel + (myPreVelInfluence *(sampleResult * gradVect)));

//                                    std::cout << "currVel: " << currVel << " sampleResult " << sampleResult
//                                              << " new vel: " <<  currVel * sampleResult << std::endl;

                  inst_N_gah.setV3(inst_N_gah.getV3() + (myPreNormalInfluence *(sampleResult * gradVect)));

//                        inst_Cd_gah.setElement(ppt);
//                        inst_Cd_gah.setV3(inst_Cd_gah.getV3() * abs(sampleResult));
//
//
//                        inst_Alpha_gah.setElement(ppt);
//                        inst_Alpha_gah.setF(inst_Alpha_gah.getF() * abs(sampleResult));

               } // if the instanced point is within the vdb volume

            if(myVerbose == CLUSTER_MSG_DEBUG) {
                  pt_counter++;
                  if((long int)(pt_counter % (stat_interval * myNumCopies * myRecursion)) == 0) {
                        cout << "VRAY_clusterThis::preProcess() Number of points pre processed: " << pt_counter
                             << "\t - Number of points found in vdb grid: " << pointsFound << std::endl;
                     }
               }

         }