I've started using LightningChart in my real time monitoring application. In my app there are many y axis which use segmented layout (one y axis per segment):
mainChart.ViewXY.AxisLayout.YAxesLayout = YAxesLayout.Segmented;
My goal is that when you mouse click a segment, it gets larger compared to other segments (kinda like zoom effect) and the other segments gets smaller. When you click it again it goes back to normal.
I know I can change the size of the segments with:
mainChart.ViewXY.AxisLayout.Segments[segmentNumber].Height = someValue;
That takes care of the zooming effect.
Now the problem is that how can I solve which segment was actually clicked? I figured out that you get mouse position via MouseClick -event (e.MousePos) but that seem to give only the screen coordinates so i'm not sure that it helps.
I'm using the LightningChart version 8.4.2
You are correct that getting mouse position via MouseClick event is the key here. The screen coordinates you get via e.GetPosition (not e.MousePos) can be converted to chart axis values with CoordToValue() -method. Then you just compare the y-coordinate to each y-axis minimum/maximum value to find out what segment was clicked. Here is an example:
_chart.MouseClick += _chart_MouseClick;
private void _chart_MouseClick(object sender, MouseButtonEventArgs e)
{
var mousePos = e.GetPosition(_chart).Y;
double axisPos = 0;
bool isWithinYRange = false;
foreach (AxisY ay in _chart.ViewXY.YAxes)
{
ay.CoordToValue((float)mousePos, out axisPos, true);
if (axisPos >= ay.Minimum && axisPos <= ay.Maximum)
{
// Segment clicked, get the index via ay.SegmentIndex;
isWithinYRange = true;
}
}
if (!isWithinYRange)
{
// Not in any segment
}
}
After finding out the segment index, you can modify its height as you described:
_chart.ViewXY.AxisLayout.Segments[0].Height = 1.5;
Note Height means segment height compared to other segments.
Hope this is helpful.
Related
In the app I'm trying to develop a key part is getting the position of where the user has touched. First I thought of using a tap gesture recognizer but after a quick google search I learned that was useless (See here for an example).
Then I believe I discovered SkiaSharp and after learning how to use it, at least somewhat, I'm still not sure how I get the proper coordinates of a touch. Here are sections of the code in my project that are relevant to the problem.
Canvas Touch Function
private void canvasView_Touch(object sender, SKTouchEventArgs e)
{
// Only carry on with this function if the image is already on screen.
if(m_isImageDisplayed)
{
// Use switch to get what type of action occurred.
switch (e.ActionType)
{
case SKTouchAction.Pressed:
TouchImage(e.Location);
// Update simply tries to draw a small square using double for loops.
m_editedBm = Update(sender);
// Refresh screen.
(sender as SKCanvasView).InvalidateSurface();
break;
default:
break;
}
}
}
Touch Image
private void TouchImage(SKPoint point)
{
// Is the point in range of the canvas?
if(point.X >= m_x && point.X <= (m_editedCanvasSize.Width + m_x) &&
point.Y >= m_y && point.Y <= (m_editedCanvasSize.Height + m_y))
{
// Save the point for later and set the boolean to true so the algorithm can begin.
m_clickPoint = point;
m_updateAlgorithm = true;
}
}
Here I'm just seeing or TRYING to see if the point clicked was in range of the image and I made a different SkSize variable to help. Ignore the boolean, not that important.
Update function (function that attempts to draw ON the point pressed so it's the most important)
public SKBitmap Update(object sender)
{
// Create the default test color to replace current pixel colors in the bitmap.
SKColor color = new SKColor(255, 255, 255);
// Create a new surface with the current bitmap.
using (var surface = new SKCanvas(m_editedBm))
{
/* According to this: https://learn.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/graphics/skiasharp/paths/finger-paint ,
the points I have to start are in Xamarin forms coordinates, but I need to translate them to SkiaSharp coordinates which are in
pixels. */
Point pt = new Point((double)m_touchPoint.X, (double)m_touchPoint.Y);
SKPoint newPoint = ConvertToPixel(pt);
// Loop over the touch point start, then go to a certain value (like x + 100) just to get a "block" that's been altered for pixels.
for (int x = (int)newPoint.X; x < (int)newPoint.X + 200.0f; ++x)
{
for (int y = (int)newPoint.Y; y < (int)newPoint.Y + 200.0f; ++y)
{
// According to the x and y, change the color.
m_editedBm.SetPixel(x, y, color);
}
}
return m_editedBm;
}
}
Here I'm THINKING that it'll start, you know, at the coordinate I pressed (and these coordinates have been confirmed to be within the range of the image thanks to the function "TouchImage". And when it does get the correct coordinates (or at least it SHOULD of done that) the square will be drawn one "line" at a time. I have a game programming background so this kind of sounds simple but I can't believe I didn't get this right the first time.
Also I have another function, it MIGHT prove worthwhile because the original image is rotated and then put on screen. Why? Well by default the image, after taking the picture, and then displayed, is rotated to the left. I had no idea why but I corrected it with the following function:
// Just rotate the image because for some reason it's titled 90 degrees to the left.
public static SKBitmap Rotate()
{
using (var bitmap = m_bm)
{
// The new ones width IS the old ones height.
var rotated = new SKBitmap(bitmap.Height, bitmap.Width);
using (var surface = new SKCanvas(rotated))
{
surface.Translate(rotated.Width, 0.0f);
surface.RotateDegrees(90);
surface.DrawBitmap(bitmap, 0, 0);
}
return rotated;
}
}
I'll keep reading and looking up stuff on what I'm doing wrong, but if any help is given I'm grateful.
So im trying to get mouse coordinates from a click on an image, and it gives the wrong coordinates. When i move the mouse to draw, the line appears away from the cursor.
This is the code i use to get the mouse coordinates:
private void ponaredek_MouseDown(object sender, MouseButtonEventArgs e)
{
mouseDown = true;
//x1 = System.Windows.Forms.Control.MousePosition;
x1 = new System.Drawing.Point((int)e.GetPosition(this).X, (int)e.GetPosition(this).Y);
}
x1 is of type System.Drawing.Point (i need the point from drawing, to use in emgucv). What do i have to do to correct the cursor location (i drew where the cursor was)
You want to get the mouse position relative to the Image element, not the Window. So replace
e.GetPosition(this)
by
e.GetPosition((IInputElement)sender)
or
e.GetPosition(ponaredek)
if that is the Image element.
It should look like this:
var pos = e.GetPosition((IInputElement)sender);
x1 = new System.Drawing.Point(pos.X, pos.Y);
Also make sure the Image element's Stretch property is set to None.
I'm working on an application where several GUI Label display names of planes.
And here's the result :
The problem is, if I rotate my camera by 180 °, those label are here, like there is a point symmetry :
So my label appear twice, once on the plane, which is good, and a second time, behind the camera.
I check if my script was not added twice, but there is no problem, more strange, if I look from an above view, the problem just disappear :
I have no idea where this can come from, here's my code, who is attached to every plane :
void OnGUI()
{
if (showInfos)
{
Rect r = new Rect((Camera.main.WorldToScreenPoint(gameObject.transform.position)).x+25, Camera.main.pixelHeight - (Camera.main.WorldToScreenPoint(gameObject.transform.position)).y+25, 75f, 75f);
GUI.Label(r, gameObject.transform.root.name);
}
}
You are drawing the labels whether or not they are in the view frustum or not.
From Camera.WorldToScreenPoint (emphasis mine):
Screenspace is defined in pixels. The bottom-left of the screen is (0,0); the right-top is (pixelWidth,pixelHeight). The z position is in world units from the camera.
You need to check if the Z value of the screen point is negative or positive (I don't know which one is in front of cam and which one is behind it, I don't use Unity), and according to that decide if it needs to be rendered or not.
void OnGUI()
{
if (showInfos)
{
var pt = Camera.main.WorldToScreenPoint(gameObject.transform.position);
if (pt.z > 0) //or < 0, no idea.
{
Rect r = new Rect(pt.x + 25, Camera.main.pixelHeight - pt.y + 25, 75f, 75f);
GUI.Label(r, gameObject.transform.root.name);
}
}
}
Is there any way I can click on a MSChart and drag it left/right in order to scroll the chart to the right/left.
The idea is to drag the chart instead of dragging the chart's scroll bar.
The reason I'm not simply using the scroll bar is because I was asked to add this feature (bosses and their demands..).
Btw, I'm using C# and winforms.
Hope I was clear enough.
Thanks!!
Yes this is possible.
One issue is that you can't use the normal zooming/scrolling mechanism as this itself works by dragging over the chart.
Instead pass it by and code two mouseevents and use the Axis.PixelPositionToValue function to access the data values.
First we store the data value of the clicked location:
double mDown = double.NaN;
private void chart1_MouseDown(object sender, MouseEventArgs e)
{
Axis ax = chart1.ChartAreas[0].AxisX;
mDown = ax.PixelPositionToValue(e.Location.X);
}
Then we calculate the old range how far the Axis.Minimum has moved and adapt Minimum and Maximum:
private void chart1_MouseMove(object sender, MouseEventArgs e)
{
if (!e.Button.HasFlag(MouseButtons.Left)) return;
Axis ax = chart1.ChartAreas[0].AxisX;
double range = ax.Maximum - ax.Minimum;
double xv = ax.PixelPositionToValue(e.Location.X);
ax.Minimum -= xv - mDown;
ax.Maximum = ax.Minimum + range;
}
You will want to add a few checks to prevent dragging too far left or right..
Note that the code assumes that you have set Minimum and Maximum!
Also note that you will want to take control over the format of the x-axis labels so they don't make the chart jump around by having lot of fractional digits.
Or, depending on your data, you may want to set the step to a multiple of some value, suitable for your interval or in my case simply to ints:
ax.Minimum -= (int)(xv - mDown) ;
If you need to have the scrollbars visible you can still use the above code; but you need to replace this
ax.Minimum -= (int)(xv - mDown);
ax.Maximum = ax.Minimum + range;
by this:
double oldPos = ax.ScaleView.Position;
ax.ScaleView.Position -= (xv - mDown);
However this does interfer with the normal scolling. You can insert checks to make sure you are not hitting the scrollbar like this:
RectangleF ipar = InnerPlotPositionClientRectangle(chart1, chart1.ChartAreas[0]);
if (ipar.Contains(e.Location))
{
..
..
..but it still acts up here. So either or seems to work best.
The InnerPlotPositionClientRectangle is defined here.
In a .NET C# program, I have to simulate the "mouse" cursor movement with coordinates received in the events of a external controller.
I did get to move the cursor depending on these coordinates.
Is there any possibility to move the cursor of a smoother way between two points?, because when I have 2 away points there is like a "jump" with the cursor.
Thanks in advance.
Edit: Added code, this function is launched each time new coordinates are received (aprox. each 20-30 ms)
//...
private void MoveCursor (int x, int y){
//Added checkup for avoiding noise between closer points
if (Math.Abs(Cursor.Position.X - x)>10 || Math.Abs(Cursor.Position.Y - y)>10){
Cursor.Position = new Point(x,y)
}
}
//...
In addition to my comment, here is a simple solution (syntax untested) that I use in one of my software and that I suggest you to test to see if it fits your needs :
Point _targetPosition;
Point _currentPosition;
private void MoveCursor (int x, int y){
_targetPosition = new Point(x,y);
_currentPosition += (_targetPosition - _currentPosition)*0.08f;
Cursor.Position = _currentPosition;
}
Basically, I maintain a target position which is the last coordinate received. I move the cursor only by a fraction (here 8% of the distance between the current position and the target).
It very efficiently smooths the movement of the mouse but lead to imprecision and a delay that may not be acceptable for brutal movement or changes of direction.
A factor of 8% give good results in my case considering 60 updates/seconds but that factor need to be adapted to your data.
Finally inspired by #Ndech solution I could solve my problem.
I've divided the solution into 2 parts:
This is the event which is raised by my gaze controller (each 20ms aprox). I discard closer points to avoid "jumps" due to the way as gaze controller works.
private void MoveCursorEvent (object sender, GazePointEventArgs e){
if (Math.Abs(Cursor.Position.X - e.X) > 40 || Math.Abs(Cursor.Position.Y - e.Y) > 40){
MoveCursor((int)e.X, (int)e.Y);
}
}
Copying current values of X,Y point of the cursor in a primitive variable since Cursor.Position could change during the process (Check if the first time…) Adding to previous point, average distance between previous point and current point with a weight of 15%. And Updating Cursor.Position with the new position calculated.
//Declare position variables
int previousX = 0;
int previousY = 0;
private void MoveCursor (int newX, int newY){
//If first time, set.
if (previousX == 0)
previousX = newX;
if (previousY == 0)
previousY = newY;
//Calculate new position
previousX = (int)(((newX - previousX) /2) * 0.15f);
previousY = (int)(((newY - previousY) /2) * 0.15f);
//Update position
Cursor.Position = new Point (previousX, previousY);
}