this is the code I use to do the hitTest:
RayHitTestResult hit = VisualTreeHelper.HitTest(
App.MainWin.Viewport, location) as RayHitTestResult;
This code works when the camera width is small (i.e. objects are big). When I zoom out the camera (i.e. objects become smaller) to certain point, then when I click on the viewport, it shows an error on the above code, and the error details is like this:
System.NotSupportedException was unhandled : Hit testing with a
singular MatrixCamera is not supported.
I googled and found nobody having this error. Any idea how to solve it? Thanks!
The code I used to create / update the MatrixCamera: (They are inspired by the examples in the book "3D Programming for Windows: Three-Dimensional Graphics Programming for the Windows Presentation Foundation" by Charles Petzold, Chapter 7)
//create a new camera, initialize it and attach it to the viewport.
public void initCamera(Point3D position, Point3D AimPoint,
Vector3D upDirection, double farDistance,
double nearDistance, double Width)
{
this._Camera = new MatrixCamera();
this.CameraAimPoint = (Vector3D)AimPoint;
//check and adjust the camera depth if needed
this.CameraZAxis = Point3D.Subtract(position, AimPoint);
this.CameraDepth = CameraZAxis.Length;
this.CameraZAxis.Normalize();
this.CameraPosition = this.CameraAimPoint + (this.CameraZAxis * this.CameraDepth);
this.CameraFarDistance = farDistance;
this.CameraNearDistance = nearDistance;
this.CameraWidth = Width;
this.CameraXAxis = Vector3D.CrossProduct(upDirection, this.CameraZAxis);
this.CameraXAxis.Normalize();
this.CameraYAxis = Vector3D.CrossProduct(this.CameraZAxis, this.CameraXAxis);
this._CameraViewMatrix = new Matrix3D();
this._CameraViewMatrix.M14 = 0;
this._CameraViewMatrix.M24 = 0;
this._CameraViewMatrix.M34 = 0;
this._CameraViewMatrix.M44 = 1;
this.updateViewMatrix();
this._CameraProjectMatrix = new Matrix3D();
this._CameraProjectMatrix.M14 = 0;
this._CameraProjectMatrix.M24 = 0;
this._CameraProjectMatrix.M34 = 0;
this._CameraProjectMatrix.M44 = 1;
this.updateProjectionMatrix();
this._Viewport.Camera = this._Camera;
}
private void updateViewMatrix(bool axisChanged=true)
{
if (axisChanged==true)
{
this._CameraViewMatrix.M11 = this.CameraXAxis.X;
this._CameraViewMatrix.M12 = this.CameraYAxis.X;
this._CameraViewMatrix.M13 = this.CameraZAxis.X;
this._CameraViewMatrix.M21 = this.CameraXAxis.Y;
this._CameraViewMatrix.M22 = this.CameraYAxis.Y;
this._CameraViewMatrix.M23 = this.CameraZAxis.Y;
this._CameraViewMatrix.M31 = this.CameraXAxis.Z;
this._CameraViewMatrix.M32 = this.CameraYAxis.Z;
this._CameraViewMatrix.M33 = this.CameraZAxis.Z;
}
this._CameraViewMatrix.OffsetX = -Vector3D.DotProduct(this.CameraXAxis, this.CameraPosition);
this._CameraViewMatrix.OffsetY = -Vector3D.DotProduct(this.CameraYAxis, this.CameraPosition);
this._CameraViewMatrix.OffsetZ = -Vector3D.DotProduct(this.CameraZAxis, this.CameraPosition);
this._Camera.ViewMatrix = this._CameraViewMatrix;
this._3DTo2DTransformMatrixIsDefined = false;
AfterDraw();
}
//
private void updateProjectionMatrix() {
double ScaleX = 2 / CameraWidth;
double ScaleY = _Viewport.ActualWidth / _Viewport.ActualHeight * ScaleX;
double ScaleZ = 1 / (CameraNearDistance - CameraFarDistance);
double zOffset = CameraNearDistance * ScaleZ;
_CameraProjectMatrix.M11 = ScaleX;
_CameraProjectMatrix.M22 = ScaleY;
_CameraProjectMatrix.M33 = ScaleZ;
_CameraProjectMatrix.OffsetZ = zOffset;
_Camera.ProjectionMatrix = _CameraProjectMatrix;
_3DTo2DTransformMatrixIsDefined = false;
_PixelToWorldUnit = CameraWidth / _Viewport.ActualWidth;
AfterDraw();
}
The code to perform camera zooming:
private void _cameraZoomToScale(double width)
{
if (width< 1) width = 1;
this.CameraWidth = width;
updateProjectionMatrix();
}
It seems pretty clear that you are zooming further than the MatrixCamera supports. Figure out at what zoom level this exception occurs and prevent the user from getting that far. It looks like you might already be trying to do that already with if (scale < 1) scale = 1; but scale doesn't seem to be used elsewhere in the posted code.
Related
This is my first project in c# and I'm trying to create plots from data.
I'm struggling with drawing minor and major grid lines and labels on a logarithmic scale.
I've set the scale to logarithmic, set the base to 10 and both major and minor intervals to 1, and it works great, however, the interval starts with the minimum value on scale, so for example if data starts at 30M (I'm dealing with frequencies) the next major tick is at 300M and 3G, which is not as it should be.
Is there a way to set major grid to 1, 10, 100 etc, independent of what data is displayed? i've tried changing intervals, base and offset but have not achieved much.
area.AxisX.IsLogarithmic = true;
area.AxisX.LogarithmBase = 10;
area.AxisX.Interval = 1;
//area.AxisX.IntervalOffset = 10000;
area.AxisX.IntervalAutoMode = IntervalAutoMode.FixedCount;
area.AxisX.MajorGrid.Enabled = true;
area.AxisX.MajorTickMark.Enabled = true;
area.AxisX.MinorGrid.Enabled = true;
area.AxisX.MinorGrid.Interval = 1;
area.AxisX.MinorTickMark.Enabled = true;
area.AxisX.MinorTickMark.Interval = 1;
area.AxisX.Minimum = minMaxXY[0]; // in this example 30 M
area.AxisX.Maximum = minMaxXY[1]; // in this example 1 G
here's the link to the current grid
https://ibb.co/3WkxLfc
Thank you for your time and answers!
Thanks to TaW replay I managed to get my program working.
Here is my solution using customLabels location to draw the grid lines.
private void Chart1_PostPaint(object sender, ChartPaintEventArgs e)
{
if (e.Chart.ChartAreas.Count > 0) // I don't yet truly understand when this event occurs,
// so I got plenty of null references.
{
Graphics g = e.ChartGraphics.Graphics;
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;
Color minorGridColor = Color.Gainsboro;
ChartArea area = e.Chart.ChartAreas[0];
double aymin = area.AxisY.Minimum;
double aymax = area.AxisY.Maximum;
int y0 = (int)area.AxisY.ValueToPixelPosition(aymin);
int y1 = (int)area.AxisY.ValueToPixelPosition(aymax);
foreach (var label in chart1.ChartAreas[0].AxisX.CustomLabels)
{
double xposition = area.AxisX.ValueToPixelPosition(Math.Pow(10, label.FromPosition + 0.1));
if (xposition > area.AxisX.ValueToPixelPosition(minMaxXY[0]) && xposition < area.AxisX.ValueToPixelPosition(minMaxXY[1]))
//this prevents drawing of lines outside of the chart area
{
int x = (int)xposition;
using (Pen dashed_pen = new Pen(Color.FromArgb(10, 0, 0, 0), 1))
{
dashed_pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
g.DrawLine(dashed_pen, x, y0, x, y1);
}
}
}
}
}
I also found the CustomLabel.GridTicks Property, but for some reason it did not work.
I'm making a program to graph parabolas, and I want to make the X and Y axes (the ones from (0,0)) a different color. I haven't found any options to do so, and the only solution I've found is to make a large grid and set its increment to half the graph's size. Is there an alternative?
I used the default chart control. I'd expect something like:
You can set Crossing for axis to move the axis to center of chart. Also you can set LineWidth for axis to make it thicker. Also you can set ArrowStyle to have an arrow at the end of axis.
For example, to have a chart like this:
Use such code:
private void Form1_Load(object sender, EventArgs e)
{
//Set Chart Margins
this.chart1.ChartAreas[0].Position.Auto = false;
this.chart1.ChartAreas[0].Position.X = 10;
this.chart1.ChartAreas[0].Position.Y = 10;
this.chart1.ChartAreas[0].Position.Width = 80;
this.chart1.ChartAreas[0].Position.Height = 80;
//Configure X Axis
this.chart1.ChartAreas[0].AxisX.Crossing = 0;
this.chart1.ChartAreas[0].AxisX.Interval = 1;
this.chart1.ChartAreas[0].AxisX.LabelStyle.Enabled = false;
this.chart1.ChartAreas[0].AxisX.LineWidth = 2;
this.chart1.ChartAreas[0].AxisX.ArrowStyle =
System.Windows.Forms.DataVisualization.Charting.AxisArrowStyle.Lines;
//Configure Y Axis
this.chart1.ChartAreas[0].AxisY.Crossing = 0;
this.chart1.ChartAreas[0].AxisY.Interval = 5;
this.chart1.ChartAreas[0].AxisY.LineWidth = 2;
this.chart1.ChartAreas[0].AxisY.LabelStyle.Enabled = false;
this.chart1.ChartAreas[0].AxisY.ArrowStyle =
System.Windows.Forms.DataVisualization.Charting.AxisArrowStyle.Lines;
//Set Chart Type
this.chart1.Series[0].ChartType =
System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Spline;
//Set Data
var p = new List<PointF>();
for (int i = -5; i <= 5; i++)
{
p.Add(new PointF(i, i * Math.Abs(i)));
}
this.chart1.DataSource = p;
this.chart1.Series[0].XValueMember = "X";
this.chart1.Series[0].YValueMembers = "Y";
this.chart1.Series[0].IsVisibleInLegend = false;
}
I am following a tutorial in a book called Learn the Kinect API by Rob Miles.
Basically, it's an augmented reality game where spiders fall from the top of the screen and you hit them with a Mallet. After following the tutorial and looking at the codes, I understand how the spiders fall and how their position are randomized etc.
I have problem understanding the mallet though, I wish to replace the mallet with an image of a basket instead.
This is how the code for the mallet looks like
Brush malletHandleBrush = new SolidColorBrush(Colors.Black);
Brush malletHeadBrush = new SolidColorBrush(Colors.Red);
float malletHandleLength = 100;
float malletHeadLength = 50;
System.Windows.Vector malletPosition;
float malletHitRadius = 40;
bool malletValid = false;
void updateMallet(Joint j1, Joint j2)
{
// If Joint 1 (Right Wrist) or Joint 2 (Right Hand) is not tracked, we stop here
if (j1.TrackingState != JointTrackingState.Tracked || j2.TrackingState != JointTrackingState.Tracked)
return;
// Get the start and end positions of the mallet vector
ColorImagePoint j1P = myKinect.CoordinateMapper.MapSkeletonPointToColorPoint(j1.Position, ColorImageFormat.RgbResolution640x480Fps30);
ColorImagePoint j2P = myKinect.CoordinateMapper.MapSkeletonPointToColorPoint(j2.Position, ColorImageFormat.RgbResolution640x480Fps30);
int dX = j2P.X - j1P.X;
int dY = j2P.Y - j1P.Y;
System.Windows.Vector malletDirection = new System.Windows.Vector(dX, dY);
if (malletDirection.Length < 1) return;
// Convert into a vector of length 1 unit
malletDirection.Normalize();
// now set the length of the mallet
System.Windows.Vector handleVector = malletDirection * malletHandleLength;
Line handleLine = new Line();
handleLine.Stroke = malletHandleBrush;
handleLine.StrokeThickness = 10;
handleLine.X1 = j1P.X;
handleLine.Y1 = j1P.Y;
handleLine.X2 = j1P.X + handleVector.X;
handleLine.Y2 = j1P.Y + handleVector.Y;
//malletCanvas.Children.Add(handleLine);
Line headLine = new Line();
headLine.Stroke = malletHeadBrush;
headLine.StrokeThickness = 50;
System.Windows.Vector headVector = malletDirection * malletHeadLength;
headLine.X1 = handleLine.X2;
headLine.Y1 = handleLine.Y2;
headLine.X2 = handleLine.X2 + headVector.X;
headLine.Y2 = handleLine.Y2 + headVector.Y;
//malletCanvas.Children.Add(headLine);
malletPosition = new System.Windows.Vector(j1P.X, j1P.Y);
malletPosition = malletPosition + (malletDirection * (malletHandleLength + (malletHeadLength / 2)));
malletValid = true;
}
This is how the code for detecting if the mallet hits the objects looks like
// Declare Hit Vector for each Dollar Note
System.Windows.Vector _spiderHitVector = new System.Windows.Vector(malletPosition.X - _spiderCenterX, malletPosition.Y - _spiderCenterY);
Does anyone have any resources or give me some hints on how to work on this?
Im wondering if its possible to add colored triangles (pointing up or down) at specific points (x,y) on an existing chart, such that they will update their positions if the chart is scrolled/zoomed.
Ive had a google around but havent stumbled across anything which seems to meet those requirements.
Any pointers would be appreciated.
Draw a point chart with markers.
Create your own image and use MarkerImage property to load custom image
Answer Explaining how to get your inverted triangles
Custom MarkerStyles possible in Microsoft Chart Controls?
As described inside the MS Chart Samples, you could probably use annotations for this
private void AddLineAnnotation()
{
LineAnnotation annotation = new LineAnnotation();
annotation.AnchorDataPoint = Chart1.Series[0].Points[2];
annotation.Height = -25;
annotation.Width = -25;
annotation.LineWidth = 2;
annotation.StartCap = LineAnchorCapStyle.Arrow;
annotation.EndCap = LineAnchorCapStyle.Arrow;
Chart1.Annotations.Add(annotation);
}
You can even define a custom polygon as annotation
private void AddPolygonAnnotation()
{
PolygonAnnotation annotation = new PolygonAnnotation();
annotation.AnchorDataPoint = Chart1.Series[0].Points[2];
// explicitly set the relative height and width
annotation.Height = 50;
annotation.Width = 30;
annotation.BackColor = Color.FromArgb(128, Color.Orange);
annotation.LineColor = Color.Black;
annotation.LineDashStyle = ChartDashStyle.Solid;
// define relative value points for a polygon
PointF [] points = new PointF[5];
points[0].X = 0;
points[0].Y = 0;
points[1].X = 100;
points[1].Y = 0;
points[2].X = 100;
points[2].Y = 100;
points[3].X = 0;
points[3].Y = 100;
points[4].X = 50;
points[4].Y = 50;
annotation.Path.AddPolygon(points);
Chart1.Annotations.Add(annotation);
}
I am trying to make a zoom effect on a picturebox with mouse wheel. Everything is OK except that when I use mouse middle button to zoom in or zoom out, it is ok, but it does not zoom in or zoom out the point which the mouse cursor is on. When I zoom in the point, I want it always slide. Please help me add a code snippet to make it work.
Here is my code:
int i = 5;
int index = 10;
private double[] zoomfactor = { .25, .33, .50, .66, .80, 1, 1.25, 1.5, 2.0, 2.5, 3.0 };
private void Zoom(int i)
{
double new_Zoom = zoomfactor[i];
imgBox.Width = Convert.ToInt32(imgBox.Image.Width * new_Zoom);
imgBox.Height = Convert.ToInt32(imgBox.Image.Height * new_Zoom);
}
private void On_wheel(object sender, System.Windows.Forms.MouseEventArgs e)
{
i = i + e.Delta / 120;
if (i < 0)
{
i = 0;
}
else
{
if (i <= index)
i = i;
else
i = index;
}
Zoom(i);
}
You need to adjust the picture box location based on the mouse position relative to the form.
Here is a rough but working example of how you might do this:
var i = 5;
var zoomfactor = new[] {.25, .33, .50, .66, .80, 1, 1.25, 1.5, 2.0, 2.5, 3.0};
var origin = new Point(100, 100);
var image = Image.FromFile(#"c:\example.png");
var imgBox = new PictureBox {
Location = origin,
Size = image.Size,
Image = image,
SizeMode = PictureBoxSizeMode.StretchImage
};
var form = new Form {
Size = new Size(800, 600),
Controls = {imgBox}
};
form.MouseWheel += (sender, e) => {
i += e.Delta/120;
if (i < 0) {
i = 0;
}
if (i >= zoomfactor.Length) {
i = zoomfactor.Length - 1;
}
var newZoom = zoomfactor[i];
imgBox.Width = (int) (imgBox.Image.Width*newZoom);
imgBox.Height = (int) (imgBox.Image.Height*newZoom);
imgBox.Left = (int) (e.X - newZoom*(e.X - origin.X));
imgBox.Top = (int) (e.Y - newZoom*(e.Y - origin.Y));
};
form.ShowDialog();
You are not taking the mouse coordinates into account.
The MouseEventArgs class tells you where the mouse is (X, Y and Location properties), so you need to adjust accordingly.