custom control to select coordinates on a picturebox or panel? - controls

I'm working on an application to display DICOM Files and now I'm getting into MPR. Is there a control that will allow you to have like a line in a picturebox or panel like this:
http://www.vcreatelogic.com/docs/gcf-2.6.0/html/MPRView.jpg
The intention is to move that line and perform some stuff while is moving, so I basically need some sort of control or custom control (or maybe a custom icon) that will allow the user to see where he's moving in the picturebox as he moves the mouse.
Thank you!
UPDATE:
Here's the code I'm using:
In the Mouse Down of the Picturebox:
if (e.Clicks == 2)
{
horizontalstart = new Point(0, e.Y);//Start point of Horizontal line.
horizontalend = new Point(picbox_mpr.Width, e.Y);//End point of Horizontal line.
verticalstart = new Point(e.X, 0);//Start point of Vertical line
verticalend = new Point(e.X, picbox_mpr.Height);//End point of Vertical line.
}
Then in the MouseMove (where I can Pan the picture) I want those lines to remain fixed in the spot that were drawn..
UPDATE:
The whole idea is to draw the lines if the user double click in the image, then if the image is moved, move those lines along with the image..

First things first,the method I am giving does not require a custom control in order to work,it makes use of two of the events provided by PictureBox to run the logic,but if still you need a custom control you should be able to do that by porting these methods to that control with a little coding.
The following are two fields that will hold most of the values in our logic,
private Point horizontalstart, horizontalend;
private Point verticalstart, verticalend;
private bool drawlines;//Specifies a value whether PictureBox should redraw itself.
Let me explain what are these points for;
The first Point statement holds starting and end point of Horizontal line,and
the second Point statement holds starting and end point of Vertical line.
When you'r done with it write this code in MouseMoveEvent of your PictureBox;
private void SourcePictureBox_MouseMove(object sender, MouseEventArgs e)
{
if(drawlines==true)//If the lines should move.
{
horizontalstart = new Point(0, e.Y);//Start point of Horizontal line.
horizontalend = new Point(SourcePictureBox.Width, e.Y);//End point of Horizontal line.
verticalstart = new Point(e.X, 0);//Start point of Vertical line
verticalend = new Point(e.X, SourcePictureBox.Height);//End point of Vertical line.
SourcePictureBox.Invalidate();//Force PictureBox to repaint.
}
else if(drawlines==false)//To lock the lines at current coordinates.
{
//Add any code if needed.
}
}
where SourcePictureBox is the PictureBox that we are operating upon.As soon as the MouseMoveEvent is raised new coordinates for both lines are calculated and a repaint message is sent to PictureBox,which draws the lines using the coordinates declared and calculated above.
Ok so now write the code below in the Paint event of your PictureBox;
private void SourcePictureBox_Paint(object sender, PaintEventArgs e)
{
if (SourcePictureBox.Image != null)
{
e.Graphics.DrawLine(SystemPens.WindowText, horizontalstart, horizontalend);//Draw Horizontal line.
e.Graphics.DrawLine(SystemPens.WindowText, verticalstart, verticalend);//Draw Vertical line.
}
}
One more thing that i noticed in your image,the guidelines the image is showing are a little small than the image,if this is the case,then replace both the statements in MouseMoveEvent event excluding the Invalidate() one with the following;
horizontalstart = new Point(0+10, e.Y);//Start point of Horizontal line.
horizontalend = new Point(SourcePictureBox.Width-10, e.Y);//End point of Horizontal line.
verticalstart = new Point(e.X, 0+10);//Start point of Vertical line
verticalend = new Point(e.X, SourcePictureBox.Height-10);//End point of Vertical line.
But what's new in these statements,nothing except they increase the start point of both lines by value 10(int) and decrease the end points with value 10,thus giving you some space between the edges of PictureBox and the guiding lines.Please note if you want to increase or decrease the space replace value 10 with a value you would like but keep in mind the value must not be bigger than half of the size of your PictureBox.
The other thing i would like to address is if you want to change the colour of guiding lines,replace the first argument of both statements in Paint Event with any of values offered by class SystemPens.If there is nothing in the class that satisfies you,you are always free to create on for yourself.If you know how to do it,then its the best choice,incase if you need some help,take a look here.Creating a new pen gives you the option of defining a width for the guilding lines.
Update
`At anytime,to stop the lines from moving,set drawlines(declared above) to false,to resume their functionality set it to true.
It's not a foolproof method but I Hope it's enough to get your job done,and don't forget to rename the PictureBox and associated events with the name of your PictureBox.

Solved it!
here's the code:
//Variable declaration:
private Point horizontalstart, horizontalend, verticalstart, verticalend, horizontalstart_initialpoint, horizontalend_initialpoint, verticalstart_initialpoint, verticalend_initialpoint;
System.Drawing.Pen redline= new System.Drawing.Pen(System.Drawing.Color.Red);
private Point redlinemovedpoints = Point.Empty;
private Point redline_panstart = Point.Empty;
void picbox_mpr_MouseDown(object sender, MouseEventArgs e)
{
redline_panstart = new Point(e.X, e.Y); //saves the point from where you pan the picture
if (e.Clicks == 2) //if the user double clicks, then the red line is drawn in the PictureBox
{
horizontalstart = new Point(0, e.Y);//Start point of Horizontal line.
horizontalend = new Point(picbox_mpr.Width, e.Y);//End point of Horizontal line.
verticalstart = new Point(e.X, 0);//Start point of Vertical line
verticalend = new Point(e.X, picbox_mpr.Height);//End point of Vertical line.
//fixed reference points, this is used to store where the line was drawn initially.
horizontalstart_initialpoint= horizontalstart;
horizontalend_initialpoint = horizontalend;
verticalstart_initialpoint = verticalstart;
verticalend_initialpoint = verticalend;
picbox_mpr.Invalidate(); // Draw the lines (see paint event at the bottom)
}
void picbox_mpr_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left & pan) // don't worry about pan, is a variable I use to determine if the user is dragging the mouse.
{
redlinemovedpoints = new Point(e.Location.X - redline_panstart.X,
e.Location.Y - redline_panstart.Y); // this is the X and Y points move
horizontalstart = new Point(horizontalstart_initialpoint.X + redlinemovedpoints.X, horizontalstart_initialpoint.Y+redlinemovedpoints.Y);
horizontalend = new Point(picbox_mpr.Width, horizontalend_initialpoint.Y + redlinemovedpoints.Y);
verticalstart = new Point(verticalstart_initialpoint.X + redlinemovedpoints.X, verticalstart_initialpoint.Y + redlinemovedpoints.Y);
verticalend = new Point(verticalstart_initialpoint.X + redlinemovedpoints.X, picbox_mpr.Height);
picbox_mpr.Invalidate(); //re draws the lines, this time moved X or Y depending the pan.
}
}
void picbox_mpr_MouseUp(object sender, MouseEventArgs e)
{
//save the line points positions where after panning the image
horizontalstart_initialpoint = horizontalstart;
horizontalend_initialpoint = horizontalend;
verticalstart_initialpoint = verticalstart;
verticalend_initialpoint = verticalend;
}
private void picbox_mpr_Paint(object sender, PaintEventArgs e)
{
Image tmp = (Image)img.RenderImage(0);
e.Graphics.Clear(System.Drawing.Color.Black);
e.Graphics.DrawImage(tmp, movingPoint);
tmp.Dispose();
e.Graphics.DrawLine(redline, horizontalstart, horizontalend);//Draw Horizontal line.
e.Graphics.DrawLine(redline, verticalstart, verticalend);//Draw Horizontal line.
}

Related

Unity Inspector Text Label at Position

I'm trying to spruce up my Inspector but I'm having a lot of trouble trying to draw text at a specified position in the inspector. I have drawn a colored rectangle on the sample inspector below and want to draw text (like a label) on top of it.
How can I draw text/label at the "X" position shown above? I'm not really sure where to start. Any help would be greatly appreciated.
Implementing the answer:
public override void OnGUI(Rect position)
{
GUIStyle myStyle = new GUIStyle();
myStyle.fontSize = 16;
myStyle.alignment = TextAnchor.UpperLeft;
myStyle.padding.top = 5;
myStyle.padding.left = -3;
myStyle.fontStyle = FontStyle.Bold;
Color32 color = colorSpacer.drawColor; // Custom Property Attribute
EditorGUI.DrawRect(new Rect(position.x-11, position.y, position.width+11, position.height-2), color);
Rect r = GUILayoutUtility.GetLastRect();
EditorGUI.LabelField(r, "Section Header", myStyle);
}
If you are drawing your Custom Editor with EditorGUI, you can use the methods LabelField, PrefixLabel, SelectableLabel or HandlePrefixLabel, depending on your specific desired behavior.
If you are drawing with EditorGUILayout, there are equivalents of those methods.
Also, the HeaderAtribute decorator does that without need for a custom Editor.

How to make a menu with this effect in unity3d?

Sorry, I'll try to explain better.
I want to make a sliding menu where you can select a character. I want the character that is at the center increases in size to know that this is the current character. The effect can be seen in the game of Crossy Road when you want to select a character.
Sorry but I can't upload a imagen because i am new in the forum
I think I might be able to help you without needing too much borrowed code. There are two possibilities here:
You have a perspective camera so the "selected" item can just be closer to the camera.
You have an orthographic camera so you will have to scale things.
For perspective:
List<GameObject> characters; //contains all character.
int selectedIndex = 0; //index to the selected character.
float spacing = 10; //space between chars
void Update()
{
if(Input.GetKeyDown("RightArrow"))
{
selectIndex++;
ApplyChanges();
}
if(Input.GetKeyDown("LeftArrow"))
{
selectIndex--;
ApplyChanges();
}
}
void ApllyChanges()
{
// Make sure the selected index is within range and then space the characters.
selectedIndex = selectedIndex % character.Count();
SpaceCharacters();
}
void SpaceCharacters()
{
for(int i = 0; i < characters.Count(); ++i)
{
// characters on the left will have a negative spacing and so will be placed to the left and vice versa for characters on the right.
int offset = i - selectedIndex;
characters[i].transform.position = new Vector3(offset * spacing, 0, 0);
}
// Move the selected character closer.
characters[selectedIndex].transform.position = new Vector3(0,0,spacing);
}
For orthographic camera you will need to set the select characters transform.scale to a larger vector.
This won't animate anything or look cool. This code will just snap your characters into position.
The solution I adopted was to attach objects to the transparent buttons in a scrool rect, so as to manage 3d objects with the convenient of scrool rect interface.
Here you can find the official documentation for use scrool rect: http://docs.unity3d.com/Manual/script-ScrollRect.html
Maybe my assets can serve you ;)
https://www.assetstore.unity3d.com/en/#!/content/60233

ILNumeric continuous rendering plots

Is there a way to continously plot changing array data? I've got a ILLinePlot to graph the line to changing data on a button event, but I would like to make it continuous.
while (true)
{
float[] RefArray = A.GetArrayForWrite();
//RefArray[0] = 100;
Shuffle<float>(ref RefArray);
Console.Write(A.ToString());
scene = new ILScene();
pc = scene.Add(new ILPlotCube());
linePlot = pc.Add(new ILLinePlot(A.T, lineColor: Color.Green));
ilPanel1.Scene = scene;
ilPanel1.Invalidate();
}
The problem I'm running into is that the loop runs, and i can see updates of the array, but the ILPanel does not update. I'm thinking maybe it's because the main loop can't be accessed due to this infinite loop, so I put it in its own thread as well, but it's still not rendering as I hoped...
As Paul pointed out, there is a more efficient attempt to do this:
private void ilPanel1_Load(object sender, EventArgs e) {
using (ILScope.Enter()) {
// create some test data
ILArray<float> A = ILMath.tosingle(ILMath.rand(1, 50));
// add a plot cube and a line plot (with markers)
ilPanel1.Scene.Add(new ILPlotCube(){
new ILLinePlot(A, markerStyle: MarkerStyle.Rectangle)
});
// register update event
ilPanel1.BeginRenderFrame += (o, args) =>
{
// use a scope for automatic memory cleanup
using (ILScope.Enter()) {
// fetch the existint line plot object
var linePlot = ilPanel1.Scene.First<ILLinePlot>();
// fetch the current positions
var posBuffer = linePlot.Line.Positions;
ILArray<float> data = posBuffer.Storage;
// add a random offset
data = data + ILMath.tosingle(ILMath.randn(1, posBuffer.DataCount) * 0.005f);
// update the positions of the line plot
linePlot.Line.Positions.Update(data);
// fit the line plot inside the plot cube limits
ilPanel1.Scene.First<ILPlotCube>().Reset();
// inform the scene to take the update
linePlot.Configure();
}
};
// start the infinite rendering loop
ilPanel1.Clock.Running = true;
}
}
Here, the full update runs inside an anonymous function, registered to BeginRenderFrame.
The scene objects are reused instead of getting recreated in every rendering frame. At the end of the update, the scene needs to know, you are done by calling Configure on the affected node or some node among its parent nodes. This prevents the scene from rendering partial updates.
Use an ILNumerics arteficial scope in order to clean up after every update. This is especially profitable once larger arrays are involved. I added a call to ilPanel1.Scene.First<ILPlotCube>().Reset() in order to rescale the limits of the plot cube to the new data content.
At the end, start the rendering loop by starting the Clock of ILPanel.
The result is a dynamic line plot, updating itself at every rendering frame.
I think you need to call Configure() after any modification of a shape or its buffers. Use the BeginRenderFrame event to do your modifications and you should not add infinitely many shapes / new scenes. It is better to reuse them!
Let me know, if you need an example...

How do I correctly get control bound coordinates relitive to the control for mouse comparison?

I am presently learning C# and have chosen as a project to write a simple colour picker control ; however things have changed substantially since I last looked at writing code and I have struck this problem.
I am using the Mousedown event in my control to get the mouse coordinates as a Point - this is working fine and returns exactly what I would expect - the mouse coordinates relative to my control; however when I try and do a check against the control location I am returned a value as a Point showing the position of my control relative to the form which in certain cases the mouse coordinates will be outside the bounds of because their values will be less than the relative start position of the control I.E I click at pixel 1,1 in the control - the mouse position is 1,1 but because the control is located at 9,9 relative to the form the location of the mouse is less than the bounds of the control - I have absolutely no idea how to fix this, I have been attempting to sort it out with PointToClient and PointToScreen to no avail as they seem to come up with outlandish values can someone please help me out it's driving me insane.
I've managed to sort this out myself so thought I'd post the answer and it can hopefully help someone else. I was having problems getting Point values that were relative to the same pixel origin : this sorted it.
private void ColourPicker_MouseDown(object sender, MouseEventArgs e)
{ // Probably being paranoid but I am worried about scaling issues, this.Location
// would return the same result as this mess but may not handle
// scaling <I haven't checked>
Point ControlCoord = this.PointToClient(this.PointToScreen(this.Location));
int ControlXStartPosition = ControlCoord.X;
int ControlYStartPosition = ControlCoord.Y;
int ControlXCalculatedWidth = ((RectangleColumnsCount + 1) * WidthPerBlock ) + ControlXStartPosition;
int ControlYCalculatedHeight = ((RectangleRowsCount + 1) * HeightPerBlock) + ControlYStartPosition;
// Ensure that the mouse coordinates are comparible to the control coordinates for boundry checks.
Point ControlRelitiveMouseCoord = this.ParentForm.PointToClient(this.PointToScreen(e.Location));
int ControlRelitiveMouseXcoord = ControlRelitiveMouseCoord.X;
int ControlRelitiveMouseYcoord = ControlRelitiveMouseCoord.Y;
// Zero Relitive coordinates are used for caluculating the selected block location
int ZeroRelitiveXMouseCoord = e.X;
int ZeroRelitiveYMouseCoord = e.Y;
// Ensure we are in the CALCULATED boundries of the control as the control maybe bigger than the painted area on
// the design time form and we don't want to use unpaited area in our calculations.
if((ControlRelitiveMouseXcoord > ControlXStartPosition) && (ControlRelitiveMouseXcoord < ControlXCalculatedWidth))
{
if((ControlRelitiveMouseYcoord > ControlYStartPosition) && (ControlRelitiveMouseYcoord < ControlYCalculatedHeight))
{
SetEvaluatedColourFromPosition(ZeroRelitiveXMouseCoord, ZeroRelitiveYMouseCoord);
}
}
}

Get the Absolute Origin of an Object when DragStartedEvent fired

I've an app which like the image shown below (sorry I used an iPhone like mock-ups)
The app has an image as background (displayed using Canvas which takes the whole visual screen size), then another shape (the red rectangle in this case) will shown above the background which can be dragged and pinch zoomed.
Now the question is:
How can I get the coordinates (origins from top-left corner of the screen, i.e. top-left of the canvas) of the top-left corner of the rectangle?
UPDATE
According to #Will's suggestion, I currently move the rectangle in this case via DragDelta using TranslateTransform inside it, like:
rectTransform.X += e.HorizonalChange;
rectTransform.Y += e.VerticalChange;
The rectangle is defined within code, not in XAML:
rect.Stroke = new SolidColorBrush(Colors.Green);
rect.StrokeThickness = 10;
rect.Fill = new SolidColorBrush(Colors.Transparent);
rect.HorizontalAlignment = System.Windows.HorizontalAlignment.Center;
rect.VerticalAlignment = System.Windows.VerticalAlignment.Center;
rect.Width = 200;
rect.Height = 200;
canvas.Children.Add(rect);
and the canvas is defined within XAML.
What I've tried:
Initially I was trying to get the start point when the Drag event begins, but by using the DragStarted(object sender, DragStartedGestureEventArgs e), I am only able to output the coords of the point which was touched, but not the point of top-left corner of this rectangle.
And it's similar to the DragCompleted event which will return me the point the gesture ends.
So is there any chance I can get the origin coords of the red rectangle?
I spent nearly an afternoon on Google as well as MSDN and then finally come to find this on SO:
How to get the position of an element in a StackPanel?
which enlightened me using the similar method. In that case, they managed to get the absolute coordinates of an UI Element. Similarily, in my case, I intend to know the absolute origin(coordinates) of the red rectangle to the background canvas.
I use:
GeneralTransform gt = canvas.TransformToVisual(rect);
Point currentPos = gt.Transform(new Point(0, 0));
Then the currentPos is the current position of rectangle (its an absolute position, relative to the canvas).
And Here's the reference of TransformToVisual in MSDN
There's only one thing to pay attention to: the value of X and Y are negative.
Not 100% sure of the syntax here, but in WPF (which should be analogous) I'd handle it by
private double _startX, startY, _endX, _endY;
private void DragStarted(object sender, DragStartedGestureEventArgs e)
{
var rect = sender as UIElement; // I'm assuming the sender is your Rectangle
this._startX = Canvas.GetLeft(rect);
this._startY = Canvas.GetTop(rect);
}
private void DragCompleted(object sender, DragCompletedGestureOrWhateverLolEventArgs e)
{
var rect = sender as UIElement; // I'm assuming the sender is your Rectangle
this._endX = Canvas.GetLeft(rect);
this._endY = Canvas.GetTop(rect);
}
What you say? Not type safe? Yeah, well, okay. How about give your rectangle an x:Name and then use that in these methods?
<Rectangle x:Name="rect" SkipOtherPropertiesLol="true" />
and
this._startX = Canvas.GetLeft(rect); //etc
Attached properties are a little confusing at first, but they are very powerful and easy to use once you understand their fairy magic.

Resources