Accessing shapes in ILPanel and ILCamera - c#

I have three question regarding ILPanel in ILNumerics:
How can I disable the double click interaction of ILPanel to reset the view.
How can I increase the speed of panning using right click and drag. The panning is very slow especially when the shape is vary large.
How can access the elements that I add to a camera and modify them.
Here is a simple example. Two spheres are added to a camera and a ILLable, "Hello", is on top of one of them (the blue one). I want the "Hello" label to jump on top of the green sphere if I rotate the scene with mouse and the green sphere become closer to the camera and visa verse. And, how can I change the color of the spheres say by clicking a button?
private void ilPanel1_Load(object sender, EventArgs e)
{
var scene = new ILScene();
var cam = scene.Camera;
var Sphere1 = new ILSphere();
Sphere1.Wireframe.Visible = true;
Sphere1.Fill.Color = Color.Blue;
Sphere1.Wireframe.Color = Color.Blue;
Sphere1.Transform = Matrix4.Translation(0, 0, 0);
var Sphere2 = new ILSphere();
Sphere2.Wireframe.Visible = true;
Sphere2.Transform = Matrix4.Translation(2, 0, 0);
cam.Add(Sphere1);
cam.Add(Sphere2);
cam.Add(new ILLabel("Hello")
{
Font = new System.Drawing.Font(FontFamily.GenericSansSerif, 8),
Position = new Vector3(0, 0, 1.1),
Anchor = new PointF(0, 1),
});
ilPanel1.Scene = scene;
}

Create a label, which is always on top of other 3D objects
cam.Add(
new ILScreenObject() {
Location = new PointF(0.5f, 0.5f),
Children = {
new ILLabel("Hello")
}
});
See also: http://ilnumerics.net/world3d-and-screen2d-nodes.html
Regarding your "jumping sphere" example: if your want your label to jump to another group (spheres are groups actually) at runtime, you will have to implement that logic into a function which is called at runtime. ilPanel1.BeginRenderFrame might be a good place to check for such updates.
Disable default behavior for the first camera:
scene.Camera.MouseDoubleClick += (_, arg) => {
arg.Cancel = true;
};
See: http://ilnumerics.net/mouse-events.html
React on a button press:
button1.Click += (_,args) => {
ilPanel1.Scene.First<ILSphere>().Fill.Color = Color.Red;
ilPanel1.Refresh();
};
Finding objects in the scene:
see: http://ilnumerics.net/nodes-accessing-managing.html
Controlling the panning speed:
There is no easy way to configure the speed currently. The best way would be to implement the corresponding mouse handler on your own:
Override MouseMove for the camera node.
Implement the panning similar to the way it was done in ILNumerics (see: ILCamera.OnMouseMove in the sources).
Increase the scale factor.
Dont forget to cancel the event processing at the end of your handler.

Related

Stop System.Windows.Shapes.Rectangle inertia if collision with another shape happens in WPF touchscreen app

I am working on touchscreen app. This app is for a certain industry, but the general ideals are all that is needed to explain issue. To start, im doing a crash course on WPF and am allowing a user to add a new command (Shape - Rectangle) then will assign functions to it. Example, they can add one, then add second, then a third and so on positioning them where they want.
What i would like to find out is how to tell the API when inertia is occurring and say a user touches a shape then flings it across screen, for the event to not only complete when it reaches the edge of the screen (already does this), but also if it intersects or hits another shape. Below is the general code for a quick basic proof of concept.
void Window_ManipulationDelta(object sender, ManipulationDeltaEventArgs e)
{
try
{
// Get the Rectangle and its RenderTransform matrix.
Rectangle rectToMove = e.OriginalSource as Rectangle;
Matrix rectsMatrix = ((MatrixTransform)rectToMove.RenderTransform).Matrix;
// Rotate the Rectangle.
rectsMatrix.RotateAt(e.DeltaManipulation.Rotation,
e.ManipulationOrigin.X,
e.ManipulationOrigin.Y);
// Resize the Rectangle. Keep it square
// so use only the X value of Scale.
rectsMatrix.ScaleAt(e.DeltaManipulation.Scale.X,
e.DeltaManipulation.Scale.X,
e.ManipulationOrigin.X,
e.ManipulationOrigin.Y);
// Move the Rectangle.
rectsMatrix.Translate(e.DeltaManipulation.Translation.X,
e.DeltaManipulation.Translation.Y);
// Apply the changes to the Rectangle.
rectToMove.RenderTransform = new MatrixTransform(rectsMatrix);
Rect containingRect =
new Rect(((FrameworkElement)e.ManipulationContainer).RenderSize);
Rect shapeBounds =
rectToMove.RenderTransform.TransformBounds(
new Rect(rectToMove.RenderSize));
// Check if the rectangle is completely in the window.
// If it is not and intertia is occuring, stop the manipulation.
if (e.IsInertial && !containingRect.Contains(shapeBounds))
{
e.Complete();
}
if (e.IsInertial)
{
//convert current moving rect to new rect drawing type
var tempRect = new Rect(Canvas.GetLeft(rectToMove), Canvas.GetTop(rectToMove), rectToMove.Width, rectToMove.Height);
//we need to convert SytsemWindows.Shapes.Rectablge to SystemWindows.Drawing.Rect first
//List<System.Windows.Shapes.Rectangle> removeCurrent = Commands.Where(r => r != rectToMove).ToList();
//List<System.Windows.Rect> coordinates = Commands.Where(r => r.Name != rectToMove.Name).Select(r => new Rect(Canvas.GetLeft(r), Canvas.GetTop(r), r.Width, r.Height)).ToList();
List<System.Windows.Rect> recs = new List<Rect>();
List<Rectangle> test = Commands.Where(r => r.Name != rectToMove.Name).ToList();
foreach (System.Windows.Shapes.Rectangle re in test)
{
if (re != rectToMove)
{
recs.Add(new Rect(Canvas.GetLeft(re), Canvas.GetTop(re), re.Width, re.Height));
}
}
//if rect item is in motion inertia and it intersects with any of pre made command rects, then stop.
if (recs.Any(c => c.InteriorIntersectsWith(tempRect)))
{
//There is overlapping
e.Complete();
}
}
}
catch(Exception ex)
{
}
e.Handled = true;
}
The problem is inertia works fine. Once a second shape is then added to the canvas, inertia no longer works. The user can click and drag them but when trying to flick the shapes they stop instantly as if it reports from all positions that it is intersecting with another shape even when they are no where near each other. Any ideas on what i am doing wrong?

Drawing multiple rectangles chokes my WPF app

I am working in my own editor for making WPF forms. My issue is that I am having the worst time selecting multiple controls (buttons, labels, etc) and dragging them smoothly across the main window. My application chokes when I try to drag, oh say, 20 selected buttons at the same time.
I found that the culprit is the fact that I am drawing multiple rectangles for each object as they are being dragged and this function is being called in the MouseMove event.
void ControlObjectControl_MouseMove(object sender, MouseEventArgs e)
{
if (newPosition != oldPosition)
{
DragSelection(newPosition);
}
}
private void DragSelection(Point newPosition)
{
foreach (FrameworkElement item in designer.SelectionService.CurrentSelection)
{
if (item is ObjectControl)
(item as ObjectControl).m_ParentControlObject.Position = new System.Drawing.Rectangle() { X = X, Y = Y, Width = (int)item.Width, Height = (int)item.Height };
//..There's code that calculates the item's position and dimensions
}
}
How do I make it to where it only draws the rectangle once and I am still able to see my selected object(s) move smoothly when I drag them?
I did something similar in my application except I used a TranslateTransform to move my elements. Each "frame" (every mouse move) that I was dragging, I got the position of the mouse and compared that to the previous position of the mouse. I would then set a new TranslateTransform X/Y values equal to the X/Y mouse position change and then would give that to the RenderTransform of each object I wanted to move. Something like:
void ControlObjectControl_MouseMove(object sender, MouseEventArgs e)
{
if (dragging)
{
// Get the change in Location
mouseLocation = Mouse.GetPosition();
Point deltaLocation = mouseLocation - previousLocation;
// Make a new transform
TranslateTransform transform = new TranslateTransform();
transform.X = deltaLocation.X;
transform.Y = deltaLocation.Y;
// Apply the transform
// foreach thing
thing.RenderTransform = transform;
// set previous location
previousLocation = mouseLocation;
}
}
Now your objects only get drawn once and only their positions get changed. Hope this helps

Window won't draw my Sprite when releasing Mouse Button

I am new to SFML and I am just playing around with an example at the moment. I try to draw a sprite where I last clicked my mouse in a window. But some reason the event fires and no sprite is drawn. I can't really figure out why.
I've been looking around in the Documentation but I don't seem to do anything wrong. Help me pretty please? :3
The event is called OnMouseLeftClick and I am trying to draw the mouseLeftClickSprite object. I have removed the bits of code that doesn't matter like the OnMouseMoved event and the OnClose event. They have nothing to do with this.
using System;
using SFML.Audio;
using SFML.Graphics;
using SFML.Window;
namespace SFMLExample
{
class Program
{
static Text xMouseCoord;
static Text yMouseCoord;
static Text statusMsg;
static float mouseX, mouseY;
static Texture mouseLeftClickTexture;
static Sprite mouseLeftClickSprite;
static void OnMouseLeftClick(object sender, EventArgs e)
{
if (Mouse.IsButtonPressed(Mouse.Button.Left))
{
statusMsg.DisplayedString = "Console: OnMouseLeftClick() Fired";
RenderWindow window = (RenderWindow)sender;
mouseLeftClickSprite.Position = new Vector2f(mouseX, mouseY);
window.Draw(mouseLeftClickSprite);
}
}
static void Main(string[] args)
{
mouseX = 0.0f;
mouseY = 0.0f;
// Create the main window
RenderWindow window = new RenderWindow(new VideoMode(800, 600), "SFML Window");
window.Closed += new EventHandler(OnClose);
window.MouseMoved += new EventHandler<MouseMoveEventArgs>(OnMouseMoved);
window.MouseButtonPressed += new EventHandler<MouseButtonEventArgs>(OnMouseLeftClick);
// Load a sprite to display
Texture texture = new Texture("cute_image.jpg");
Sprite sprite = new Sprite(texture);
mouseLeftClickTexture = new Texture("GM106.png");
mouseLeftClickSprite = new Sprite(mouseLeftClickTexture);
// Create a graphical string to display
Font font = new Font("arial.ttf");
Text text = new Text("Hello SFML.Net", font);
xMouseCoord = new Text("Mouse X: ", font);
yMouseCoord = new Text("Mouse Y: ", font);
statusMsg = new Text("Console: ", font);
// Play some Music
//Music music = new Music("nice_music.mp3");
//music.Play();
// Start the game loop
while (window.IsOpen())
{
// Process events
window.DispatchEvents();
// Clear screen
window.Clear();
sprite.Scale = new Vector2f(1.0f, 1.0f);
sprite.Position = new Vector2f(window.Size.X / 2, window.Size.Y / 2);
// Draw the sprite
window.Draw(sprite);
// Draw the strings
xMouseCoord.Position = new Vector2f(text.Position.X, text.Position.Y + 30);
yMouseCoord.Position = new Vector2f(xMouseCoord.Position.X, xMouseCoord.Position.Y + 30);
statusMsg.Position = new Vector2f(yMouseCoord.Position.X, yMouseCoord.Position.Y + 30);
window.Draw(text);
window.Draw(xMouseCoord);
window.Draw(yMouseCoord);
window.Draw(statusMsg);
// Update the window
window.Display();
}
}
}
}
Added this bit below for some clarification
static void OnMouseMoved(object sender, EventArgs e)
{
RenderWindow window = (RenderWindow)sender;
Vector2i vector = window.InternalGetMousePosition();
mouseX = vector.X;
mouseY = vector.Y;
xMouseCoord.DisplayedString = "Mouse X: " + mouseX;
yMouseCoord.DisplayedString = "Mouse Y: " + mouseY;
statusMsg.DisplayedString = "Console: ";
}
You only draw your sprite once. With approximately 60 frames per second, you might see it blink... or not. If you need it to stay permanent, you need to remember the point in your main program and then draw a sprite at that point every time your draw the other things.
You may want to look into game programming patterns. Normally, you have three big blocks that are repeated endlessly: you get user input, you act on user input, you draw the result.
In pseudo code:
while(isRunning)
{
HandleUserInput();
ChangeWorld():
Render();
}
That way, you can check yourself if what you do is correct. For example, in your scenario you would have drawn something while handling user input. Not good.
The events are dispatched and evaluated inside the so-called window.DispatchEvents(). When you click on your window, the event will be fired and your sprite will be drawn.
The thing is that you are calling window.Clear() after it, so you can not see the result. As nvoigt said, you must render everything at each frame, that is to say at each turn of your loop. This is why one uses Clear() method from Window, to clear the result of the previous frame.
What you could do is remember the position of the mouse when the event is fired or directly set the position of the sprite according to it. Then draw your sprite along with your text etc. If you don't want it to be displayed before any click of the mouse, just put its coordinates at a point outside the window so it will not be drawn.

Performance issue of DrawingVisual following cursor while using onMouseMove event

I'm busy with a small application in which I want to display information at the location of the cursor when it hoovers over a Canvas. The Canvas in question is a custom one (inherited from Canvas) which provides functionality to add DrawingVisuals (as shown in basically every tutorial on displaying large amounts of geometric shapes on a canvas).
I would like to display a vertical line and horizontal line as well as the local coordinates (p in the code below) which are directly derived from the canvas coordinates (v). At the moment I'm rendering these objects at position (0,0) and use offset during the OnMouseMove event to update their location.
The horizontal and vertical lines are rendered in the DrawingVisual _cursor and the location in local y,z-coordinates in _info.
private void oCanvas_MouseMove(object sender, MouseEventArgs e)
{
#region 1. Get location data
System.Windows.Vector v = (System.Windows.Vector)e.GetPosition(oCanvas);
// point in YZ coordinates
BSMath.DoubleXY p = new BSMath.DoubleXY();
p.X = (oCanvas.OriginY - v.Y) / oCanvas.ZoomFactor;
p.Y = (oCanvas.OriginX - v.X) / oCanvas.ZoomFactor;
#endregion
#region 2. Update cursor and info
if (oSettings.ShowInformation)
{
_info.Info = p.X.ToString("0.0") + " | " + p.Y.ToString("0.0");
_info.Render(0, 0);
_info.Visual.Offset = v;
}
// move cursor
_cursor.Visual.Offset = v;
}
Using the mousemove event seems to be creating a lot of overhead and I can see that there are issues tracking the mouse movements when I move the mouse quickly.
Can anyone recommend a better way of creating the same effect?
example http://www.iccg.be/test/images/canvas.jpg
Edit:
I investigated it a bit further and the problem seems to occur when the resolution of the canvas is bigger. If it is a 600x400 canvas then there is no delay, but when it is around 1000x800 I get the problem with delays when hoovering. The performance also improves if I use user drawn crosshairs instead of the lines that have the full width/ height of the canvas.
I recently have built something similar and haven't had any performance issues.
Did it the very simple way by adding the stuff directly on the canvas.
The drawn items are in a second canvas behind the mouse position canvas. Both reside in a Grid.
This is for sure not the most sophisticated way to solve this, but it works quite well for me.
Here's the code:
private Point _previous;
private Point _current;
private Line _xLine;
private Line _yLine;
private TextBlock _displayTextBlock;
private void Canvas_MouseMove(object sender, MouseEventArgs e)
{
_current = e.GetPosition(myCanvas);
if (_previous != _current)
{
if (_xLine == null)
{
_xLine = new Line() {X1 = 0, X2 = myCanvas.ActualWidth, Stroke = new SolidColorBrush(Colors.Black)};
_yLine = new Line() {Y1 = 0, Y2 = myCanvas.ActualHeight, Stroke = new SolidColorBrush(Colors.Black)};
_displayTextBlock = new TextBlock();
myCanvas.Children.Add(_xLine);
myCanvas.Children.Add(_yLine);
myCanvas.Children.Add(_displayTextBlock);
}
_displayTextBlock.SetValue(Canvas.TopProperty, _current.Y);
_displayTextBlock.SetValue(Canvas.LeftProperty, _current.X);
_displayTextBlock.Text = _current.X.ToString() + " | " + _current.Y.ToString();
_xLine.Y1 = _current.Y;
_xLine.Y2 = _current.Y;
_yLine.X1 = _current.X;
_yLine.X2 = _current.X;
_previous = _current;
}
}

WPF, animate over container boundaries

I'm learning WPF and have a specific goal.
Imagine you have a grid (3 rows by 3 columns), and a control, say a simple blue rectangle fills the middle cell. When I click on the cell I want the square to rotate smoothly through 180 degrees.
The specific issue I have at the moment is; as the rectangle rotates, it won't change its dimensions, so it will extend beyond the boundary of the cell. I don't want it to clip, i want it to appear on top, partially obscuring surrounding cells.
The second part of this is, if there is one cell that fills the entire window, and I click on the blue rectangle in that cell, can I make the rectangle rotate and extend beyond the sides of the form?
If that doesn't make sense, please ask. I'm finding it hard to google because I don't know the exact terms I should be using.
Thank you
The first part can be acomplished by using the attached property Panel.ZIndex, set it to a high value when you start the animation and a lower value when the animation is complete. The second part (having a control outside of the window) is more complicated. I tried a few things and this method seemed to be the best. It uses a full screen window instead of a Popup as I encountered cliping issues. A copy of the element is made using RenderTargetBitmap this is then placed in the same position. The original element is hidden whilst the copy is animated.
public void PopupAnimation(UIElement element)
{
double w = element.RenderSize.Width,h = element.RenderSize.Height;
var screen = new Canvas();
var pos = element.PointToScreen(new Point(0, 0));
var rtb = new RenderTargetBitmap((int)w,(int)h, 96, 96, PixelFormats.Pbgra32);
rtb.Render(element);
Image i = new Image { Source = rtb, Width = w, Height = h,Stretch=Stretch.Fill};
Canvas.SetLeft(i, pos.X);
Canvas.SetTop(i, pos.Y);
screen.Children.Add(i);
var window = new Window() {
Content = screen, AllowsTransparency = true,
Width=SystemParameters.PrimaryScreenWidth,Height=SystemParameters.PrimaryScreenHeight,
WindowStyle=WindowStyle.None,ShowInTaskbar=false,Topmost=true,
Background=Brushes.Transparent,ShowActivated=false,Left=0,Top=0
};
var transform = new RotateTransform();
i.RenderTransformOrigin = new Point(0.5, 0.5);
i.RenderTransform = transform;
var anim = new DoubleAnimation { To = 360 };
anim.Completed += (s,e) =>
{
element.Visibility = Visibility.Visible;
var delay = new Storyboard { Duration = TimeSpan.FromSeconds(0.1) };
delay.Completed += (s2, e2) => window.Close();
delay.Begin();
};
window.ContentRendered += (s, e) =>
{
transform.BeginAnimation(RotateTransform.AngleProperty, anim);
element.Visibility = Visibility.Hidden;
};
window.Show();
}

Categories

Resources