Esempio n. 1
0
bool SlicesRotator::DoDecideBetweenRotationAndSliceSelection(Action*, const StateEvent* e)
{
    // Decide between moving and rotation slices.
    // For basic decision logic see class documentation.

    /*
    Detail logic:

    1. Find the SliceNavigationController that has sent the event: this one defines our rendering plane and will NOT be rotated. Needs not even be counted or checked.
    2. Inspect every other SliceNavigationController
    - calculate the line intersection of this SliceNavigationController's plane with our rendering plane
    - if there is NO interesection, ignore and continue
    - IF there is an intersection
    - check the mouse cursor's distance from that line.
    0. if the line is NOT near the cursor, remember the plane as "one of the other planes" (which can be rotated in "locked" mode)
    1. on first line near the cursor,  just remember this intersection line as THE other plane that we want to rotate
    2. on every consecutive line near the cursor, check if the line is geometrically identical to the line that we want to rotate
    - if yes, we just push this line to the "other" lines and rotate it along
    - if no, then we have a situation where the mouse is near two other lines (e.g. crossing point) and don't want to rotate
    */
    const DisplayPositionEvent* posEvent = dynamic_cast<const DisplayPositionEvent*>(e->GetEvent());
    if (!posEvent) return false;

    BaseRenderer* clickedRenderer = e->GetEvent()->GetSender();
    const PlaneGeometry* ourViewportGeometry = dynamic_cast<const PlaneGeometry*>( clickedRenderer->GetCurrentWorldPlaneGeometry() );
    // These sanity checks were introduced with bug 17877, since plane geometries are now a shared base class of several geometries
    // They may ultimately be unecessary

    const mitk::AbstractTransformGeometry* abstractGeometry = dynamic_cast< const AbstractTransformGeometry * > (ourViewportGeometry);
    if (abstractGeometry != NULL) MITK_WARN << "SliceRotator recieved an AbstractTransformGeometry, expecting a simple PlainGeometry, behaviour should be verified.";
    const mitk::DisplayGeometry* displayGeometry = dynamic_cast< const DisplayGeometry * > (ourViewportGeometry);
    if (displayGeometry != NULL) MITK_WARN << "SliceRotator recieved a DisplayGeometry, expecting a simple PlainGeometry, behaviour should be verified.";
    // End sanity checks

    if (!ourViewportGeometry) return false;

    DisplayGeometry* clickedDisplayGeometry = clickedRenderer->GetDisplayGeometry();
    if (!clickedDisplayGeometry) return false;

    MITK_DEBUG << "=============================================";
    MITK_DEBUG << "Renderer under cursor is " << clickedRenderer->GetName();

    Point3D cursorPosition = posEvent->GetWorldPosition();
    const PlaneGeometry* geometryToBeRotated = NULL;  // this one is under the mouse cursor
    const PlaneGeometry* anyOtherGeometry = NULL;    // this is also visible (for calculation of intersection ONLY)
    Line3D intersectionLineWithGeometryToBeRotated;

    bool hitMultipleLines(false);
    m_SNCsToBeRotated.clear();

    const double threshholdDistancePixels = 12.0;

    for (SNCVector::iterator iter = m_RotatableSNCs.begin(); iter != m_RotatableSNCs.end(); ++iter)
    {
        // If the mouse cursor is in 3D Renderwindow, do not check for intersecting planes.
        if (clickedRenderer->GetMapperID() == BaseRenderer::Standard3D)
            break;

        const PlaneGeometry* otherRenderersRenderPlane = (*iter)->GetCurrentPlaneGeometry();
        if (otherRenderersRenderPlane == NULL) continue; // ignore, we don't see a plane
        MITK_DEBUG << "  Checking plane of renderer " << (*iter)->GetRenderer()->GetName();

        // check if there is an intersection
        Line3D intersectionLine; // between rendered/clicked geometry and the one being analyzed
        if (!ourViewportGeometry->IntersectionLine( otherRenderersRenderPlane, intersectionLine ))
        {
            continue; // we ignore this plane, it's parallel to our plane
        }

        // check distance from intersection line
        double distanceFromIntersectionLine = intersectionLine.Distance( cursorPosition );
        ScalarType distancePixels = distanceFromIntersectionLine / clickedDisplayGeometry->GetScaleFactorMMPerDisplayUnit();
        MITK_DEBUG << "    Distance of plane from cursor " << distanceFromIntersectionLine << " mm, which is around " << distancePixels << " px" ;

        // far away line, only remember for linked rotation if necessary
        if (distanceFromIntersectionLine > threshholdDistancePixels)
        {
            MITK_DEBUG << "    Plane is too far away --> remember as otherRenderersRenderPlane";
            anyOtherGeometry = otherRenderersRenderPlane; // we just take the last one, so overwrite each iteration (we just need some crossing point)
            // TODO what about multiple crossings? NOW we have undefined behavior / random crossing point is used

            if (m_LinkPlanes)
            {
                m_SNCsToBeRotated.push_back(*iter);
            }
        }
        else // close to cursor
        {
            MITK_DEBUG << "    Plane is close enough to cursor...";
            if ( geometryToBeRotated == NULL ) // first one close to the cursor
            {
                MITK_DEBUG << "    It is the first close enough geometry, remember as geometryToBeRotated";
                geometryToBeRotated = otherRenderersRenderPlane;
                intersectionLineWithGeometryToBeRotated = intersectionLine;
                m_SNCsToBeRotated.push_back(*iter);
            }
            else
            {
                MITK_DEBUG << "    Second or later close enough geometry";
                // compare to the line defined by geometryToBeRotated: if identical, just rotate this otherRenderersRenderPlane together with the primary one
                //                                                     if different, DON'T rotate

                if ( intersectionLine.IsParallel( intersectionLineWithGeometryToBeRotated )
                        && intersectionLine.Distance( intersectionLineWithGeometryToBeRotated.GetPoint1() ) < mitk::eps )
                {
                    MITK_DEBUG << "    This line is the same as intersectionLineWithGeometryToBeRotated which we already know";
                    m_SNCsToBeRotated.push_back(*iter);
                }
                else
                {
                    MITK_DEBUG << "    This line is NOT the same as intersectionLineWithGeometryToBeRotated which we already know";
                    hitMultipleLines = true;
                }
            }
        }
    }

    bool moveSlices(true);

    if ( geometryToBeRotated && anyOtherGeometry && ourViewportGeometry && !hitMultipleLines )
    {
        // assure all three are valid, so calculation of center of rotation can be done
        moveSlices = false;
    }

    MITK_DEBUG << "geometryToBeRotated:   " << (void*)geometryToBeRotated;
    MITK_DEBUG << "anyOtherGeometry:    " << (void*)anyOtherGeometry;
    MITK_DEBUG << "ourViewportGeometry: " << (void*)ourViewportGeometry;
    MITK_DEBUG << "hitMultipleLines?    " << hitMultipleLines;
    MITK_DEBUG << "moveSlices?          " << moveSlices;

    std::auto_ptr<StateEvent> decidedEvent;

    // question in state machine is: "rotate?"
    if (moveSlices) // i.e. NOT rotate
    {
        // move all planes to posEvent->GetWorldPosition()
        decidedEvent.reset( new StateEvent(EIDNO, e->GetEvent()) );
        MITK_DEBUG << "Rotation not possible, not enough information (other planes crossing rendering plane) ";
    }
    else
    {   // we DO have enough information for rotation
        m_LastCursorPosition = intersectionLineWithGeometryToBeRotated.Project(cursorPosition); // remember where the last cursor position ON THE LINE has been observed

        if (anyOtherGeometry->IntersectionPoint(intersectionLineWithGeometryToBeRotated, m_CenterOfRotation)) // find center of rotation by intersection with any of the OTHER lines
        {
            decidedEvent.reset( new StateEvent(EIDYES, e->GetEvent()) );
            MITK_DEBUG << "Rotation possible";
        }
        else
        {
            MITK_DEBUG << "Rotation not possible, cannot determine the center of rotation!?";
            decidedEvent.reset( new StateEvent(EIDNO, e->GetEvent()) );
        }
    }

    this->HandleEvent( decidedEvent.get() );

    return true;
}
Esempio n. 2
0
void mitk::PlaneGeometryDataMapper2D::CreateVtkCrosshair(mitk::BaseRenderer *renderer)
{
  bool visible = true;
  LocalStorage* ls = m_LSH.GetLocalStorage(renderer);
  ls->m_CrosshairActor->SetVisibility(0);
  ls->m_ArrowActor->SetVisibility(0);
  ls->m_CrosshairHelperLineActor->SetVisibility(0);

  GetDataNode()->GetVisibility(visible, renderer, "visible");

  if(!visible)
  {
    return;
  }

  PlaneGeometryData::Pointer input = const_cast< PlaneGeometryData * >(this->GetInput());
  mitk::DataNode* geometryDataNode = renderer->GetCurrentWorldPlaneGeometryNode();
  const PlaneGeometryData* rendererWorldPlaneGeometryData = dynamic_cast< PlaneGeometryData * >(geometryDataNode->GetData());

  // intersecting with ourself?
  if ( input.IsNull() || input.GetPointer() == rendererWorldPlaneGeometryData)
  {
    return; //nothing to do in this case
  }

  const PlaneGeometry *inputPlaneGeometry = dynamic_cast< const PlaneGeometry * >( input->GetPlaneGeometry() );

  const PlaneGeometry* worldPlaneGeometry = dynamic_cast< const PlaneGeometry* >(
        rendererWorldPlaneGeometryData->GetPlaneGeometry() );

  if ( worldPlaneGeometry && dynamic_cast<const AbstractTransformGeometry*>(worldPlaneGeometry)==NULL
       && inputPlaneGeometry && dynamic_cast<const AbstractTransformGeometry*>(input->GetPlaneGeometry() )==NULL)
  {
    const BaseGeometry *referenceGeometry = inputPlaneGeometry->GetReferenceGeometry();

    // calculate intersection of the plane data with the border of the
    // world geometry rectangle
    Point3D point1, point2;

    Line3D crossLine;

    // Calculate the intersection line of the input plane with the world plane
    if ( worldPlaneGeometry->IntersectionLine( inputPlaneGeometry, crossLine ) )
    {
      bool hasIntersection = referenceGeometry ? CutCrossLineWithReferenceGeometry(referenceGeometry, crossLine) :
                                                 CutCrossLineWithPlaneGeometry(inputPlaneGeometry, crossLine);

      if (!hasIntersection)
      {
        return;
      }

      point1 = crossLine.GetPoint1();
      point2 = crossLine.GetPoint2();

      vtkSmartPointer<vtkCellArray> lines = vtkSmartPointer<vtkCellArray>::New();
      vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
      vtkSmartPointer<vtkPolyData> linesPolyData = vtkSmartPointer<vtkPolyData>::New();


      // Now iterate through all other lines displayed in this window and
      // calculate the positions of intersection with the line to be
      // rendered; these positions will be stored in lineParams to form a
      // gap afterwards.
      NodesVectorType::iterator otherPlanesIt = m_OtherPlaneGeometries.begin();
      NodesVectorType::iterator otherPlanesEnd = m_OtherPlaneGeometries.end();

      otherPlanesIt = m_OtherPlaneGeometries.begin();
      int gapSize = 32;
      this->GetDataNode()->GetPropertyValue("Crosshair.Gap Size", gapSize, NULL);


      auto intervals = IntervalSet<double>( SimpleInterval<double>(0, 1));

      ScalarType lineLength = point1.EuclideanDistanceTo(point2);
      ScalarType gapInMM = gapSize * renderer->GetScaleFactorMMPerDisplayUnit();
      float gapSizeParam = gapInMM / lineLength;

      if( gapSize != 0 )
      {
        while ( otherPlanesIt != otherPlanesEnd )
        {
          bool ignorePlane = false;
          (*otherPlanesIt)->GetPropertyValue("Crosshair.Ignore", ignorePlane);
          if (ignorePlane)
          {
              ++otherPlanesIt;
              continue;
          }

          PlaneGeometry *otherPlaneGeometry = static_cast< PlaneGeometry * >(
                static_cast< PlaneGeometryData * >((*otherPlanesIt)->GetData() )->GetPlaneGeometry() );

          if (otherPlaneGeometry != inputPlaneGeometry && otherPlaneGeometry != worldPlaneGeometry)
          {
              double intersectionParam;
              if (otherPlaneGeometry->IntersectionPointParam(crossLine, intersectionParam) && intersectionParam > 0 &&
                  intersectionParam < 1)
              {
                Point3D point = crossLine.GetPoint() + intersectionParam * crossLine.GetDirection();

                bool intersectionPointInsideOtherPlane =
                  otherPlaneGeometry->HasReferenceGeometry() ?
                    TestPointInReferenceGeometry(otherPlaneGeometry->GetReferenceGeometry(), point) :
                    TestPointInPlaneGeometry(otherPlaneGeometry, point);

                if (intersectionPointInsideOtherPlane)
                {
                  intervals -= SimpleInterval<double>(intersectionParam - gapSizeParam, intersectionParam + gapSizeParam);
                }
              }
          }
          ++otherPlanesIt;
        }
      }

      for (const auto& interval : intervals.getIntervals()) {
          this->DrawLine(crossLine.GetPoint(interval.GetLowerBoundary()), crossLine.GetPoint(interval.GetUpperBoundary()), lines, points);
      }

      // Add the points to the dataset
      linesPolyData->SetPoints(points);
      // Add the lines to the dataset
      linesPolyData->SetLines(lines);

      Vector3D orthogonalVector;
      orthogonalVector = inputPlaneGeometry->GetNormal();
      worldPlaneGeometry->Project(orthogonalVector,orthogonalVector);
      orthogonalVector.Normalize();

      // Visualize
      ls->m_Mapper->SetInputData(linesPolyData);
      ls->m_CrosshairActor->SetMapper(ls->m_Mapper);

      // Determine if we should draw the area covered by the thick slicing, default is false.
      // This will also show the area of slices that do not have thick slice mode enabled
      bool showAreaOfThickSlicing = false;
      GetDataNode()->GetBoolProperty( "reslice.thickslices.showarea", showAreaOfThickSlicing );

      // determine the pixelSpacing in that direction
      double thickSliceDistance = SlicedGeometry3D::CalculateSpacing(
        referenceGeometry ? referenceGeometry->GetSpacing() : inputPlaneGeometry->GetSpacing(), orthogonalVector);

      IntProperty *intProperty=0;
      if( GetDataNode()->GetProperty( intProperty, "reslice.thickslices.num" ) && intProperty )
        thickSliceDistance *= intProperty->GetValue()+0.5;
      else
        showAreaOfThickSlicing = false;

      // not the nicest place to do it, but we have the width of the visible bloc in MM here
      // so we store it in this fancy property
      GetDataNode()->SetFloatProperty( "reslice.thickslices.sizeinmm", thickSliceDistance*2 );

      ls->m_CrosshairActor->SetVisibility(1);

      vtkSmartPointer<vtkPolyData> arrowPolyData = vtkSmartPointer<vtkPolyData>::New();
      ls->m_Arrowmapper->SetInputData(arrowPolyData);
      if(this->m_RenderOrientationArrows)
      {
        ScalarType triangleSizeMM = 7.0 * renderer->GetScaleFactorMMPerDisplayUnit();

        vtkSmartPointer<vtkCellArray> triangles = vtkSmartPointer<vtkCellArray>::New();
        vtkSmartPointer<vtkPoints> triPoints = vtkSmartPointer<vtkPoints>::New();

        DrawOrientationArrow(triangles,triPoints,triangleSizeMM,orthogonalVector,point1,point2);
        DrawOrientationArrow(triangles,triPoints,triangleSizeMM,orthogonalVector,point2,point1);
        arrowPolyData->SetPoints(triPoints);
        arrowPolyData->SetPolys(triangles);
        ls->m_ArrowActor->SetVisibility(1);
      }

      // Visualize
      vtkSmartPointer<vtkPolyData> helperlinesPolyData = vtkSmartPointer<vtkPolyData>::New();
      ls->m_HelperLinesmapper->SetInputData(helperlinesPolyData);
      if ( showAreaOfThickSlicing )
      {
        vtkSmartPointer<vtkCellArray> helperlines = vtkSmartPointer<vtkCellArray>::New();
        // vectorToHelperLine defines how to reach the helperLine from the mainLine
        // got the right direction, so we multiply the width
        Vector3D vecToHelperLine = orthogonalVector * thickSliceDistance;

        this->DrawLine(point1 - vecToHelperLine, point2 - vecToHelperLine,helperlines,points);
        this->DrawLine(point1 + vecToHelperLine, point2 + vecToHelperLine,helperlines,points);

        // Add the points to the dataset
        helperlinesPolyData->SetPoints(points);

        // Add the lines to the dataset
        helperlinesPolyData->SetLines(helperlines);

        ls->m_CrosshairActor->GetProperty()->SetLineStipplePattern(0xf0f0);
        ls->m_CrosshairActor->GetProperty()->SetLineStippleRepeatFactor(1);
        ls->m_CrosshairHelperLineActor->SetVisibility(1);
      }
    }
  }
}