3D Hit Testing in WPF - wpf

I'm writing a WPF application that displays terrain in 3D.
When I perform hit testing, the wrong 3D point is returned (not the point I clicked on).
I tried highlighting the triangle that was hit (by creating a new mesh, taking the coordinates from the RayMeshGeometry3DHitTestResult object). I see that the wrong triangle gets hit (a triangle is highlighted, but it is not under the cursor).
I'm using a perspective camera with field of view of 60, and the near and far planes are of 3 and 35000 respectively.
Any idea why it might happen and what I can do to solve it?
Let me know if you need any more data.
Edit: This is the code I use to perform the hit testing:
private void m_viewport3d_MouseDown(object sender, MouseButtonEventArgs e)
{
Point mousePos = e.GetPosition(m_viewport3d);
PointHitTestParameters hitParams = new PointHitTestParameters(mousePos);
HitTestResult result = VisualTreeHelper.HitTest(m_viewport3d, mousePos);
RayMeshGeometry3DHitTestResult rayMeshResult = result as RayMeshGeometry3DHitTestResult;
if (rayMeshResult != null)
{
MeshGeometry3D mesh = new MeshGeometry3D();
mesh.Positions.Add(rayMeshResult.MeshHit.Positions[rayMeshResult.VertexIndex1]);
mesh.Positions.Add(rayMeshResult.MeshHit.Positions[rayMeshResult.VertexIndex2]);
mesh.Positions.Add(rayMeshResult.MeshHit.Positions[rayMeshResult.VertexIndex3]);
mesh.TriangleIndices.Add(0);
mesh.TriangleIndices.Add(1);
mesh.TriangleIndices.Add(2);
GeometryModel3D marker = new GeometryModel3D(mesh, new DiffuseMaterial(Brushes.Blue));
//...add marker to the scene...
}
}

Something that caught me was that the points were in model coords. I had to transform to world coords. Here is my code that does the hit test (this will return all hits under the cursor, not just the first):
// This will cast a ray from the point (on _viewport) along the direction that the camera is looking, and returns hits
private List<RayMeshGeometry3DHitTestResult> CastRay(Point clickPoint, IEnumerable<Visual3D> ignoreVisuals)
{
List<RayMeshGeometry3DHitTestResult> retVal = new List<RayMeshGeometry3DHitTestResult>();
// This gets called every time there is a hit
HitTestResultCallback resultCallback = delegate(HitTestResult result)
{
if (result is RayMeshGeometry3DHitTestResult) // It could also be a RayHitTestResult, which isn't as exact as RayMeshGeometry3DHitTestResult
{
RayMeshGeometry3DHitTestResult resultCast = (RayMeshGeometry3DHitTestResult)result;
if (ignoreVisuals == null || !ignoreVisuals.Any(o => o == resultCast.VisualHit))
{
retVal.Add(resultCast);
}
}
return HitTestResultBehavior.Continue;
};
// Get hits against existing models
VisualTreeHelper.HitTest(grdViewPort, null, resultCallback, new PointHitTestParameters(clickPoint));
// Exit Function
return retVal;
}
And some logic that consumes a hit:
if (hit.VisualHit.Transform != null)
{
return hit.VisualHit.Transform.Transform(hit.PointHit);
}
else
{
return hit.PointHit;
}

You need to provide the ray to hit test along in order for this to work in 3d. Use the correct overload of VisualTreeHelper.HitTest which takes a Visual3D and a RayHitTestParameters: http://msdn.microsoft.com/en-us/library/ms608751.aspx

Figures out it was a Normalize issue. I shouldn't have normalized the camera's look and up vectors. In the scales I'm using, the distortion is too big for the hit test to work correctly.

Related

Unity3D - Playback object array of position (with dynamic velocity)

We own two objects in the scene. One follows the mouse position on the screen, and the object 2 in turn follows the route object 1 did. We are storing the positions covered by the object 1 and causing the object 2 play them.
When you run the game, an object follows the other quietly, reproducing the stored position ... but when one object's speed is changed (on mouse click increase velocity) the object 2 can not keep up, as this still following the positions already be cached in the array (including the calculations speed). Please, watch the shot video below:
YouTube: https://youtu.be/_HbP09A3cFA
public class Play : MonoBehaviour
{
public Transform obj;
private List<Recorder> recordList;
private float velocity = 10.0f;
private Transform clone;
void Start()
{
recordList = new List<Recorder>();
clone = obj;
}
void Update()
{
if (Input.GetMouseButton(0))
{
velocity = 20.0f;
}
else {
velocity = 10.0f;
}
var dir = Input.mousePosition - Camera.main.WorldToScreenPoint(transform.position);
var angle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg;
transform.rotation = Quaternion.RotateTowards(transform.rotation, Quaternion.AngleAxis(angle, Vector3.forward), 180 * Time.deltaTime);
transform.position += transform.right * Time.deltaTime * velocity;
Camera.main.transform.position = new Vector3(transform.position.x, transform.position.y, Camera.main.transform.position.z);
recordList.Insert(0, new Recorder
{
Position = transform.position,
Rotation = transform.rotation,
Velocity = velocity
});
var x = 8;
if (x < recordList.Count)
{
clone.position = recordList[x].Position;
clone.rotation = recordList[x].Rotation;
clone.position += clone.right * Time.deltaTime * velocity;
}
if (recordList.Count > x)
recordList.RemoveRange(x, recordList.Count - x);
}
}
public class Recorder
{
public Vector3 Position{get;set;}
public Quaternion Rotation{get;set;}
public float Velocity{get;set;}
}
How can we play the positions stored always with the speed of the object 1?
Summary:
If the object 1 is slowly moving object 2 as well;
If the object 2 is running, the object 2 should do the route at a faster speed to always follow the object 1;
Thanks in advance.
If i understood correctly you might want to consider using Queue<T> instead of List<T>. I think it would be a better suited datatype as it represents a FIFO collection (first in, first out), which is how you use List anyway. You can add elements with Enqueue(T) to the end of the queue and always get the first item with Dequeue() (it also removes it). As for Stack<T> (the opposite), there is also a Peek() function which lets you "preview" the next element.
Another thing, it depends on distance and speed, but i have the feeling that storing the position of every frame could become a bit excessive (maybe im just overly concerned though)
I think the issue with your code is that you always get the 8th element of the List.

AndEngine TimerHandler - onTimePassed

I am using AndEngine to create Physics Simulations of projectiles being launched. As it simulates, I want to draw the parable's track.
To do so, I am drawing a square every second according to the position of the projectile(sPlayer).
time_handler=new TimerHandler(1, true, new ITimerCallback() {
#Override
public void onTimePassed(TimerHandler pTimerHandler) {
if(simulationOn){ // every 1 second if the simulation is on
int px=(int)sPlayer.getSceneCenterCoordinates()[0];
int py=(int)sPlayer.getSceneCenterCoordinates()[1];
parabola_point=new Rectangle(px, py,4, 4,getVertexBufferObjectManager());
parabola_point.setColor(Color.WHITE);
if(!highest_point_found){ //if highest point not found, check it
float difY = (float) Math.floor(Math.abs(body.getLinearVelocity().y)) ;
if(Float.compare(0f, difY) == 0){ // if it is the highest point
highest_point_found=true;
drawPointText(); //draw the positions on the scene
parabola_point=new Rectangle(px, py,16, 16,getVertexBufferObjectManager());
parabola_point.setColor(Color.RED); // paint this point red
}
}
parabola.add(parabola_point);
scene.attachChild(parabola_point);
}
// pTimerHandler.reset();
}
});
I am using a FixedStepEngine:
#Override
public Engine onCreateEngine(final EngineOptions pEngineOptions) {
return new FixedStepEngine(pEngineOptions, 50);
}
THE PROBLEM IS:
I don't know why onTimePassed is being called faster than 1 second interval.It happens after some seconds.
I read that problaby the FixedStepEngine is changing the interval that 'onTimePassed' is called. How to fix it?
It seems to me that you are not unregistering your timer handlers which causes them to intersect with one another. Try unregistering pTimerHandler

get data point when user click a line graph using DataVisualization.Charting.Chart

I am using DataVisualization.Charting.Chart (winform), I need to get the data point index when user clicks on a line graph in MouseDown event.
I know there is a HitTest function accepting x & y, but for a line graph, we only need to verify x, if we scan the y (0 to height of graph), it will work, but the performance is too bad.
One way to do this is to enable the cursor
chartArea1.CursorX.IsUserEnabled = true;
chartArea1.CursorX.IsUserSelectionEnabled = true;
// set selection color to transparent so that range selection is not drawn
chartArea1.CursorX.SelectionColor = System.Drawing.Color.Transparent;
and handle the CursorPositionChanged event.
private void chart1_CursorPositionChanged(object sender, CursorEventArgs e)
{
// find a point (this series only has Y values, so using position as index works
// for a series with actual X values, you'd need to Find the closest point
DataPoint pt = chart1.Series[0].Points[(int)Math.Max(e.ChartArea.CursorX.Position - 1, 0)];
// do what is need with the data point
pt.MarkerStyle = MarkerStyle.Square;
}
This obviously assumes a single Series in your ChartArea.
if you are use HitTestResult's ChartElementType.
HitTestResult result = chart.HitTest(e.X, e.Y);
if (result.ChartElementType == ChartElementType.DataPoint)
{
int index = result.PointIndex;
// todo something...
}

How to change the direction of wpf marquee dynamically?

I want to change the direction of my marquee on changeDirection button click.
My code for changing direction is :
private void changeDirection_click(object sender, RoutedEventArgs e)
{
if (_marqueeType == MarqueeType.RightToLeft)
{
_marqueeType = MarqueeType.LeftToRight;
StartMarqueeing(_marqueeType);
}
else if (_marqueeType == MarqueeType.LeftToRight)
{
_marqueeType = MarqueeType.RightToLeft;
StartMarqueeing(_marqueeType);
}
}
And code for start marquee is :
public void StartMarqueeing(MarqueeType marqueeType)
{
double height = canMain.ActualHeight - marqueeList.ActualHeight;
marqueeList.Margin = new Thickness(0, 0, 0, 0);
doubleAnimation.From = -marqueeList.ActualWidth;
doubleAnimation.To = canMain.ActualWidth;
doubleAnimation.RepeatBehavior = RepeatBehavior.Forever;
doubleAnimation.Duration = new Duration(TimeSpan.FromSeconds(_marqueeTimeInSeconds));
if (marqueeType == MarqueeType.RightToLeft)
{
Storyboard.SetTargetProperty(doubleAnimation, new PropertyPath("(Canvas.Right)"));
_storyBoard.Children.Add(doubleAnimation);
_storyBoard.Begin(marqueeList, true);
}
else if (marqueeType == MarqueeType.LeftToRight)
{
Storyboard.SetTargetProperty(doubleAnimation, new PropertyPath("(Canvas.Left)"));
_storyBoard.Children.Add(doubleAnimation);
_storyBoard.Begin(marqueeList, true);
}
}
Now here I am able to change the direction from Right to Left only first time.
But when I am change it from Left to Right it’s not changing the marquee position Left to Right.
It looks like you left out a _storyboard = new Storyboard(), perhaps at the top of the StartMarqueeing method.
From what I see it appears that every call to StartMarqueeing will add an additional DoubleAnimation to the storyboard, then start it again. So all the old DoubleAnimations will be recreated, and it looks like they take precedence.
Try creating a new Storyboard object each time, not just re-using it and adding to its children collection.
Update
Oh, now I see the problem. You should not be setting both (Canvas.Left) and (Canvas.Right). Use only one of the two: That's all you need anyway, and using both will give the Canvas conflicting instructions. Traditionally people use (Canvas.Left). I think that's what Canvas selects, which is what is causing your asymmetry.
You may wonder why I say you are using both when you don't think your two animations every run at the same time. Actually they do: The first animation runs then holds the value on the animated property until it is removed or bumped off by another animation. If the second animation then runs and modifies a different property it doesn't bump off the first animation so the first animation's value is still present.
The bottom line is, using (Canvas.Left) on both animations should fix it as long as you are using the default HandoffBehavior.SnapshotAndReplace.

Selecting an object on a WPF Canvas?

I have a WPF Canvas with some Ellipse objects on it (displayed as circles). Each circle is from a collection class instance which is actually a custom hole pattern class. Each pattern has a certain number of circles, and each circle then gets added to the canvas using an iteration over the collection using the code below.
So, the canvas is populated with a bunch of circles and each circle belongs to a certain pattern instance. You can see a screenshot here: http://twitpic.com/1f2ci/full
Now I want to add the ability to click on a circle on the canvas, and be able to determine the collection it belongs to, so that I can then do some more work on the selected pattern to which that circle belongs.
public void DrawHoles()
{
// Iterate over each HolePattern in the HolePatterns collection...
foreach (HolePattern HolePattern in HolePatterns)
{
// Now iterate over each Hole in the HoleList of the current HolePattern...
// This code adds the HoleEntity, HoleDecorator, and HoleLabel to the canvas
foreach (Hole Hole in HolePattern.HoleList)
{
Hole.CanvasX = SketchX0 + (Hole.AbsX * _ZoomScale);
Hole.CanvasY = SketchY0 - (Hole.AbsY * _ZoomScale);
canvas1.Children.Add(Hole.HoleEntity);
}
}
}
All FrameworkElements have a Tag property which is of type object that can be used to hold arbitrary information. You could assign the HolePattern to the Tag property and easily use that later to get the associated collection.
i.e.:
...
Hole.HoleEntity.Tag = HolePattern as object;
canvas1.Children.Add(Hole.HoleEntity);
later on in the click event:
event(object sender,....)
{
Ellipse e = sender as Ellipse;
HolePattern hp = e.Tag as HolePattern;
...
}
So you probably already read my reply where I said I had it working. And it does work perfectly, (except that it requires great precision with the mouse), but I want to ask this: is it really smart to add an event handler to EVERY ellipse that gets added to a canvas? Now I don't know what kind of memory bog that could be, or maybe it is a piece of cake for WPF and Windows to handle.
In a practical case, I guess there would be not more that 30-50 holes even on a screen that had multiple patterns, but still; FIFTY event handlers? It just seems scary. And actually, each "Hole" is visually represented by two concentric circles and a text label (see the screenshow here: http://twitpic.com/1f2ci/full ), and I know the user would expect to be able to click on any one of those elements to select a hole. That means an event handler on 3 elements for every hole. Now we could be talking about 100 or more event handlers.
It seems like there should be a solution where you could have just one event handler on the Canvas and read the element reference under the mouse, then work off of that to get the .Tag property of that elment, and so on.
I thought I'd post my final and more refined solution in case it helps anyone else.
void canvas1_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
int ClickMargin = 2;// Adjust here as desired. Span is in both directions of selected point.
var ClickMarginPointList = new Collection<Point>();
Point ClickedPoint = e.GetPosition(canvas1);
Point ClickMarginPoint=new Point();
for (int x = -1 * ClickMargin; x <= ClickMargin; x++)
{
for (int y = -1 * ClickMargin; y <= ClickMargin; y++)
{
ClickMarginPoint.X = ClickedPoint.X + x;
ClickMarginPoint.Y = ClickedPoint.Y + y;
ClickMarginPointList.Add(ClickMarginPoint);
}
}
foreach (Point p in ClickMarginPointList)
{
HitTestResult SelectedCanvasItem = System.Windows.Media.VisualTreeHelper.HitTest(canvas1, p);
if (SelectedCanvasItem.VisualHit.GetType().BaseType == typeof(Shape))
{
var SelectedShapeTag = SelectedCanvasItem.VisualHit.GetValue(Shape.TagProperty);
if (SelectedShapeTag!=null && SelectedShapeTag.GetType().BaseType == typeof(Hole))
{
Hole SelectedHole = (Hole)SelectedShapeTag;
SetActivePattern(SelectedHole.ParentPattern);
SelectedHole.ParentPattern.CurrentHole = SelectedHole;
return; //Get out, we're done.
}
}
}
}

Resources