/// <summary> /// Determines the positions, in world coordinates, of the small ticks /// if they have not already been generated. /// /// </summary> /// <param name="physicalMin">The physical position corresponding to the world minimum of the axis.</param> /// <param name="physicalMax">The physical position corresponding to the world maximum of the axis.</param> /// <param name="largeTickPositions">The positions of the large ticks.</param> /// <param name="smallTickPositions">If null, small tick positions are returned via this parameter. Otherwise this function does nothing.</param> internal override void WorldTickPositions_SecondPass( Point physicalMin, Point physicalMax, ArrayList largeTickPositions, ref ArrayList smallTickPositions) { // return if already generated. if (smallTickPositions != null) { return; } int physicalAxisLength = Utils.Distance(physicalMin, physicalMax); double adjustedMax = this.AdjustedWorldValue(WorldMax); double adjustedMin = this.AdjustedWorldValue(WorldMin); smallTickPositions = new ArrayList(); // TODO: Can optimize this now. bool shouldCullMiddle; double bigTickSpacing = this.DetermineLargeTickStep(physicalAxisLength, out shouldCullMiddle); int nSmall = this.DetermineNumberSmallTicks(bigTickSpacing); double smallTickSpacing = bigTickSpacing / (double)nSmall; // if there is at least one big tick if (largeTickPositions.Count > 0) { double pos1 = (double)largeTickPositions[0] - smallTickSpacing; while (pos1 > adjustedMin) { smallTickPositions.Add(pos1); pos1 -= smallTickSpacing; } } for (int i = 0; i < largeTickPositions.Count; ++i) { for (int j = 1; j < nSmall; ++j) { double pos = (double)largeTickPositions[i] + ((double)j) * smallTickSpacing; if (pos <= adjustedMax) { smallTickPositions.Add(pos); } } } }
/// <summary> /// Determines the positions, in world coordinates, of the large ticks. /// When the physical extent of the axis is small, some of the positions /// that were generated in this pass may be converted to small tick /// positions and returned as well. /// /// If the LargeTickStep isn't set then this is calculated automatically and /// depends on the physical extent of the axis. /// </summary> /// <param name="physicalMin">The physical position corresponding to the world minimum of the axis.</param> /// <param name="physicalMax">The physical position corresponding to the world maximum of the axis.</param> /// <param name="largeTickPositions">ArrayList containing the positions of the large ticks.</param> /// <param name="smallTickPositions">ArrayList containing the positions of the small ticks if calculated, null otherwise.</param> internal override void WorldTickPositions_FirstPass( Point physicalMin, Point physicalMax, out ArrayList largeTickPositions, out ArrayList smallTickPositions ) { // (1) error check if (double.IsNaN(WorldMin) || double.IsNaN(WorldMax)) { throw new NPlotException("world extent of axis not set."); } double adjustedMax = this.AdjustedWorldValue(WorldMax); double adjustedMin = this.AdjustedWorldValue(WorldMin); // (2) determine distance between large ticks. bool shouldCullMiddle; double tickDist = this.DetermineLargeTickStep( Utils.Distance(physicalMin, physicalMax), out shouldCullMiddle); // (3) determine starting position. double first = 0.0f; if (!double.IsNaN(largeTickValue_)) { // this works for both case when largTickValue_ lt or gt adjustedMin. first = largeTickValue_ + (Math.Ceiling((adjustedMin - largeTickValue_) / tickDist)) * tickDist; } else { if (adjustedMin > 0.0) { double nToFirst = Math.Floor(adjustedMin / tickDist) + 1.0f; first = nToFirst * tickDist; } else { double nToFirst = Math.Floor(-adjustedMin / tickDist) - 1.0f; first = -nToFirst * tickDist; } // could miss one, if first is just below zero. if ((first - tickDist) >= adjustedMin) { first -= tickDist; } } // (4) now make list of large tick positions. largeTickPositions = new ArrayList(); if (tickDist < 0.0) // some sanity checking. TODO: remove this. { throw new NPlotException("Tick dist is negative"); } double position = first; int safetyCount = 0; while ( (position <= adjustedMax) && (++safetyCount < 5000)) { largeTickPositions.Add(position); position += tickDist; } // (5) if the physical extent is too small, and the middle // ticks should be turned into small ticks, then do this now. smallTickPositions = null; if (shouldCullMiddle) { smallTickPositions = new ArrayList(); if (largeTickPositions.Count > 2) { for (int i = 1; i < largeTickPositions.Count - 1; ++i) { smallTickPositions.Add(largeTickPositions[i]); } } ArrayList culledPositions = new ArrayList(); culledPositions.Add(largeTickPositions[0]); culledPositions.Add(largeTickPositions[largeTickPositions.Count - 1]); largeTickPositions = culledPositions; } }
/// <summary> /// Given Graphics surface, and physical extents of axis, draw ticks and /// associated labels. /// </summary> /// <param name="g">The GDI+ Graphics surface on which to draw.</param> /// <param name="physicalMin">The physical location of the world min point</param> /// <param name="physicalMax">The physical location of the world max point</param> /// <param name="boundingBox">out: smallest box that completely encompasses all of the ticks and tick labels.</param> /// <param name="labelOffset">out: a suitable offset from the axis to draw the axis label.</param> protected override void DrawTicks( IGraphics g, Point physicalMin, Point physicalMax, out object labelOffset, out object boundingBox) { Point tLabelOffset; Rectangle tBoundingBox; labelOffset = getDefaultLabelOffset(physicalMin, physicalMax); boundingBox = null; // draw the tick labels (but not the ticks). PointF lastPos = WorldToPhysical((double)numbers_[0], physicalMin, physicalMax, true); for (int i = 0; i < labels_.Count; ++i) { if ((double)numbers_[i] > WorldMin && (double)numbers_[i] < WorldMax) { // check to make sure labels are far enough appart. PointF thisPos = WorldToPhysical((double)numbers_[i], physicalMin, physicalMax, true); float dist = Utils.Distance(thisPos, lastPos); if (i == 0 || (dist > PhysicalSpacingMin)) { lastPos = thisPos; DrawTick(g, (double)numbers_[i], 0, (string)labels_[i], new Point(0, 0), physicalMin, physicalMax, out tLabelOffset, out tBoundingBox); UpdateOffsetAndBounds( ref labelOffset, ref boundingBox, tLabelOffset, tBoundingBox); } } } // now draw the ticks (which might not be aligned with the tick text). List <double> largeTickPositions; List <double> smallTickPositions; WorldTickPositions_FirstPass(physicalMin, physicalMax, out largeTickPositions, out smallTickPositions); lastPos = WorldToPhysical((double)largeTickPositions[0], physicalMin, physicalMax, true); for (int i = 0; i < largeTickPositions.Count; ++i) { double tickPos = (double)largeTickPositions[i]; // check to see that labels are far enough appart. PointF thisPos = WorldToPhysical(tickPos, physicalMin, physicalMax, true); float dist = Utils.Distance(thisPos, lastPos); if ((i == 0) || (dist > PhysicalSpacingMin)) { lastPos = thisPos; DrawTick(g, tickPos, LargeTickSize, "", new Point(0, 0), physicalMin, physicalMax, out tLabelOffset, out tBoundingBox); UpdateOffsetAndBounds( ref labelOffset, ref boundingBox, tLabelOffset, tBoundingBox); } } }