This is the discussion forum for OxyPlot.
For bugs and new features, use the issue tracker located at GitHub.
Also try the chat room!
0

plotarea background

sunil joshi 10 років тому оновлений 9 років тому 2
can i fill plot area with gradient color.

Track all the series same time?

Oystein Bjorke 10 років тому 0
This discussion was imported from CodePlex

aapov wrote at 2014-01-06 20:36:

Hi and thanks for exellent project!

When I activate the tracker, it shows only values from one serie. Is it possible to show values of all the series which vertical tracker crosses? I could do this outside of plot control, but how do I get the X-axis value of current tracker?

objo wrote at 2014-01-07 22:14:

I think this is issue
https://oxyplot.codeplex.com/workitem/10017
and not yet implemented

I think you can get the x-axis value from the TrackerChanged event!

aapov wrote at 2014-01-08 19:30:

Thanks, TrackerChanged event is exactly what I was looking for!

Tracker funktional question

Oystein Bjorke 10 років тому 0
This discussion was imported from CodePlex

Archer25 wrote at 2014-04-30 10:40:

Hello i need help
Ihave a scatterSeries. And i need to for every point's have unique string lines, and this lines must show at the tracker. And how send the tracker show position???
How i can do that???

Image

Scatter series on heat map

Oystein Bjorke 10 років тому 0
This discussion was imported from CodePlex

BlindMonk wrote at 2014-01-20 17:00:

Is it possible to display points on a bitmap/polarheatmap/bitmap series?
These points are dynamic so the background image remains the same, but the overlay points change.

any leads please.

thanks

objo wrote at 2014-01-27 19:21:

Yes, you can add both a HeatMapSeries and a ScatterSeries (based on different data) to the same plot model.
Performance for the heat map can be an issue if you need a high refresh rate for the dynamic points.

CSharpdevs wrote at 2014-05-15 14:13:

I added both heat map series and scatter series to the same plot model. but only heat map is shown on the plot model. What is reason of the problem?
The code is given below:

PlotModel pm ;
pm = new PlotModel();

OxyPlot.Axes.LinearColorAxis lca = new OxyPlot.Axes.LinearColorAxis();
        lca.HighColor = OxyColors.Gray;
        lca.LowColor = OxyColors.Black;
        lca.Position = OxyPlot.Axes.AxisPosition.Right;
        pm.Axes.Add(lca);

        OxyPlot.Axes.LinearAxis la = new OxyPlot.Axes.LinearAxis();
        la.Position = OxyPlot.Axes.AxisPosition.Bottom;
        pm.Axes.Add(la);

        OxyPlot.Series.HeatMapSeries hm = new OxyPlot.Series.HeatMapSeries();
        hm.Interpolate = true;
        hm.X0 = 0;
        hm.X1 = 8;
        hm.Y0 = 0;
        hm.Y1 = 8;

        hm.Data = new double[9, 9];
        hm.Data[3, 5] = 5;
        hm.Data[4 ,5] = 5;
        hm.Data[2, 4] = 5;
        hm.Data[5, 4] = 5;
        hm.Data[3, 3] = 5;
        hm.Data[4, 3] = 5;

        hm.Data[3, 7] = 15;
        hm.Data[4, 7] = 15;
        hm.Data[6, 6] = 15;
        hm.Data[7, 4] = 15;
        hm.Data[6, 2] = 15;
        hm.Data[4, 1] = 15;
        hm.Data[2, 2] = 15;
        hm.Data[1, 4] = 15;
        hm.Data[2, 6] = 15;

        pm.Series.Add(hm);
OxyPlot.Series.ScatterSeries sc = new OxyPlot.Series.ScatterSeries();
        sc.BinSize = 8;
        sc.MarkerStrokeThickness = 3;
        sc.MarkerType = MarkerType.Circle;
        sc.MarkerFill = OxyColors.Black;

        sc.Points.Add(new OxyPlot.Series.ScatterPoint(0, 0, 5));
        sc.Points.Add(new OxyPlot.Series.ScatterPoint(3, 1, 2));
        sc.Points.Add(new OxyPlot.Series.ScatterPoint(5, 5, 5));
        sc.Points.Add(new OxyPlot.Series.ScatterPoint(1, 5, 7));
        sc.Points.Add(new OxyPlot.Series.ScatterPoint(2, 2, 5));
        sc.Points.Add(new OxyPlot.Series.ScatterPoint(8, 8, 5));

        pm.Series.Add(sc);
plot1.Model = pm;

nickn3710 wrote at 2014-05-27 22:23:

Hi, I am working on a similar project where the HeatMapSeries changes vary rarely, but a FunctionSeries on the same plot will be changing rapidly (ideally 5-10 times/second). I am currently using WinForms. To test the update rates, I'm using a simple timer that cycles through different FunctionSeries that get overlaid on top of the HeatMapSeries. As I understand it, when I call "plotView1.InvalidatePlot(true)" it will re-draw both the HeatMapSeries and the FunctionSeries. Is there any way to get it to only redraw/render only the FunctionSeries? Would this be significantly faster if I were using WPF?

CSharpdevs wrote at 2014-05-30 13:54:

I dont think that the WPF works faster than Windows form application. Not too long a time interval of 100 ms. The standart timer control is used to the process. But a sample rate with more precise time is required, (For example 1-2 milisecond), I recommend that you can use the milisecond class library that is a class written by C# language. the sample rate of standart timer control is not stable under the 10 ms. I have been using the class in windows form application to obtain a sample rate of 2 ms. Additionaly, If you want to give priority to the work of some functions,you should use multi-threading programming. The microlibrary class is below;

Best regards.

using System;

namespace MicroLibrary
{
/// <summary>
/// MicroStopwatch class
/// </summary>
public class MicroStopwatch : System.Diagnostics.Stopwatch
{
    readonly double _microSecPerTick =
        1000000D / System.Diagnostics.Stopwatch.Frequency;

    public MicroStopwatch()
    {
        if (!System.Diagnostics.Stopwatch.IsHighResolution)
        {
            throw new Exception("On this system the high-resolution " +
                                "performance counter is not available");
        }
    }

    public long ElapsedMicroseconds
    {
        get
        {
            return (long)(ElapsedTicks * _microSecPerTick);
        }
    }
}

/// <summary>
/// MicroTimer class
/// </summary>
public class MicroTimer
{
    public delegate void MicroTimerElapsedEventHandler(
                         object sender,
                         MicroTimerEventArgs timerEventArgs);
    public event MicroTimerElapsedEventHandler MicroTimerElapsed;

    System.Threading.Thread _threadTimer = null;
    long _ignoreEventIfLateBy = long.MaxValue;
    long _timerIntervalInMicroSec = 0;
    bool _stopTimer = true;

    public MicroTimer()
    {
    }

    public MicroTimer(long timerIntervalInMicroseconds)
    {
        Interval = timerIntervalInMicroseconds;
    }

    public long Interval
    {
        get
        {
            return System.Threading.Interlocked.Read(
                ref _timerIntervalInMicroSec);
        }
        set
        {
            System.Threading.Interlocked.Exchange(
                ref _timerIntervalInMicroSec, value);
        }
    }

    public long IgnoreEventIfLateBy
    {
        get
        {
            return System.Threading.Interlocked.Read(
                ref _ignoreEventIfLateBy);
        }
        set
        {
            System.Threading.Interlocked.Exchange(
                ref _ignoreEventIfLateBy, value <= 0 ? long.MaxValue : value);
        }
    }

    public bool Enabled
    {
        set
        {
            if (value)
            {
                Start();
            }
            else
            {
                Stop();
            }
        }
        get
        {
            return (_threadTimer != null && _threadTimer.IsAlive);
        }
    }

    public void Start()
    {
        if (Enabled || Interval <= 0)
        {
            return;
        }

        _stopTimer = false;

        System.Threading.ThreadStart threadStart = delegate()
        {
            NotificationTimer(ref _timerIntervalInMicroSec,
                              ref _ignoreEventIfLateBy,
                              ref _stopTimer);
        };

        _threadTimer = new System.Threading.Thread(threadStart);
        _threadTimer.Priority = System.Threading.ThreadPriority.Highest;
        _threadTimer.Start();
    }

    public void Stop()
    {
        _stopTimer = true;
    }

    public void StopAndWait()
    {
        StopAndWait(System.Threading.Timeout.Infinite);
    }

    public bool StopAndWait(int timeoutInMilliSec)
    {
        _stopTimer = true;

        if (!Enabled || _threadTimer.ManagedThreadId ==
            System.Threading.Thread.CurrentThread.ManagedThreadId)
        {
            return true;
        }

        return _threadTimer.Join(timeoutInMilliSec);
    }

    public void Abort()
    {
        _stopTimer = true;

        if (Enabled)
        {
            _threadTimer.Abort();
        }
    }

    void NotificationTimer(ref long timerIntervalInMicroSec,
                           ref long ignoreEventIfLateBy,
                           ref bool stopTimer)
    {
        int  timerCount = 0;
        long nextNotification = 0;

        MicroStopwatch microStopwatch = new MicroStopwatch();
        microStopwatch.Start();

        while (!stopTimer)
        {
            long callbackFunctionExecutionTime =
                microStopwatch.ElapsedMicroseconds - nextNotification;

            long timerIntervalInMicroSecCurrent =
                System.Threading.Interlocked.Read(ref timerIntervalInMicroSec);
            long ignoreEventIfLateByCurrent =
                System.Threading.Interlocked.Read(ref ignoreEventIfLateBy);

            nextNotification += timerIntervalInMicroSecCurrent;
            timerCount++;
            long elapsedMicroseconds = 0;

            while ( (elapsedMicroseconds = microStopwatch.ElapsedMicroseconds)
                    < nextNotification)
            {
                System.Threading.Thread.SpinWait(10);
            }

            long timerLateBy = elapsedMicroseconds - nextNotification;

            if (timerLateBy >= ignoreEventIfLateByCurrent)
            {
                continue;
            }

            MicroTimerEventArgs microTimerEventArgs =
                 new MicroTimerEventArgs(timerCount,
                                         elapsedMicroseconds,
                                         timerLateBy,
                                         callbackFunctionExecutionTime);
            MicroTimerElapsed(this, microTimerEventArgs);
        }

        microStopwatch.Stop();
    }
}

/// <summary>
/// MicroTimer Event Argument class
/// </summary>
public class MicroTimerEventArgs : EventArgs
{
    // Simple counter, number times timed event (callback function) executed
    public int  TimerCount { get; private set; }

    // Time when timed event was called since timer started
    public long ElapsedMicroseconds { get; private set; }

    // How late the timer was compared to when it should have been called
    public long TimerLateBy { get; private set; }

    // Time it took to execute previous call to callback function (OnTimedEvent)
    public long CallbackFunctionExecutionTime { get; private set; }

    public MicroTimerEventArgs(int  timerCount,
                               long elapsedMicroseconds,
                               long timerLateBy,
                               long callbackFunctionExecutionTime)
    {
        TimerCount = timerCount;
        ElapsedMicroseconds = elapsedMicroseconds;
        TimerLateBy = timerLateBy;
        CallbackFunctionExecutionTime = callbackFunctionExecutionTime;
    }
}
}

Clicking on an annotation

Oystein Bjorke 10 років тому 0
This discussion was imported from CodePlex

Slxe wrote at 2014-05-01 17:46:

Hello! Just wanted to start by saying thanks for all the hard work on this awesome project. I'm hoping to move our current heavy chart implementation over to it in the coming months, just working on various demos to impress people that don't care about the code side of things.

I've read through the code for the various mouse event examples but haven't had one that covers my issue. I'm looking to dynamically add and remove events to annotations, which means I've had to implement them as methods instead of lambdas.

The issue I'm running into now is how to actually get the annotation clicked on from what's passed to the method. I haven't found a way to access it from the PlotController or OxyMouseDownEventArgs.

Any suggestions are much appreciated! It wouldn't be the first time I'd missed something obvious hehe. Hopefully when I'm done this I'll have something interesting to add to the WinForms demos and help other people out.

Slxe wrote at 2014-05-09 17:13:

I guess due to lack of response there's no way to do this right now unless I want to create my own Annotation or a similar strategy?

objo wrote at 2014-05-10 22:54:

Thanks for pointing us to this issue! I think the implementation of the EventHandler<T> pattern was wrong. The On* methods in the UIPlotElement should not take the sender as a parameter. Also, I see it is convenient to include the element that is hit in the HitTestResults.
I am submitting a change that should fix this. I have added two examples that should be close to what you need. Have a look at the source code!

Slxe wrote at 2014-05-12 16:07:

Hey! Thanks for the answer, sorry for taking a few days to respond, but I wanted to while I had my code in front of me.

Yea I noticed HitTestResults only had the item and index, it didn't actually have a reference to the element clicked on, so I was hoping to find another solution. Thanks for the examples and the fix, this is exactly what I was hoping for! I'm playing around with the idea of adding a drawing toolbar to the chart display to allow the users to draw annotations onto it without having to save it and editing it in paint, with the added bonus of having things drawn be below the series I figured it'd quite helpful and faster than what they're doing now for their reports. Hopefully when I'm done I can cut it up and submit it as an example too.

Now I just have to find a nice way to sync the Axes when the user Zooms. I've got it working for just plain zooming (and panning), but I had to disable mouse wheel and +/- zooming as using ZoomAt throws it all to hell (I'm essentially transforming the actual minimum and maximum points to a screen point, then back to points on the new axes). I noticed the wdf example, but it doesn't help much when the axes have very different data on them. Would be nice if the AxisChanged event somehow had access to the pan/zoom amounts.

Thanks again for the help objo.

objo wrote at 2014-05-12 16:34:

Yes, please create an example with an annotation toolbar. I am sure this can be a popular example.

We have a feature request for synchronization of the axes: https://oxyplot.codeplex.com/workitem/9945
I am not sure if this covers your case, I don't quite understand your usage.
It should be easy to add pan/zoom deltas in the axis changed event arguments, but I have not yet seen a need for this.

Slxe wrote at 2014-05-12 17:20:

Yea I saw that request while looking around for a solution. Basically I just want to be able to zoom into an area using the Zoom Rectangle feature, and have all axes change their min and max for the area zoomed into, instead of just the primary vertical and horizontal axes. Like I said I'm currently doing it like so:
private void axis_AxisChanged(object sender, AxisChangedEventArgs args)
{
    if (_syncChange || ((Axis) sender).IsHorizontal())
        return;
    
    _syncChange = true;
    switch (args.ChangeType)
    {
        case AxisChangeTypes.Zoom:
        case AxisChangeTypes.Pan:
            _pauseChartUpdate = true;
            foreach (Axis a in mainPlot.Model.Axes)
            {
                if (a.Key == ((Axis) sender).Key || a.IsHorizontal())
                    continue;

                double min = a.InverseTransform(((Axis) sender).Transform(((Axis) sender).ActualMinimum));
                double max = a.InverseTransform(((Axis) sender).Transform(((Axis) sender).ActualMaximum));

                a.Zoom(min, max);
            }
            break;

        case AxisChangeTypes.Reset:
            _pauseChartUpdate = false;
            break;
    }

    mainPlot.InvalidatePlot(false);
    _syncChange = false;
}
Ignore the _pauseChartUpdate bool, since this is being used for real time data, they wanted two different kinds of zooms, essentially one that pauses the chart when zoomed in on an area, and one that you can actually watch the chart update in the zoomed area, and I'm still playing with it.

But yea, this is basically how I'm handling syncing panning and zooming to the other axes. Probably not the best way to do it, but I'm still learning, and it works. This is why I mentioned it'd be nice for AxisChangedEventArgs to have the various zoom and pan values so if required it can transfer them to other axes (a way to tell the difference between Zoom and ZoomAt, basically points vs factor, would be nice too). Although because of how I've done this, if you use a zoom factor instead of screen points it doesn't translate properly to the other axes (the primary axes goes out of sync with the others).

guilhermecgs wrote at 2014-05-16 23:56:

I´m always getting HitTestResult.Item = null on a ScatterSeries MouseDown Event. Is it related to this discussion. (I´m using the latest version)

Polar chart question

Oystein Bjorke 10 років тому 0
This discussion was imported from CodePlex

SamTauris wrote at 2014-06-27 12:35:

Hi

I'm struggling with following problem.
Hope you can help me out. :) !
I try to achive something similar to

Image

On my case I need one series per line, and have the first point of every series be the center of the chart.

Is this possible?

objo wrote at 2014-06-27 13:09:

If using series is a requirement I think you need to create a custom series drawing the arrows. Otherwise I guess the ArrowAnnotation can be used.
If you want to use one series per line, you must specify the colour of each series. Otherwise, the default colour will be different for each series.
The position of the magnitude axis labels is not yet customizable. This is covered by https://oxyplot.codeplex.com/workitem/10223
See the latest code checkin for an example that is close to your case!

SamTauris wrote at 2014-06-27 14:05:

Objo

Thanks for the FAST response ! It works!!

NuGet packages

Oystein Bjorke 10 років тому 0
This discussion was imported from CodePlex

objo wrote at 2014-03-13 07:10:

sorry, the NuGet packages are currently a bit out of sync. Will correct this later today or tomorrow.

simple TextAnnotation

Oystein Bjorke 10 років тому 0
This discussion was imported from CodePlex

heromyth wrote at 2011-11-03 08:12:

I need some text annotations for my curves, so I implemented a simple TextAnnotation.

 

    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            var pm = new PlotModel("Trigonometric functions", "Example using the FunctionSeries")
                         {
                             PlotType = PlotType.Cartesian,
                             Background = OxyColors.White
                         };
            pm.Series.Add(new FunctionSeries(Math.Sin, -10, 10, 0.1, "sin(x)"));
            pm.Series.Add(new FunctionSeries(Math.Cos, -10, 10, 0.1, "cos(x)"));
            pm.Series.Add(new FunctionSeries(t => 5 * Math.Cos(t), t => 5 * Math.Sin(t), 0, 2 * Math.PI, 0.1, "cos(t),sin(t)"));

            TextAnnotation ta = new TextAnnotation();
            DataPoint dp = new DataPoint();
            dp.X = 1;
            dp.Y = 3.5;
            ta.addPoint(dp);

            dp = new DataPoint();
            dp.X = 21;
            dp.Y = 4.5;
            ta.addPoint(dp);
            pm.Annotations.Add(ta);

            plot1.Model = pm;
        }
   }
    public class TextAnnotation : Annotation
    {
        private IList<IDataPoint> dataPoints;

        public void addPoint(IDataPoint dp)
        {
            dataPoints.Add(dp);
        }

        /// <summary>
        ///   Vertical alignment of text (above or below the line).
        /// </summary>
        public VerticalTextAlign TextVerticalAlignment { get; set; }

        /// <summary>
        ///   Horizontal alignment of text.
        /// </summary>
        public HorizontalTextAlign TextHorizontalAlignment { get; set; }


        public override void Render(IRenderContext rc, PlotModel model)
        {
            double angle = 0;
            ScreenPoint position = new ScreenPoint();

            foreach (IDataPoint dp in dataPoints)
            {

                if (dp.X > this.XAxis.ActualMaximum || dp.X < this.XAxis.ActualMinimum)
                    continue;
                if (dp.Y > this.YAxis.ActualMaximum || dp.Y < this.YAxis.ActualMinimum)
                    continue;

                position.X = this.XAxis.Transform(dp.X);
                position.Y = this.YAxis.Transform(dp.Y);

                
                string text = dp.Y.ToString("F1");
                rc.DrawText(
                    position,
                    text,
                    model.TextColor,
                    model.ActualAnnotationFont,
                    model.AnnotationFontSize,
                    FontWeights.Normal,
                    angle,
                    this.TextHorizontalAlignment,
                    this.TextVerticalAlignment);
            }
        }

        public TextAnnotation()
        {
            this.TextHorizontalAlignment = HorizontalTextAlign.Center;
            this.TextVerticalAlignment = VerticalTextAlign.Top;
            dataPoints = new List<IDataPoint>(50);
        }
    }


objo wrote at 2011-11-03 22:53:

cool, I will add a TextAnnotation class, but I think we should make it a bit more general (not only showing Y value).

I think the input properties should be a single point, horizontal/vertical alignment and a text string. 

Where is YAxisKey used and how

Oystein Bjorke 10 років тому 0
This discussion was imported from CodePlex

Ambron wrote at 2014-03-06 06:12:

I see plenty reference to YAxisKey, but I cannot find an implemented series that have this property; what am I missing?

maddog56 wrote at 2014-03-11 01:54:

Ambron,

The key is used to link (or key) an axis to a series. This will insure that the series all appear in the plot area. In this example I have 3 x-axes and 1 y-axis in a single plot, with the x-axes at the bottom of the chart laid in "tiers". You can see how I used the key property.

For i = 1 To 3
Dim ax As LinearAxis
ax = New LinearAxis(AxisPosition.Bottom, 0, 50*i, "Title")
ax.Key = "key" & i.ToString
ax.PositionTier = i - 1
ax.IsZoomEnabled = False

ax.Font = "Trebuchet MS"
ax.FontSize = 12.0
ax.FontWeight = OxyPlot.FontWeights.Bold

ax.TitleFont = "Trebuchet MS"
ax.TitleFontSize = 12.0
ax.TitleFontWeight = OxyPlot.FontWeights.Bold

myModel.Axes.Add(ax)
Next

For i = 1 To 3
Dim lineSeries0 As New LineSeries()
lineSeries0.Title = "Title"

lineSeries0.Color = OxyColors.Black
lineSeries0.StrokeThickness = 1.25

lineSeries0.MarkerFill = OxyColors.Red
lineSeries0.MarkerSize = 5.0
lineSeries0.MarkerStroke = OxyColors.DarkGray
lineSeries0.MarkerStrokeThickness = 1.0
lineSeries0.MarkerType = MarkerType.Circle

For j = 0 To dataseriesC.Points.Count - 1
 dp = dataseriesC.Points(j)
 lineSeries0.Points.Add(dp)
Next

lineSeries0.CanTrackerInterpolatePoints = False
lineSeries0.XAxisKey = "key" & i.ToString
myModel.Series.Add(lineSeries0)
Next

Ambron wrote at 2014-03-11 03:55:

That is indeed how I understood it should work.
I probably should have added some more detail; in the .Net OxyPlot assemblies (notably OxyPlot.Wpf ) there is no XAxisKey property for a lineSeries
Hence the question, what am I missing?

Ambron wrote at 2014-03-11 04:01:

To elaborate:

I have a ViewModel that creates an

var dateAxis = new OxyPlot.Axes.DateTimeAxis(AxisPosition.Bottom, "Time", "HH:mm")
        {
            MajorGridlineStyle = LineStyle.Solid,
            MinorGridlineStyle = LineStyle.Dot,
            IntervalLength = 80
        };
        var tempAxis = new OxyPlot.Axes.LinearAxis(AxisPosition.Left, "Temperature") {Key = "Temp"};
        var humAxis = new OxyPlot.Axes.LinearAxis(AxisPosition.Right, "Humidity") {Key = "Hum"}; 


        model.Axes.Add(dateAxis);
        model.Axes.Add(tempAxis);
        model.Axes.Add(humAxis); 

Ambron wrote at 2014-03-11 04:10:

apologies, browser playing up.. So I create the OxyModel in a ViewModel which I den populate with line-series, based on the location and whether it is a temperature or Humidity.

so when the data comes in I add the points with something like this, where I would want to use YAxisKey

private static void AddNamedValue(PlotModel plotModel, string name, DateTime time, decimal value)
    {
        var s = plotModel.Series.FirstOrDefault(ms => ms.Title == name);
        if (s == null)
        {
            s = new OxyPlot.Series.LineSeries { Title = name };
            //if (name.StartsWith("Hum"))
            //    s.YAxisKey = "Hum";
            plotModel.Series.Add(s);
        }
        var ls = s as OxyPlot.Series.LineSeries;
        ls.Points.Add(new DataPoint(OxyPlot.Axes.DateTimeAxis.ToDouble(time), (Double)value));
    } 

Tracker Position in Winforms

Oystein Bjorke 10 років тому 0
This discussion was imported from CodePlex

Tockz wrote at 2014-04-03 06:20:

Hi,

Just started using oxyplot for winforms and so far so good.

The one issue I'm having is that on a column graph it seems the tracker position is not where I click. (see image below)
Image


I clicked on the middle column, and as you can see the tracker displays left and up of where I clicked

Am I doing something wrong?

Thanks

objo wrote at 2014-04-03 08:51:

The tracker should be shown at the mouse position. It works in the Winforms example browser. Can you create a simple Winforms example that reproduces this bug?

Tockz wrote at 2014-04-04 00:50:

Here's an example

Tracker Winforms Example

On further testing, it seems to be in relation to where you place the plot control relative to other controls. If other controls are placed to the left of the plot, the tracker seems to be still working of the form borders, not where the plot is.

However if you place the plot inside a container, like a group box, it seems to work again.

Regards