Profile Picture

PrimaryY Axis & SecondaryY Axis zero align issue

Posted By inhyuk son 14 Years Ago
Author
Message
inhyuk son
Posted 14 Years Ago
View Quick Profile
Junior Member

Junior Member (21 reputation)Junior Member (21 reputation)Junior Member (21 reputation)Junior Member (21 reputation)Junior Member (21 reputation)Junior Member (21 reputation)Junior Member (21 reputation)Junior Member (21 reputation)Junior Member (21 reputation)

Group: Forum Members
Last Active: 12 Years Ago
Posts: 21, Visits: 1
hi~
I have a problem.
I have two bar series, and one of the two has a negative value.
So I used a Y Axes(PrimaryY Axis & SecondaryY Axis).
But I can't align zero point each other.
What should i do?
Thanks~~



sample code:

NChart m_Chart;
NBarSeries m_Bar1;
NBarSeries m_Bar2;

Random random = new Random();
m_Chart = nChartControl1.Charts[0];

// add a bar series
m_Bar1 = (NBarSeries)m_Chart.Series.Add(SeriesType.Bar);
m_Bar1.MultiBarMode = MultiBarMode.Series;
m_Bar1.DataLabelStyle.Visible = false;

// add another bar series
m_Bar2 = (NBarSeries)m_Chart.Series.Add(SeriesType.Bar);
m_Bar2.MultiBarMode = MultiBarMode.Clustered;
m_Bar2.DataLabelStyle.Visible = false;

// fill with random data
m_Bar1.Values.FillRandomRange(random, 5, -100, 100);
m_Bar2.Values.FillRandomRange(random, 5, 10, 500);

m_Bar1.DisplayOnAxis(StandardAxis.PrimaryY, false);
m_Bar1.DisplayOnAxis(StandardAxis.SecondaryY, true);

m_Chart.Axis(StandardAxis.SecondaryY).Visible = true;

m_Chart.Axis(StandardAxis.PrimaryX).Anchor = new NCrossAxisAnchor(AxisOrientation.Horizontal, new NValueAxisCrossing(m_Chart.Axis(StandardAxis.PrimaryY), 0));
m_Chart.Axis(StandardAxis.PrimaryX).Anchor.RulerOrientation = RulerOrientation.Right;
m_Chart.Axis(StandardAxis.PrimaryY).View = new NRangeAxisView(new NRange1DD(0, 0), false, false);

NStyleSheet.CreatePredefinedStyleSheet(PredefinedStyleSheet.Nevron).Apply(nChartControl1.Document);

Attachments
chart.PNG (44 views, 10.00 KB)
bob milanov
Posted 14 Years Ago
View Quick Profile
Supreme Being

Supreme Being (152 reputation)Supreme Being (152 reputation)Supreme Being (152 reputation)Supreme Being (152 reputation)Supreme Being (152 reputation)Supreme Being (152 reputation)Supreme Being (152 reputation)Supreme Being (152 reputation)Supreme Being (152 reputation)

Group: Forum Members
Last Active: 6 Months Ago
Posts: 153, Visits: 11

Hi Inhyuk,

The problem is somewhat complex as you generally have to align the two axis ranges so that their relative aspects toward zero match. Generally there are nine cases depending of the primary and secondary axis ranges relative position against zero. The following code shows a complete implementation, which also allow to align two ranges to any value (not just zero) and have this value visually matched on the PrimaryX and PrimaryY axes:

    enum ValuePosition
    {
        AboveRange,
        BelowRange,
        ContainedInRange
    }
    public partial class Form1 : Form
    {
        NChart m_Chart;
        NBarSeries m_Bar1;
        NBarSeries m_Bar2;

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            m_Chart = nChartControl1.Charts[0];

            // add a bar series
            m_Bar1 = (NBarSeries)m_Chart.Series.Add(SeriesType.Bar);
            m_Bar1.MultiBarMode = MultiBarMode.Series;
            m_Bar1.DataLabelStyle.Visible = false;

            // add another bar series
            m_Bar2 = (NBarSeries)m_Chart.Series.Add(SeriesType.Bar);
            m_Bar2.MultiBarMode = MultiBarMode.Clustered;
            m_Bar2.DataLabelStyle.Visible = false;

            m_Bar2.DisplayOnAxis(StandardAxis.PrimaryY, false);
            m_Bar2.DisplayOnAxis(StandardAxis.SecondaryY, true);
            m_Chart.Axis(StandardAxis.SecondaryY).Visible = true;

            // turn off rounding to preserve ranges relative disposition towards zero
            NStandardScaleConfigurator primaryYScale = m_Chart.Axis(StandardAxis.PrimaryY).ScaleConfigurator as NStandardScaleConfigurator;
            primaryYScale.RoundToTickMax = false;
            primaryYScale.RoundToTickMin = false;

            NStandardScaleConfigurator secondaryYScale = m_Chart.Axis(StandardAxis.SecondaryY).ScaleConfigurator as NStandardScaleConfigurator;
            secondaryYScale.RoundToTickMax = false;
            secondaryYScale.RoundToTickMin = false;

            NStyleSheet.CreatePredefinedStyleSheet(PredefinedStyleSheet.Nevron).Apply(nChartControl1.Document);
        }

        private NRange1DD GetSeriesMinMax(NBarSeries series)
        {
            return new NRange1DD((double)series.Values[series.Values.FindMinValue()],
                                 (double)series.Values[series.Values.FindMaxValue()]);
        }
        /// <summary>
        ///
        /// </summary>
        /// <param name="pivotValue"></param>
        /// <param name="range1"></param>
        /// <param name="range2"></param>
        private void AlignRangeAspectsToPivot(double pivotValue, ref NRange1DD range1, ref NRange1DD range2)
        {
            // extend ranges to contain pivot
            range1 = ExtendRangeToContainValue(range1, pivotValue);
            range2 = ExtendRangeToContainValue(range2, pivotValue);

            ValuePosition range1Disposition = GetValueDisposition(range1, pivotValue);
            ValuePosition range2Disposition = GetValueDisposition(range2, pivotValue);

            // examine all possible cases of range1 and range2 vs pivotValue
            switch (range1Disposition)
            {
                case ValuePosition.AboveRange:
                    switch (range2Disposition)
                    {
                        case ValuePosition.AboveRange:
                            // both above range => do nothing
                            break;
                        case ValuePosition.BelowRange:
                            {
                                // need to extend both ranges
                                range1.End = pivotValue + range1.GetLength();
                                range2.Begin = pivotValue - range2.GetLength();
                            }
                            break;
                        case ValuePosition.ContainedInRange:
                            {
                                // need to extend range1 end by aspect of range2
                                double aspect = GetRangeAspect(range2, pivotValue);
                                range1 = ExtendRangeWithAspect(range1, aspect, pivotValue);
                            }
                            break;
                    }

                    break;
                case ValuePosition.BelowRange:
                    switch (range2Disposition)
                    {
                        case ValuePosition.AboveRange:
                            {
                                range1.Begin = pivotValue - range1.GetLength();
                                range2.End = pivotValue + range2.GetLength();
                            }
                            break;
                        case ValuePosition.BelowRange:
                            // both below range => do nothing
                            break;
                        case ValuePosition.ContainedInRange:
                            {
                                // need to extend range1 end by aspect of range2
                                double aspect = GetRangeAspect(range2, pivotValue);
                                range1 = ExtendRangeWithAspect(range1, aspect, pivotValue);
                            }
                            break;
                    }

                    break;
                case ValuePosition.ContainedInRange:
                    switch (range2Disposition)
                    {
                        case ValuePosition.AboveRange:
                            {
                                // need to extend range2 end by aspect of range1
                                double aspect = GetRangeAspect(range1, pivotValue);
                                range2 = ExtendRangeWithAspect(range2, aspect, pivotValue);
                            }
                            break;
                        case ValuePosition.BelowRange:
                            {
                                // need to extend range2 end by aspect of range1
                                double aspect = GetRangeAspect(range1, pivotValue);
                                range2 = ExtendRangeWithAspect(range2, aspect, pivotValue);
                            }
                            break;
                        case ValuePosition.ContainedInRange:
                            {
                                // both contain pivot, find the most balanced aspect
                                double aspect1 = GetRangeAspect(range1, pivotValue);
                                double aspect2 = GetRangeAspect(range2, pivotValue);

                                if (Math.Abs(1.0 - aspect1) < Math.Abs(1.0 - aspect2))
                                {
                                    range2 = ExtendRangeWithAspect(range2, aspect1, pivotValue);
                                }
                                else
                                {
                                    range1 = ExtendRangeWithAspect(range1, aspect2, pivotValue);
                                }                               
                            }
                            break;
                    }
                    break;
            }
        }
        /// <summary>
        ///
        /// </summary>
        /// <param name="range"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        private static NRange1DD ExtendRangeToContainValue(NRange1DD range, double value)
        {
            // extend range2 to contain 0
            if (!range.Contains(value))
            {
                if (range.Begin > value)
                {
                    range.Begin = value;
                }
                else if (range.End < value)
                {
                    range.End = value;
                }
            }

            return range;
        }
        /// <summary>
        ///
        /// </summary>
        /// <param name="range"></param>
        /// <param name="pivotValue"></param>
        /// <returns></returns>
        private static double GetRangeAspect(NRange1DD range, double pivotValue)
        {
            Debug.Assert(range.Contains(pivotValue));

            double diffBegin = pivotValue - range.Begin;
            double diffEnd = range.End - pivotValue;

            return diffBegin / diffEnd;
        }
        /// <summary>
        /// Extends the range in such a way that its aspect towards pivot becomes the specified one
        /// </summary>
        /// <param name="range"></param>
        /// <param name="aspect"></param>
        /// <param name="pivotValue"></param>
        /// <returns></returns>
        private static NRange1DD ExtendRangeWithAspect(NRange1DD range, double aspect, double pivotValue)
        {
            Debug.Assert(aspect != 0);
            Debug.Assert(range.Contains(pivotValue));

            double diffBegin = pivotValue - range.Begin;
            double diffEnd = range.End - pivotValue;

            double newDiffBegin = diffBegin;
            double newDiffEnd = diffEnd;

            if (diffBegin == 0)
            {
                // extend begin
                newDiffBegin = aspect * diffEnd;
            }
            else if (diffEnd == 0)
            {
                // extend end
                newDiffEnd = diffBegin / aspect;
            }
            else
            {
                double aspect1 = diffBegin / diffEnd;

                if (aspect > aspect1)
                {
                    newDiffEnd = diffBegin / aspect;
                }
                else
                {
                    newDiffBegin = aspect * diffEnd;
                }
            }

            return new NRange1DD(pivotValue - newDiffBegin, pivotValue + newDiffEnd);
        }
        /// <summary>
        ///
        /// </summary>
        /// <param name="range"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        private static ValuePosition GetValueDisposition(NRange1DD range, double value)
        {
            // determine begin value disposition
            if (value <= range.Begin)
            {
                return ValuePosition.BelowRange;
            }
            else if (value >= range.End)
            {
                return ValuePosition.AboveRange;
            }
            else
            {
                return ValuePosition.ContainedInRange;
            }
        }
        /// <summary>
        /// Inflates the range with the specified factor
        /// </summary>
        /// <param name="factor"></param>
        /// <returns></returns>
        private static NRange1DD InflateRangeByFactor(NRange1DD range, double factor)
        {
            return new NRange1DD(range.Begin * factor, range.End * factor);
        }

        private void button1_Click(object sender, EventArgs e)
        {
            nChartControl1.ShowEditor();
        }

        private void button2_Click(object sender, EventArgs e)
        {
            Random random = new Random();

            int caseRange1 = random.Next(3);
            int caseRange2 = random.Next(3);

            switch (caseRange1)
            {
                case 0:
                    // above
                    m_Bar1.Values.FillRandomRange(random, 5, 0, 100);
                    break;
                case 1:
                    // below
                    m_Bar1.Values.FillRandomRange(random, 5, -100, 0);
                    break;
                case 2:
                    m_Bar1.Values.FillRandomRange(random, 5, -100, 100);
                    break;
                default:
                    Debug.Assert(false);
                    break;
            }

            switch (caseRange2)
            {
                case 0:
                    m_Bar2.Values.FillRandomRange(random, 5, 0, 500);
                    break;
                case 1:
                    m_Bar2.Values.FillRandomRange(random, 5, -500, 0);
                    break;
                case 2:
                    m_Bar2.Values.FillRandomRange(random, 5, -500, 200);
                    break;
                default:
                    Debug.Assert(false);
                    break;
            }

            // pivot value 0
            double pivotValue = 0.0;

            // get series min/max values and form a normalized range
            NRange1DD range1 = GetSeriesMinMax(m_Bar1);
            NRange1DD range2 = GetSeriesMinMax(m_Bar2);

            AlignRangeAspectsToPivot(pivotValue, ref range1, ref range2);

            range1 = InflateRangeByFactor(range1, 1.1);
            range2 = InflateRangeByFactor(range2, 1.1);

            m_Chart.Axis(StandardAxis.PrimaryY).View = new NRangeAxisView(range1, true, true);
            m_Chart.Axis(StandardAxis.SecondaryY).View = new NRangeAxisView(range2, true, true);

            nChartControl1.Refresh();
        }
    }

Hope this helps - I've commented the code a bit, but if you have any questions let me know...

Best regards,
Bob



inhyuk son
Posted 14 Years Ago
View Quick Profile
Junior Member

Junior Member (21 reputation)Junior Member (21 reputation)Junior Member (21 reputation)Junior Member (21 reputation)Junior Member (21 reputation)Junior Member (21 reputation)Junior Member (21 reputation)Junior Member (21 reputation)Junior Member (21 reputation)

Group: Forum Members
Last Active: 12 Years Ago
Posts: 21, Visits: 1
Thank you for your help.
It's a great help.




Similar Topics


Reading This Topic