示例#1
0
        /// <summary>
        /// Gets the vectors describing the tick marks in source coordinates.
        /// </summary>
        private void GetTickVectors(out SizeF majorTickVector, out SizeF minorTickVector)
        {
            // compute tick vectors in display coordinates (since they are effectively invariant graphics)
            // but then convert them back to source coordinates because the graphics all use source coordinates
            CoordinateSystem = CoordinateSystem.Destination;
            try
            {
                var pt0 = Point1;
                var pt1 = Point2;

                // compute normal to the base line
                var normalX         = (double)pt0.Y - pt1.Y;
                var normalY         = (double)pt1.X - pt0.X;
                var normalMagnitude = Math.Sqrt(normalX * normalX + normalY * normalY);
                var normalUnit      = new SizeF((float)(Math.Abs(normalX) / normalMagnitude), (float)(Math.Abs(normalY) / normalMagnitude));

                var majorTickLength = _isMirrored ? -_majorTickLength : _majorTickLength;
                var minorTickLength = _isMirrored ? -_minorTickLength : _minorTickLength;

                majorTickVector = SpatialTransform.ConvertToSource(new SizeF(majorTickLength * normalUnit.Width, majorTickLength * normalUnit.Height));
                minorTickVector = SpatialTransform.ConvertToSource(new SizeF(minorTickLength * normalUnit.Width, minorTickLength * normalUnit.Height));
            }
            finally
            {
                ResetCoordinateSystem();
            }
        }
        protected override PointF ConstrainControlPointLocation(int controlPointIndex, PointF cursorLocation)
        {
            // this operation must be performed in source coodinates because the definition of top/left/right/bottom controls are relative to image not tile!
            var sourcePoint = SpatialTransform.ConvertToSource(cursorLocation);

            CoordinateSystem = CoordinateSystem.Source;
            try
            {
                var rect = Subject.Rectangle;
                switch (controlPointIndex)
                {
                case _top:
                case _bottom:
                    return(SpatialTransform.ConvertToDestination(new PointF(rect.Left + rect.Width / 2, sourcePoint.Y)));

                case _left:
                case _right:
                    return(SpatialTransform.ConvertToDestination(new PointF(sourcePoint.X, rect.Top + rect.Height / 2)));
                }
            }
            finally
            {
                ResetCoordinateSystem();
            }

            return(base.ConstrainControlPointLocation(controlPointIndex, cursorLocation));
        }
示例#3
0
        public void IsTransformed_ScaleUnity_RotateZeroPanZero_IsFalse()
        {
            var transform = new SpatialTransform {
                Scale = 1.0, Rotation = 0, Pan = new Point2(0, 0)
            };

            Assert.False(transform.IsTransformed);
        }
示例#4
0
        public void IsTransformed_PanNonZero_IsTrue()
        {
            var transform = new SpatialTransform {
                Pan = new Point2(5, -7)
            };

            Assert.True(transform.IsTransformed);
        }
示例#5
0
        /// <summary>
        /// Computes positions of tick marks along the base line.
        /// </summary>
        private Tick[] ComputeTickMarks()
        {
            // compute tick locations in source coordinates because all the underlying graphics use source coordinates, so we save on a ton of ridiculous back-and-forth conversions
            CoordinateSystem = CoordinateSystem.Source;
            try
            {
                var srcPoint1 = Point1;
                var srcPoint2 = Point2;

                // get the pixel dimensions of the image - if it's not calibrated, there's nothing to show!
                double pxW, pxH;
                bool   isCalibrated = TryGetPixelDimensions(out pxW, out pxH);
                if (!isCalibrated)
                {
                    return(null);
                }

                // figure out how long the base line is in mm, and thus how many ticks apart they are
                var mmLength       = GetDistance(srcPoint1.X * pxW, srcPoint1.Y * pxH, srcPoint2.X * pxW, srcPoint2.Y * pxH);
                var countTickUnits = mmLength / _minorTick;

                // round down and add 1 to get the number of tick marks displayable on the given base line
                var countTickMarks = 1 + (int)countTickUnits;

                // get the total distance between the end points in screen units
                var dstLength = Vector.Distance(SpatialTransform.ConvertToDestination(srcPoint1),
                                                SpatialTransform.ConvertToDestination(srcPoint2));

                // compute number of screen pixels between consecutive ticks
                // if the effective spacing is less than 3 pixels, the ticks will not render distinctly so we can stop here
                // additionally, we need at least two tick visible tick marks in order to have anything to show
                if (dstLength / countTickUnits < 3 || countTickMarks < 2)
                {
                    return(null);
                }

                // only distinguish between major/minor ticks if there are at least MAJOR_TICK_FREQ+1 ticks of spacing (i.e. at least two major ticks)
                var useMajorTicks = countTickMarks >= _majorTickFrequency + 1;

                var src1 = new Vector3D(srcPoint1.X, srcPoint1.Y, 0);
                var src2 = new Vector3D(srcPoint2.X, srcPoint2.Y, 0);
                var srcV = src2 - src1;                 // vector from point2 to point1

                var ticks = new Tick[countTickMarks];
                for (var n = 0; n < countTickMarks; ++n)
                {
                    // compute location for each tick from point1 + the vector scaled appropriately by index and the number of tick units along the line
                    // don't just compute one offset and keep adding it, as the floating point errors would then be cumulative
                    var location = src1 + srcV * (float)(n / countTickUnits);
                    ticks[n] = new Tick(location.X, location.Y, useMajorTicks && n % _majorTickFrequency == 0);
                }
                return(ticks);
            }
            finally
            {
                ResetCoordinateSystem();
            }
        }
        public PatientOrientationHelper(SpatialTransform imageTransform, PatientOrientation patientOrientation)
        {
            Platform.CheckForNullReference(imageTransform, "imageTransform");
            Platform.CheckForNullReference(patientOrientation, "patientOrientation");

            _imageTransform     = imageTransform;
            _patientOrientation = patientOrientation;
            AngleTolerance      = 1;
        }
        public PatientOrientationHelper(SpatialTransform imageTransform, ImageOrientationPatient imageOrientationPatient)
        {
            Platform.CheckForNullReference(imageTransform, "imageTransform");
            Platform.CheckForNullReference(imageOrientationPatient, "imageOrientationPatient");

            _imageTransform          = imageTransform;
            _imageOrientationPatient = imageOrientationPatient;
            AngleTolerance           = 1;
        }
 public override void OnDrawing()
 {
     if (base.ParentPresentationImage != null)
     {
         SpatialTransform transform = base.SpatialTransform;
         transform.TranslationX = (base.ParentPresentationImage.ClientRectangle.Width - this.Width) / 2f;
         transform.TranslationY = (base.ParentPresentationImage.ClientRectangle.Height - this.Height) / 2f;
     }
     base.OnDrawing();
 }
示例#9
0
 public override bool HitTest(Vector3D point)
 {
     CoordinateSystem = CoordinateSystem.Source;
     try
     {
         return(HitTest(SpatialTransform.ConvertPointToSource(point), Rectangle, SpatialTransform));
     }
     finally
     {
         ResetCoordinateSystem();
     }
 }
示例#10
0
        /// <summary>
        /// Called by GetAnnotationText (and also by Unit Test code).  Making this function internal simply makes it easier
        /// to write unit tests for this class (don't have to implement a fake PresentationImage).
        /// </summary>
        /// <param name="imageTransform">the image transform</param>
        /// <param name="patientOrientation">the image orientation patient (direction cosines)</param>
        /// <returns></returns>
        internal string GetAnnotationTextInternal(SpatialTransform imageTransform, PatientOrientation patientOrientation)
        {
            SizeF[] imageEdgeVectors = new SizeF[4];
            for (int i = 0; i < 4; ++i)
            {
                imageEdgeVectors[i] = imageTransform.ConvertToDestination(_edgeVectors[i]);
            }

            //find out which source image edge got transformed to coincide with this viewport edge.
            ImageEdge transformedEdge = GetTransformedEdge(imageEdgeVectors);

            //get the marker for the appropriate (source) image edge.
            return(GetMarker(transformedEdge, patientOrientation));
        }
示例#11
0
        /// <summary>
        /// Sets both endpoints of the scale's base line segment in one atomic operation, cause only one update and draw.
        /// </summary>
        /// <param name="point1">One endpoint of the base line segment.</param>
        /// <param name="point2">The other endpoint of the base line segment.</param>
        public void SetEndPoints(PointF point1, PointF point2)
        {
            if (CoordinateSystem != CoordinateSystem.Source)
            {
                point1 = SpatialTransform.ConvertToSource(point1);
                point2 = SpatialTransform.ConvertToSource(point2);
            }

            if (point1 != _point1 || point2 != _point2)
            {
                _point1 = point1;
                _point2 = point2;
                FlagAsDirty();
            }
        }
示例#12
0
        public static DicomImagePlane FromImage(IPresentationImage sourceImage)
        {
            if (sourceImage == null)
            {
                return(null);
            }

            Frame            frame     = GetFrame(sourceImage);
            SpatialTransform transform = GetSpatialTransform(sourceImage);

            if (transform == null || frame == null)
            {
                return(null);
            }

            if (String.IsNullOrEmpty(frame.FrameOfReferenceUid)
                ||
                String.IsNullOrEmpty(frame.ParentImageSop.StudyInstanceUID)
                )
            {
                return(null);
            }

            DicomImagePlane plane;

            if (_referenceCount > 0)
            {
                plane = CreateFromCache(frame);
            }
            else
            {
                plane = CreateFromFrame(frame);
            }

            if (plane != null)
            {
                plane._sourceImage          = sourceImage;
                plane._sourceImageTransform = transform;
                plane._sourceFrame          = frame;
            }

            return(plane);
        }
示例#13
0
        private void IncrementPan(int xIncrement, int yIncrement)
        {
            if (!CanPan())
            {
                return;
            }

            SpatialTransform transform = (SpatialTransform)_operation.GetOriginator(this.SelectedPresentationImage);

            // Because the pan increment is in destination coordinates, we have to convert
            // them to source coordinates, since the transform translation is in source coordinates.
            // This will allow the pan to work properly irrespective of the zoom, flip and rotation.

            SizeF sourceIncrement = transform.ConvertToSource(new SizeF(xIncrement, yIncrement));

            transform.TranslationX += sourceIncrement.Width;
            transform.TranslationY += sourceIncrement.Height;

            this.SelectedPresentationImage.Draw();
        }
        /// <summary>
        /// Called to notify the derived class of a control point change event.
        /// </summary>
        /// <param name="index">The index of the point that changed.</param>
        /// <param name="point">The value of the point that changed.</param>
        protected override void OnControlPointChanged(int index, PointF point)
        {
            // this operation must be performed in source coodinates because the definition of top-left/bottom-left/top-right/bottom-right controls are relative to image not tile!
            var sourcePoint = CoordinateSystem == CoordinateSystem.Destination ? SpatialTransform.ConvertToSource(point) : point;

            CoordinateSystem = CoordinateSystem.Source;
            try
            {
                var subject = Subject;
                var rect    = subject.Rectangle;
                switch (index)
                {
                case _topLeft:
                    subject.TopLeft = sourcePoint;
                    break;

                case _bottomRight:
                    subject.BottomRight = sourcePoint;
                    break;

                case _topRight:
                    subject.TopLeft     = new PointF(rect.Left, sourcePoint.Y);
                    subject.BottomRight = new PointF(sourcePoint.X, rect.Bottom);
                    break;

                case _bottomLeft:
                    subject.TopLeft     = new PointF(sourcePoint.X, rect.Top);
                    subject.BottomRight = new PointF(rect.Right, sourcePoint.Y);
                    break;
                }
            }
            finally
            {
                ResetCoordinateSystem();
            }

            base.OnControlPointChanged(index, point);
        }
            private static void SerializeDashedLine(PointF point1, PointF point2, SpatialTransform spatialTransform, GraphicAnnotationSequenceItem serializationState, bool showHashes)
            {
                // these control parameters are in screen pixels at the nominal presentation zoom level
                const float period    = 8;
                const float amplitude = 0.5f;

                SizeF normalVector;
                SizeF dashVector;
                float periods;

                // compute the dash vector and cross hash vector to be sized relative to screen pixels at nominal presentation zoom level
                {
                    PointF dstLineVector = spatialTransform.ConvertToDestination(new SizeF(point2) - new SizeF(point1)).ToPointF();
                    float  dstMagnitude  = (float)Math.Sqrt(dstLineVector.X * dstLineVector.X + dstLineVector.Y * dstLineVector.Y);
                    periods          = dstMagnitude / period;
                    dstLineVector.X /= dstMagnitude;
                    dstLineVector.Y /= dstMagnitude;
                    dashVector       = spatialTransform.ConvertToSource(new SizeF(dstLineVector.X * period / 2, dstLineVector.Y * period / 2));
                    normalVector     = spatialTransform.ConvertToSource(new SizeF(-dstLineVector.Y * amplitude, dstLineVector.X * amplitude));
                }

                PointF start = point1;
                int    limit = (int)periods;

                for (int n = 0; n < limit; n++)
                {
                    PointF midPeriod = start + dashVector;
                    start = midPeriod + dashVector;

                    GraphicObject dash = new GraphicObject();
                    dash.GraphicAnnotationUnits = GraphicAnnotationSequenceItem.GraphicAnnotationUnits.Pixel;
                    dash.GraphicData            = new PointF[] { midPeriod, start };
                    dash.GraphicDimensions      = 2;
                    dash.GraphicFilled          = GraphicAnnotationSequenceItem.GraphicFilled.N;
                    dash.GraphicType            = GraphicAnnotationSequenceItem.GraphicType.Polyline;
                    dash.NumberOfGraphicPoints  = 2;
                    serializationState.AppendGraphicObjectSequence(dash);
                }

                // the first half of each period has no line, so this is only necessary if the residual is over half a period
                if (periods - limit > 0.5f)
                {
                    GraphicObject dash = new GraphicObject();
                    dash.GraphicAnnotationUnits = GraphicAnnotationSequenceItem.GraphicAnnotationUnits.Pixel;
                    dash.GraphicData            = new PointF[] { start + dashVector, point2 };
                    dash.GraphicDimensions      = 2;
                    dash.GraphicFilled          = GraphicAnnotationSequenceItem.GraphicFilled.N;
                    dash.GraphicType            = GraphicAnnotationSequenceItem.GraphicType.Polyline;
                    dash.NumberOfGraphicPoints  = 2;
                    serializationState.AppendGraphicObjectSequence(dash);
                }

                if (showHashes)
                {
                    GraphicObject hash1 = new GraphicObject();
                    hash1.GraphicAnnotationUnits = GraphicAnnotationSequenceItem.GraphicAnnotationUnits.Pixel;
                    hash1.GraphicData            = new PointF[] { point1 - normalVector, point1 + normalVector };
                    hash1.GraphicDimensions      = 2;
                    hash1.GraphicFilled          = GraphicAnnotationSequenceItem.GraphicFilled.N;
                    hash1.GraphicType            = GraphicAnnotationSequenceItem.GraphicType.Polyline;
                    hash1.NumberOfGraphicPoints  = 2;
                    serializationState.AppendGraphicObjectSequence(hash1);

                    GraphicObject hash2 = new GraphicObject();
                    hash2.GraphicAnnotationUnits = GraphicAnnotationSequenceItem.GraphicAnnotationUnits.Pixel;
                    hash2.GraphicData            = new PointF[] { point2 - normalVector, point2 + normalVector };
                    hash2.GraphicDimensions      = 2;
                    hash2.GraphicFilled          = GraphicAnnotationSequenceItem.GraphicFilled.N;
                    hash2.GraphicType            = GraphicAnnotationSequenceItem.GraphicType.Polyline;
                    hash2.NumberOfGraphicPoints  = 2;
                    serializationState.AppendGraphicObjectSequence(hash2);
                }
            }
示例#16
0
 internal void SetParentGraphic(IGraphic3D parentGraphic)
 {
     _parentGraphic = parentGraphic;
     SpatialTransform.ForceRecalculation();
 }