/// <summary> /// Recomputes and reformats the graphics that comprise the scale. /// </summary> protected virtual void UpdateScale() { // no point recomputing the scale if client code has made us invisible or we aren't on screen if (!this.Visible || (base.ParentPresentationImage != null && base.ParentPresentationImage.ClientRectangle.IsEmpty)) { _isDirty = true; return; } base.CoordinateSystem = CoordinateSystem.Destination; try { PointF pt0 = this.Point1; PointF pt1 = this.Point2; // draw base line FormatBaseLine(_baseLine, pt0, pt1); // compute normal to the base line TickOffset unitNormal = new TickOffset(pt0.Y - pt1.Y, pt1.X - pt0.X).GetUnitOffset(); // compute tick marks IList <PointF> majorTicks; IList <PointF> minorTicks; ComputeTickMarks(out majorTicks, out minorTicks, pt0, pt1, false); if (majorTicks == null || minorTicks == null) { _baseLine.Visible = false; return; } // draw tick marks if (majorTicks.Count + minorTicks.Count > 1) // must be at least 2 ticks for this to be useful { IList <LinePrimitive> ticks = AllocateTicks(majorTicks.Count + minorTicks.Count); for (int n = 0; n < minorTicks.Count; n++) { FormatMinorTick(ticks[n], minorTicks[n], unitNormal); } for (int n = 0; n < majorTicks.Count; n++) { FormatMajorTick(ticks[minorTicks.Count + n], majorTicks[n], unitNormal); } _baseLine.Visible = true; } else { AllocateTicks(0); _baseLine.Visible = false; } _isDirty = false; } finally { base.ResetCoordinateSystem(); } }
/// <summary> /// Recomputes and reformats the graphics that comprise the scale. /// </summary> protected virtual void UpdateScale() { // no point recomputing the scale if client code has made us invisible or we aren't on screen if (!this.Visible || (base.ParentPresentationImage != null && base.ParentPresentationImage.ClientRectangle.IsEmpty)) { _isDirty = true; return; } base.CoordinateSystem = CoordinateSystem.Destination; try { PointF pt0 = this.Point1; PointF pt1 = this.Point2; // draw base line FormatBaseLine(_baseLine, pt0, pt1); // compute normal to the base line TickOffset unitNormal = new TickOffset(pt0.Y - pt1.Y, pt1.X - pt0.X).GetUnitOffset(); // compute tick marks IList<PointF> majorTicks; IList<PointF> minorTicks; ComputeTickMarks(out majorTicks, out minorTicks, pt0, pt1, false); if (majorTicks == null || minorTicks == null) { _baseLine.Visible = false; return; } // draw tick marks if (majorTicks.Count + minorTicks.Count > 1) // must be at least 2 ticks for this to be useful { IList<LinePrimitive> ticks = AllocateTicks(majorTicks.Count + minorTicks.Count); for (int n = 0; n < minorTicks.Count; n++) { FormatMinorTick(ticks[n], minorTicks[n], unitNormal); } for (int n = 0; n < majorTicks.Count; n++) { FormatMajorTick(ticks[minorTicks.Count + n], majorTicks[n], unitNormal); } _baseLine.Visible = true; } else { AllocateTicks(0); _baseLine.Visible = false; } _isDirty = false; } finally { base.ResetCoordinateSystem(); } }
/// <summary> /// Computes positions of major and minor tick marks along the specified line segment. /// </summary> /// <remarks> /// <para> /// For performance reasons the arguments are <b>not</b> validated. In particular, the line segment specified by the two points /// must be valid and non-trivial - specifying the same point (or close to the same point) for both end points produces indeterminate /// results. /// </para> /// <para> /// If the image is not calibrated and has no pixel spacing information, both /// <paramref name="majorTicks"/> and <paramref name="minorTicks"/> return null. /// </para> /// </remarks> /// <param name="majorTicks">Output variable to receive a list of major tick positions.</param> /// <param name="minorTicks">Output variable to receive a list of minor tick positions.</param> /// <param name="linePoint1">One endpoint of the line segment along which to compute tick positions.</param> /// <param name="linePoint2">The other endpoint of the line segment along which to compute tick positions.</param> /// <param name="allowOverlap">Specifies if minor ticks coincident with an existing major tick should be included in the results.</param> protected void ComputeTickMarks(out IList <PointF> majorTicks, out IList <PointF> minorTicks, PointF linePoint1, PointF linePoint2, bool allowOverlap) { PointF p0 = base.SpatialTransform.ConvertToSource(linePoint1); PointF p1 = base.SpatialTransform.ConvertToSource(linePoint2); PointF pM = Vector.Midpoint(p0, p1); bool xyW; double len; double pxR, pxS; double pxW, pxH; bool isCalibrated = TryGetPixelDimensions(out pxW, out pxH); if (!isCalibrated) { majorTicks = null; minorTicks = null; return; } if (!FloatComparer.AreEqual(p0.X, p1.X, 0.001f)) { pxR = Math.Abs((p0.Y - p1.Y) / (p0.X - p1.X)); pxS = 1 / Math.Sqrt(pxW * pxW + pxH * pxH * pxR * pxR); xyW = false; len = Math.Abs(p0.X - p1.X) / pxS; } else { pxR = 0f; pxS = 1 / Math.Sqrt(pxH * pxH + pxW * pxW * pxR * pxR); xyW = true; len = Math.Abs(p0.Y - p1.Y) * pxH; } // compute the square of the effective on-screen tick spacing (in screen pixels) // if the effective spacing is less than 3 pixels, the ticks will start being too silly to render distinctly const int effectiveScreenTickSpacingThreshold = 9; var screenTickSpacing = base.SpatialTransform.ConvertToDestination(TickOffset.CreateTickOffset(MinorTick * pxS, pxR, xyW)); var effectiveScreenTickSpacing = screenTickSpacing.Width * screenTickSpacing.Width + screenTickSpacing.Height * screenTickSpacing.Height; List <PointF> listMajorTicks = new List <PointF>(); List <PointF> listMinorTicks = new List <PointF>(); if (this.MinorTick < len && effectiveScreenTickSpacing > effectiveScreenTickSpacingThreshold) { Dictionary <string, object> map = new Dictionary <string, object>(); PointF ptPos, ptNeg; // compute major tick positions and index their positions if (this.MajorTick < len) { TickOffset majorOffset = TickOffset.CreateTickOffset(this.MajorTick * pxS, pxR, xyW); ptPos = ptNeg = pM; while (Math.Abs(Vector.SubtendedAngle(p0, ptPos, p1)) > 90) { listMajorTicks.Add(base.SpatialTransform.ConvertToDestination(ptPos)); map.Add(string.Format("{0:f2},{1:f2}", ptPos.X, ptPos.Y), null); if (!ptPos.Equals(ptNeg)) { listMajorTicks.Insert(0, base.SpatialTransform.ConvertToDestination(ptNeg)); map.Add(string.Format("{0:f2},{1:f2}", ptNeg.X, ptNeg.Y), null); } ptPos = ptPos + majorOffset; ptNeg = ptNeg - majorOffset; } } // compute minor tick positions, checking the index for existence of a major tick at the same position TickOffset minorOffset = TickOffset.CreateTickOffset(this.MinorTick * pxS, pxR, xyW); ptPos = ptNeg = pM; while (Math.Abs(Vector.SubtendedAngle(p0, ptPos, p1)) > 90) { if (allowOverlap || !map.ContainsKey(string.Format("{0:f2},{1:f2}", ptPos.X, ptPos.Y))) { listMinorTicks.Add(base.SpatialTransform.ConvertToDestination(ptPos)); } if (!ptPos.Equals(ptNeg) && (allowOverlap || !map.ContainsKey(string.Format("{0:f2},{1:f2}", ptNeg.X, ptNeg.Y)))) { listMinorTicks.Insert(0, base.SpatialTransform.ConvertToDestination(ptNeg)); } ptPos = ptPos + minorOffset; ptNeg = ptNeg - minorOffset; } // if we don't have room for 2 ticks, try recomputing from one end instead of the middle if (listMajorTicks.Count + listMinorTicks.Count < 2) { listMajorTicks.Clear(); listMinorTicks.Clear(); ptPos = p0 + minorOffset; ptNeg = p0 - minorOffset; if (Math.Abs(Vector.SubtendedAngle(p0, ptPos, p1)) > 90) { listMinorTicks.Add(linePoint1); listMinorTicks.Add(base.SpatialTransform.ConvertToDestination(ptPos)); } else if (Math.Abs(Vector.SubtendedAngle(p0, ptNeg, p1)) > 90) { listMinorTicks.Add(linePoint1); listMinorTicks.Add(base.SpatialTransform.ConvertToDestination(ptNeg)); } } } majorTicks = listMajorTicks.AsReadOnly(); minorTicks = listMinorTicks.AsReadOnly(); }