Example #1
0
//  generate recursively
void STGGeneric::constructTreeRecursive(SphereTree *st) const{
  CHECK_DEBUG0(st != NULL && st->degree > 1 && st->levels >= 1);
  CHECK_DEBUG0(reducer != NULL);
  CHECK_DEBUG0(eval != NULL);
  CHECK_DEBUG0(surfacePoints != NULL);

  //  make cells to have 10 pts each, most will have alot more
  int numCells = surfacePoints->getSize() / PTS_PER_CELL;
  int gridDim = ceil(pow(numCells, 1.0 / 3.0));
  OUTPUTINFO("numCells = %d, gridDim = %d\n", numCells, gridDim);

  //  create surface rep
  SurfaceRep surRep;
  surRep.setup(*surfacePoints, gridDim);

  //  bounding sphere for root
  SFWhite::makeSphere(&st->nodes.index(0), *surfacePoints);

  //  setup for the base level (should really do for 
  //  all levels - but doesn't fit in recursive form)
  reducer->setupForLevel(0, st->degree, &surRep);

  //  make children nodes - using recursive algorithm
  if (st->levels > 1)
    makeChildren(st, 0, 1, surRep);
}
Example #2
0
double VFAdaptive::getErr(Voronoi3D::Vertex *vert, const SEBase *eval, const MedialTester *mt, const Voronoi3D *vor){
  if (vert->err < 0){
    if (!eval){                 //  no evaluator - use convex approx
      //  get closest point
      Point3D pClose;
      mt->closestPoint(&pClose, vert);

      if (vert->flag == VOR_FLAG_UNKNOWN)
        OUTPUTINFO("GetErr : UNCATEGORISED POINT\n");

      //  evaluate fit
      float dSur = pClose.distance(vert->s.c);
      if (vert->flag == VOR_FLAG_COVER){
        vert->err = vert->s.r + dSur;    //  sphere center is outside surface 
        }
      else{
        vert->err = vert->s.r - dSur;    //  sphere center is inside surface
        if (vert->err < 0)
          vert->err = 0;
        }
      }
    else{
      vert->err = eval->evalSphere(vert->s);
      }
    }

  return vert->err;
}
Example #3
0
bool insideSurfaceCrossingIter(const Point3D &p, const Surface &s,  const SpacialHash &faceHash, float mult){
  //  check against bounding box
  if (p.x < s.pMin.x || p.x > s.pMax.x ||
      p.y < s.pMin.y || p.y > s.pMax.y ||
      p.z < s.pMin.z || p.z > s.pMax.z){
    return false;
    }

  //  if we get 10 tests and they don't agree then we are ON the mesh
  //  would it be better to use the distance to surface first ??
  int inCount = 0, outCount = 0;
  while ((inCount+outCount < 1) || ((inCount < outCount*mult && outCount < inCount*mult) && inCount+outCount < 10)){
    for (int i = 0; i < 2; i++){
      bool in = insideSurfaceCrossing(p, s, faceHash);
      if (in)
        inCount++;
      else
        outCount++;
      }
    }

  if (inCount != 0 && outCount != 0)
    OUTPUTINFO("In = %d, out = %d\n", inCount, outCount);

  bool in = inCount > outCount;
  return in;
}
Example #4
0
void sampleDomain(Array<int> *counts, SamplerInfo infos[], int numVals, int numSamples){
  //  setup counts
  counts->reallocate(numVals);

  //  initialise domain info & counts
  for (int i = 0; i < numVals; i++){
    SamplerInfo *inf = &infos[i];
    inf->sourceI = i;

    counts->index(i) = 0;
    }

  //  order the infos in increasing order
  qsort(infos, numVals, sizeof(SamplerInfo), samplerInfoCompare);

  //  get total of the domain values
  float total = 0.0f;
  for (int i = 0; i < numVals; i++){
    total += infos[i].val;
    }


  //   make cumulative and work out maxSamples
  float totalSofar = 0.0f;
  for (int i = 0; i < numVals; i++){
    SamplerInfo *s = &infos[i];
    s->maxSamples = numSamples*(s->val / total);
    totalSofar += s->val;
    s->val = totalSofar;
    }

  //  generate samples
  for (int i = 0; i < numSamples; i++){
    //  pick a random triangle
    float r = rand()/(float)RAND_MAX;
    int j = findInfo(infos, r*total, numVals);

    if (j == -1){
      OUTPUTINFO("Problem attributing sample");
      i--;
      }
    else{
      SamplerInfo *s = &infos[j];
      if (counts->index(s->sourceI) <= s->maxSamples){
        counts->index(s->sourceI)++;  //  do sample
        }
      else{
        i--;                          //  reject sample
        }
      }
    }
}
Example #5
0
//-----------------------------------------------
CANPeakSysDongle::CANPeakSysDongle(int iBaudRate, std::string strDeviceName)
{
	m_bInitialized = false;
	m_handle = LINUX_CAN_Open(strDeviceName.c_str(), O_RDWR | O_NONBLOCK);
	if (!m_handle)
	{
		// Fatal error
		LOGERROR("can not open CANPeakSysDongle device : "<<strDeviceName<< " " << strerror(errno));
		Sleep(3000);
		exit(0);
	}
OUTPUTINFO("baudrate: %i", iBaudRate);
	init(iBaudRate);
}
Example #6
0
//  implementation of RE algorithm
bool REDiscard::reduceSpheres(Array<int> *inds, int maxNum, 
                               Array<int> *destCounts,
                               double maxMet, 
                               Array<double> *mets) const{
  //  get points
  const Array<Surface::Point> *surPts = surRep->getSurPts();
  int numPts = surPts->getSize();
  int numSph = srcSpheres->getSize();

  //  list of counts for number of spheres over each point
  Array<int> pointCounts(numPts), sphereCount(numSph);
  pointCounts.clear();

  //  fill list
  Array<bool> coveredPts(numPts);
  coveredPts.clear();
  for (int i = 0; i < numSph; i++){
    Array<int> list;
    surRep->listContainedPoints(&list, &coveredPts, srcSpheres->index(i));

    int numList = list.getSize(); 
    for (int j = 0; j < numList; j++){
      int pI = list.index(j);
      pointCounts.index(pI)++;
      }

    sphereCount.index(i) = numList;
    }

  //  no point continuing if the points aren't all covered to start
  for (int i = 0; i < numPts; i++)
    if (!coveredPts.index(i))
      return false;

  //  make list of spheres that are removable
  Array<bool> removable(numSph);
  for (int i = 0; i < numSph; i++)
    removable.index(i) = isSphereRemovable(pointCounts, i);  

  //  flags for which spheres have been dumped
  Array<bool> removed(numSph);
  removed.clear();

  //  do reduction
  while (true){
    //  find "smallest" removable sphere
    int minI = -1;
    int minCount = INT_MAX;
    for (int i = 0; i < numSph; i++){
      if (removable.index(i)){
        int count = sphereCount.index(i);
        if (count < minCount){
          minCount = count;
          minI = i;
          }
        }
      }

    //  did we find a removable sphere 
    if (minI < 0)
      break;

    OUTPUTINFO("Discarding Sphere %d\n", minI);

    //  remove sphere
    removed.index(minI) = true;
    removable.index(minI) = false;

    //  decrement counts for number of spheres covering the points
    Array<int> list;
    surRep->listContainedPoints(&list, NULL, srcSpheres->index(minI));
    int numList = list.getSize();
    for (int i = 0; i < numList; i++){
      int pI = list.index(i);
      pointCounts.index(pI)--;
      }
    
    //  check the spheres for removability
    for (int i = 0; i < numSph; i++){
      bool *flag = &removable.index(i);
      if ((*flag) == true){
        (*flag) = isSphereRemovable(pointCounts, i);  
        }
      }
    }

  //  generate indices
  inds->setSize(0);
  for (int i = 0; i < numSph; i++){
    if (!removed.index(i))
      inds->addItem() = i;
    }

  OUTPUTINFO("%d Spheres left\n", inds->getSize());

  return (maxNum < 0) || (inds->getSize() <= maxNum);
}
Example #7
0
void VFAdaptive::adaptiveSample(Voronoi3D *vor, const MedialTester &mt, const SurfaceRep *coverRep, float maxErr, int maxSam, int maxSph, int minSph, const Sphere *filterSphere, const SEBase *eval, bool countOnlyCoverSpheres, int maxLoops){
  if (maxErr < 0.0f)
    return;

  //  find the worst approximated point
  float errSofar;
  findWorstSphere(vor, mt, eval, filterSphere, &errSofar, NULL, false);
  OUTPUTINFO("Initial Error Sofar : %f\n", errSofar);

  //  label the vertices
  int numCover = mt.processMedial(vor, coverRep, filterSphere, countOnlyCoverSpheres);

  //  work out maximum loops alowed
  if (maxLoops <= 0){
    if (maxSam > 0)
      maxLoops = maxSam*3;
    else if (maxSph)
      maxLoops = maxSph*3;
    }

  //  find the starting error
  int loop = 0;
  while (true){
    loop++;

    //  find the worst approximated point
    int numInt = 0;
    float worstD = -1;
    int worstI = findWorstSphere(vor, mt, eval, filterSphere, &worstD, &numInt, true);
    if (countOnlyCoverSpheres)
      numInt = numCover;
    if (worstI < 0){
      //  clear the ban flags and try again
      int numVert = vor->vertices.getSize();
      for (int i = 0; i < numVert; i++){
        Voronoi3D::Vertex *vert = &vor->vertices.index(i);
        if (filterSphere && !vert->s.overlap(*filterSphere))
          continue;
        vor->resetFlag(vert);
        }

      //  update medial/coverage spheres
      numCover = mt.processMedial(vor, coverRep, filterSphere);
      worstI = findWorstSphere(vor, mt, eval, filterSphere, &worstD, &numInt, true);
      if (worstI < 0)
          break;  //  nothing more we can do
      }

    //  enable this to output POV files
    //saveSpheres(vor, mt, worstI);

    if ((loop %25) ==0)
      OUTPUTINFO("NumSpheres : %4d  \tErr : %10.6f  \terrSofar : %10.6f\n", numInt, worstD, errSofar);

    if (errSofar < 0)                       //  first iteration to have some spheres
      errSofar = worstD;  

    //  is it worth continuing ?? 
    if (worstD < maxErr && numInt > minSph)
      break;
    else if ((maxSam > 0 && vor->formingPoints.getSize()-9 >= maxSam*2) ||
             (maxSph > 0 && numInt >= maxSph*2) ||
             (maxLoops > 0 && loop > maxLoops)){
        //  normally we would wait until we get back down to the correct error
        //  but this CAN mean the algorithm will NEVER terminate
        //  so guard against that happening

        //  remove BAD cover spheres i.e. those with error > minError sofar
        //  otherwise this will cause major problems for Merge/Burst
        int numVerts = vor->vertices.getSize();
        for (int i = 0; i < numVerts; i++){
          Voronoi3D::Vertex *v = &vor->vertices.index(i);
          if (v->flag == VOR_FLAG_COVER){
            float err = getErr(v, eval, &mt, vor);
            if (err > errSofar)
              v->flag = VOR_FLAG_EXTERNAL;
            }
          }

        break;
        }
    else if (worstD <= errSofar || worstD <= EPSILON_LARGE){
      if (worstD > EPSILON_LARGE)
        errSofar = worstD;
      else
        errSofar = EPSILON_LARGE;

      if ((maxSam > 0 && vor->formingPoints.getSize()-9 >= maxSam) ||
          (maxSph > 0 && numInt >= maxSph) ||
          (maxLoops > 0 && loop > maxLoops))
        break;
      }

    //  flag the vertex so that we cannot consider it in the future
    //  just in case the vertex isn't removed
    Voronoi3D::Vertex *v = &vor->vertices.index(worstI);
    mt.blockMedial(v);

    //  get the closest surface point to the vertex 
    Vector3D n;
    Point3D pClose;
    mt.closestPointNormal(&pClose, &n, v);

    //  check if the new point will improve the approximation
    if (pClose.distanceSQR(v->s.c) > v->s.r*v->s.r)
      OUTPUTINFO("WARNING : the closest point is further away than it should be\n");

    //  add the point to the Voronoi diagram
    vor->insertPoint(pClose, n, worstI);

    // save off the results of adding the new sphere
    //  uncomment this to save POV files
    //saveSpheres(vor, mt, -1);

    //  update medial/coverage spheres
    numCover = mt.processMedial(vor, coverRep, filterSphere);
    }
}
Example #8
0
void STGGeneric::makeChildren(SphereTree *st, int node, int level, const SurfaceRep &surRep) const{
  //  get the error of the parent
  Sphere parS = st->nodes.index(node);
  double parErr = eval->evalSphere(parS);

  //  get minimum bounding sphere for points to give to reducer
  Sphere boundingSphere;
  SFWhite::makeSphere(&boundingSphere, *surRep.getSurPts());

  //  generate the set of child spheres
  Array<Sphere> initChildren, children;
  reducer->getSpheres(&initChildren, st->degree, surRep, &boundingSphere, parErr);

  //  do sphere refit - local optimisation
  if (useRefit){
    OUTPUTINFO("Refitting\n");
    SOPerSphere perSphere;
    perSphere.numIter = 3;
    perSphere.eval = eval;
    perSphere.optimise(&initChildren, surRep);
    }

  //  apply optimiser if required
  if (optimiser && (maxOptLevel < 0 || level <= maxOptLevel))
    optimiser->optimise(&initChildren, surRep, -1, &parS, level-1);

  //  remove redundent spheres
  RELargest reLargest;
  if (!reLargest.reduceSpheres(&children, initChildren, surRep))
    children.clone(initChildren);

  int numChildren = children.getSize();
  if (numChildren == 0)
    return;

  //  get the points that this node covers
  const Array<Surface::Point> *surPts = surRep.getSurPts();
  int numPts = surPts->getSize();

  //  info for areas to be covered by each sphere
  Array<Array<Surface::Point> > subPts(numChildren);
  Array<bool> covered(numPts);
  covered.clear();

  //  make the divisions between the children
  SurfaceDivision surDiv;
  surDiv.setup(children, surPts);

  //  do the children
  int firstChild = st->getFirstChild(node);
  for (int i = 0; i < numChildren; i++){
    //  get sphere
    Sphere s = children.index(i);

    //  list the points in the sphere
    Array<int> listPoints;
    surRep.listContainedPoints(&listPoints, NULL, s, NULL);
    int numList = listPoints.getSize();

    //  filter points
    Array<Surface::Point> *filterPts = &subPts.index(i);
    filterPts->resize(0);
    for (int j = 0; j < numList; j++){
      //  get point
      int pI = listPoints.index(j);
      Surface::Point p = surPts->index(pI);

      //  check if it's in the region
      if (surDiv.pointInRegion(p.p, i)){
        covered.index(j) = true;
        filterPts->addItem() = p;
        }
      }
    }

  //  count/cover uncovered points
  for (int i = 0; i < numPts; i++){
    if (!covered.index(i)){
      //  get the point
      Point3D p = surPts->index(i).p;

      //  find the closest sphere
      int closestJ = -1;
      float closestD = 0;
      for (int j = 0; j < numChildren;  j++){
        Sphere s = children.index(j);
        float d = p.distance(s.c) - s.r;
        if (d < closestD){
          closestJ = j;
          closestD = d;
          }
        }

      subPts.index(closestJ).addItem() = surPts->index(i);
      }
    }

  //  store spheres & recurse to children 
  int childNum = firstChild;
  for (int i = 0; i < numChildren; i++){
    if (subPts.index(i).getSize() > 1){
      //  recreate the sphere
      Sphere s = children.index(i);

      //  add sphere to tree
      st->nodes.index(childNum).c = s.c;
      st->nodes.index(childNum).r = s.r;

      if (level < st->levels-1 && numChildren > 1){
        const Array<Surface::Point> *pts = &subPts.index(i);

        //  make cells to have 10 pts each, most will have alot more
        int numCells = pts->getSize() / PTS_PER_CELL;
        int gridDim = ceil(pow(numCells, 1.0 / 3.0));
        OUTPUTINFO("numCells = %d, gridDim = %d\n", numCells, gridDim);

        //  make children by recursion
        SurfaceRep subRep;
        subRep.setup(*pts, gridDim);

        makeChildren(st, childNum, level+1, subRep);
        }

      childNum++;
      }
    }

  //  NULL out the rest of the spheres
  for (int i = childNum; i < st->degree; i++)
    st->initNode(firstChild+i, level+1);
}
Example #9
0
//  generate breadth first
void STGGeneric::constructTree(SphereTree *st) const{
  CHECK_DEBUG0(st != NULL && st->degree > 1 && st->levels >= 1);
  CHECK_DEBUG0(reducer != NULL);
  CHECK_DEBUG0(eval != NULL);
  CHECK_DEBUG0(surfacePoints != NULL);

  //  NULL out the entire tree
  st->initNode(0);

  //  make cells to have 10 pts each, most will have alot more
  int numCells = surfacePoints->getSize() / PTS_PER_CELL;
  int gridDim = ceil(pow(numCells, 1.0 / 3.0));
  OUTPUTINFO("numCells = %d, gridDim = %d\n", numCells, gridDim);

  SurfaceRep surRep;
  surRep.setup(*surfacePoints, gridDim);

  //  bounding sphere for root - should use vertices
  SFWhite::makeSphere(&st->nodes.index(0), *surfacePoints);

  //  list of points to be covered by each node
  unsigned long start, num;
  st->getRow(&start, &num, st->levels-1);
  Array<Array<int>/**/> pointsInSpheres;
  pointsInSpheres.resize(st->nodes.getSize() - num);

  //  initialise the list for the first node
  int numPts = surfacePoints->getSize();
  Array<int> *list0 = &pointsInSpheres.index(0);
  list0->resize(numPts);
  for (int i = 0; i < numPts; i++)
    list0->index(i) = i;

  //  process the remaining levels
  int numLeaves = 0;
  for (int level = 0; level < st->levels-1; level++){
    //  get the positions of the nodes
    unsigned long start, num;
    st->getRow(&start, &num, level);

    //  update samples etc. for this level
    reducer->setupForLevel(level, st->degree, &surRep);

    //  get the errors for all the spheres in that level
    int numActualSpheres = 0;
    double averageError = 0;
    Array<double> sphereErrors(num);
    for (int i = 0; i < num; i++){
      Sphere s = st->nodes.index(start+i);
      if (s.r >= 0){
        double err = eval->evalSphere(s);
        sphereErrors.index(i) = err;
        averageError += err;
        numActualSpheres++;
        }
      else 
        sphereErrors.index(i) = -1;
      }
    averageError /= numActualSpheres;
    if (level != 0 && numActualSpheres <= 1){
      numLeaves++;
      continue;     //  there is only one sphere here - will never improve
      }

    //  process each node's to make children
    int maxNode = -1;
    double maxR = -1;
    int levelChildren = 0;
    for (int nodeI = 0; nodeI < num; nodeI++){
      //OUTPUTINFO("Level = %d, node = %d\n", level, nodeI);
      printf("Level = %d, node = %d\n", level, nodeI);

      int node = nodeI + start;
      if (st->nodes.index(node).r <= 0){
          OUTPUTINFO("R = %f\n", st->nodes.index(node).r);
          st->initNode(node);
          continue;
          }

/*
      //  hack to do largest sphere at each run - gives guide to good params
      double r = st->nodes.index(node).r;
      if (r > maxR){
        maxR = r;
        maxNode = node;
        }
      }

      nodeI = maxNode - start;
      int node = maxNode;{
      //  end hack
*/

      //  make the set of surface poitns to be covered by this sphere
      Array<int> *selPtsI = &pointsInSpheres.index(node);
      int numSelPts = selPtsI->getSize();

      if (numSelPts <= 0)
        break;

      Array<Surface::Point> selPts(numSelPts);
      for (int i = 0; i < numSelPts; i++)
        selPts.index(i) = surfacePoints->index(selPtsI->index(i));

      //  get filter sphere
      Sphere s;
      SFWhite::makeSphere(&s, selPts);

      //  make cells to have 10 pts each, most will have alot more
      int numCells = numSelPts / PTS_PER_CELL;
      int gridDim = ceil(pow(numCells, 1.0 / 3.0));
      OUTPUTINFO("%d Points\n", numSelPts);
      OUTPUTINFO("numCells = %d, gridDim = %d\n", numCells, gridDim);

      //  make new SurfaceRepresentation
      SurfaceRep subRep;
      subRep.setup(selPts, gridDim);

      //  compute error for this sphere
      double err = sphereErrors.index(nodeI);
      if (err > averageError)
        err = averageError;  //  improve the bad ones a bit more

      //  generate the children nodes
      Array<Sphere> initChildren, children;
      reducer->getSpheres(&initChildren, st->degree, subRep, &s, err);

      //  apply optimiser if required
      if (optimiser && (maxOptLevel < 0 || level <= maxOptLevel)){
printf("RUNNING OPTIMISER...\n");
        optimiser->optimise(&initChildren, subRep, -1, &s, level);
printf("DONE OPTIMISING...\n");
        }

      //  do sphere refit - local optimisation
      if (useRefit){
        OUTPUTINFO("Refitting\n");
        SOPerSphere perSphere;
        perSphere.numIter = 3;
        perSphere.eval = eval;
        perSphere.optimise(&initChildren, subRep);
        }

      //  remove redundent spheres
      RELargest reLargest;
      if (!reLargest.reduceSpheres(&children, initChildren, subRep))
        children.clone(initChildren);

      //  setup the node's sub-division (make the regions to be covered by children)
      //subDivs.index(node).setup(children, &selPts);
      SurfaceDivision surDiv;
      surDiv.setup(children, &selPts);

      //  list of points that are covered
      Array<bool> covered(numSelPts);
      covered.clear();

      //  create the new nodes and their the points to cover
      int numChildren = children.getSize();
      int firstChild = st->getFirstChild(node);
      levelChildren += numChildren;
      for (int i = 0; i < numChildren; i++){
        int childNum = firstChild + i;

        //  get sphere
        const Sphere& s = children.index(i);

        //  add sphere to tree
        st->nodes.index(childNum).c = s.c;
        st->nodes.index(childNum).r = s.r;

        if (level < st->levels-2){
          //  get the points in this sphere
          Array<int> pointsInS;
          subRep.listContainedPoints(&pointsInS, NULL, s);
          int numInS = pointsInS.getSize();

          //  populate list of points in sphere
          Array<int> *pointsToCover = &pointsInSpheres.index(childNum);
          pointsToCover->resize(0);   //  just in case
          for (int j = 0; j < numInS; j++){
            int pI = pointsInS.index(j);
            if (surDiv.pointInRegion(selPts.index(pI).p, i)){
              pointsToCover->addItem() = selPtsI->index(pI);
              covered.index(pI) = true;
              }
            }
          }
        }

      //  assign uncovered points
      if (numChildren > 0 && level < st->levels-2){
        for (int i = 0; i < numSelPts; i++){
          if (!covered.index(i)){
            //  get point
            const Point3D& pt = selPts.index(i).p;

            //  find the sphere
            int minJ = -1;
            float minD = FLT_MAX;
            for (int j = 0; j < numChildren; j++){
              const Sphere& s = children.index(j);
              float d = pt.distance(s.c);// - s.r;
              if (d < minD){
                minD = d;
                minJ = j;
                }
              }

            //  add the point to the sphere's list
            pointsInSpheres.index(firstChild+minJ).addItem() = selPtsI->index(i);
            }
          }
        }

      //  save after each child set
      //st->saveSphereTree("saved.sph");
      }

    //  save after each level
    //st->saveSphereTree("saved.sph");

    //  see if we need to add another level
    if (level == st->levels - 2 && minLeaves > 0 && numLeaves + levelChildren < minLeaves){
      //  grow the tree
      OUTPUTINFO("Growing the tree : %d-->%d\n", st->levels, st->levels+1);
      OUTPUTINFO("New Nodes : %d-->", st->nodes.getSize());
      st->growTree(st->levels+1);
      OUTPUTINFO("%d\n", st->nodes.getSize());
      }
    }
}
Example #10
0
bool insideSurfaceClosest(const Point3D &pTest, const Surface &s, const SpacialHash &faceHash, ClosestPointInfo *inf, float stopBelow, bool allowQuickTest){
  if (inf)
    inf->type = DIST_TYPE_INVALID;

  //  quick bounding box test
  if (allowQuickTest){
    if (pTest.x < s.pMin.x || pTest.x > s.pMax.x ||
        pTest.y < s.pMin.y || pTest.y > s.pMax.y ||
        pTest.z < s.pMin.z || pTest.z > s.pMax.z){
      return false;
      }
    }

  ClosestPointInfo localClosestInf;
  if (!inf)
    inf = &localClosestInf;
  float dist = getClosestPoint(inf, pTest, s, faceHash, stopBelow);

  if (dist < stopBelow){
    //  optimise for dodec
    return true;
    }

  //  vector to point on surface
  Vector3D v;
  v.difference(pTest, inf->pClose);
  v.norm();

  if (inf->type == FACE){
    //  face test 
    Vector3D n;
    s.getTriangleNormal(&n, inf->triangle);
    double dt = n.dot(v);
    return dt <= 0;
    }
  else if (inf->type == EDGE){
    //  edge test
    const Surface::Triangle *tri = &s.triangles.index(inf->triangle);

    //  edge will be between vertices v[num] and v[(num+1)%3]
    int e[2];
    e[0] = tri->v[inf->num];
    e[1] = tri->v[(inf->num+1)%3];

    int neigh = findNeighbour(s, *tri, e);
    if (neigh >= 0){
      //  make a plane for one of the triangles
      Vector3D n1;
      s.getTriangleNormal(&n1, inf->triangle);
      Point3D p1 = s.vertices.index(e[0]).p;
      Plane pl1;
      pl1.assign(n1, p1);

      //  get the point from the other triangle which is not part of edge
      const Surface::Triangle *tri2 = &s.triangles.index(neigh);
      for (int i = 0; i < 3; i++){
        if (tri2->v[i] != e[0] && tri2->v[i] != e[1])
          break;
        }
      CHECK_DEBUG0(i != 3);
      Point3D p2 = s.vertices.index(e[1]).p;
      
      //  get signed distance to plane
      float dist = pl1.dist(p2);

      //  need normal for second triangle
      Vector3D n2;
      s.getTriangleNormal(&n2, neigh);

      if (dist <= 0.0f){
        //  faces form convex spike, back facing to both
        return v.dot(n1) <= 0 && v.dot(n2) <= 0;
        }
      else{
        //  faces form concavity, back facing to either
        return v.dot(n1) <= 0 || v.dot(n2) <= 0;
        }
      }
    else{
      OUTPUTINFO("HHHHHMMMMMMM loose edge\n");
      return false;  //  only one triangle on edge - use face ??
      }
    }
  else{// if (minType == VERTEX)
    // chosen triangle
    const Surface::Triangle *tri = &s.triangles.index(inf->triangle);

    //  chosen vertex
    int vI = tri->v[inf->num];
    Vector3D n;
    s.getVertexNormal(&n, vI);
    return n.dot(v) <= 0;

/*
    //  get all faces
    Array<int> tris;
    s.findNeighbours(&tris, vI, inf->triangle);

    //  behind test for all faces
    int numTri = tris.getSize();
    for (int i = 0; i < numTri; i++){
      Vector3D n;
      s.getTriangleNormal(&n, tris.index(i));
      double dt = n.dot(v);
      if (dt > 0)
        return false;
      }

    //  must be behind all
    return true;*/
    }
}