I’ve been doing a project in Visual Studio 2022 in C# using WPF, based on the Cobbs method to measure the degree of scoliosis.
Basically for you to understand how I have taken that method to code what I did was that when drawing two lines in real time where they cut they form an angle and that angle is the Cobbs angle which is the one that tells you the degree of scoliosis.
I have been trying to figure out how to paint a second red dot on the WPF canvas to finish drawing a first line because the first and second dots determine where the line starts and ends. The problem is that when I do the second click anywhere on the canvas but with the cursor not moving it does not draw the second red dot, it only draws the second red dot if I click with the cursor moving, which is very uncomfortable for a doctor using this program, and the same thing happens to me with the second line.
However when I make the first click in any part of the canvas without moving the cursor in the case of the first red point where the line begins if it draws it, and repeats the same process with the second line. And the rest if it works perfectly, I just needed to know if there was someone who knew about this if you could comment something you know or any experience. Thanks for watching guys.
I have tried everything, modifying the code, but I don’t know why I feel that it is a tiny error, but I can’t find it.
Here is the code of MainWindow.xaml.cs
:
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
namespace CobbAngleMeasurement
{
public partial class MainWindow : Window
{
private Point? startPoint = null;
private Point? endPoint = null;
private Point? secondStartPoint = null;
private Point? secondEndPoint = null;
private Point? currentPoint = null;
private bool drawingFirstLine = true;
private int lineCount = 0;
public MainWindow()
{
InitializeComponent();
}
private void Canvas_MouseDown(object sender, MouseButtonEventArgs e)
{
if (lineCount >= 2)
{
startPoint = null;
endPoint = null;
secondStartPoint = null;
secondEndPoint = null;
lineCount = 0;
canvas.Children.Clear();
}
if (drawingFirstLine)
{
if (!startPoint.HasValue)
{
startPoint = e.GetPosition(canvas);
DrawPoint(startPoint.Value);
}
else
{
endPoint = e.GetPosition(canvas);
drawingFirstLine = false;
lineCount++;
DrawPoint(endPoint.Value);
DrawLine(startPoint.Value, endPoint.Value);
}
}
else
{
if (!secondStartPoint.HasValue)
{
secondStartPoint = e.GetPosition(canvas);
DrawPoint(secondStartPoint.Value);
}
else
{
secondEndPoint = e.GetPosition(canvas);
drawingFirstLine = true;
lineCount++;
DrawPoint(secondEndPoint.Value);
DrawLine(secondStartPoint.Value, secondEndPoint.Value);
}
}
currentPoint = null;
e.Handled = true;
// Asegúrate de que el evento se maneje correctamente
}
private void Canvas_MouseMove(object sender, MouseEventArgs e)
{
if (startPoint.HasValue && !endPoint.HasValue)
{
currentPoint = e.GetPosition(canvas);
canvas.Children.Clear();
DrawAllElements();
DrawLine(startPoint.Value, currentPoint.Value);
}
else if (secondStartPoint.HasValue && !secondEndPoint.HasValue)
{
currentPoint = e.GetPosition(canvas);
canvas.Children.Clear();
DrawAllElements();
DrawLine(secondStartPoint.Value, currentPoint.Value);
if (startPoint.HasValue && endPoint.HasValue)
{
float intersectionAngle = CalculateIntersectionAngle(startPoint.Value, endPoint.Value, secondStartPoint.Value, currentPoint.Value);
DrawAngle(intersectionAngle, secondStartPoint.Value);
}
}e.Handled = true;
// Asegúrate de que el evento se maneje correctamente
}
private void Canvas_MouseEnter(object sender, MouseEventArgs e)
{
this.Cursor = Cursors.Cross;
}
private void Canvas_MouseLeave(object sender, MouseEventArgs e)
{
this.Cursor = Cursors.Arrow;
}
private void btnCalculateAngle_Click(object sender, RoutedEventArgs e)
{
if (startPoint.HasValue && endPoint.HasValue && secondStartPoint.HasValue && secondEndPoint.HasValue)
{
float intersectionAngle = CalculateIntersectionAngle(startPoint.Value, endPoint.Value, secondStartPoint.Value, secondEndPoint.Value);
MessageBox.Show($"El ángulo de intersección es: { intersectionAngle:F2} grados", "Ángulo Calculado", MessageBoxButton.OK, MessageBoxImage.Information);
}
else
{
MessageBox.Show("Por favor, dibuje ambas líneas antes de calcular el ángulo.", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void DrawLine(Point p1, Point p2)
{
Line line = new Line { X1 = p1.X,Y1 = p1.Y,X2 = p2.X,Y2 = p2.Y,Stroke = Brushes.Red,StrokeThickness = 2} ;
canvas.Children.Add(line);
}
private void DrawPoint(Point p)
{
Ellipse ellipse = new Ellipse { Fill = Brushes.Red,Width = 6,Height = 6} ;
Canvas.SetLeft(ellipse, p.X - 3);
Canvas.SetTop(ellipse, p.Y - 3);
canvas.Children.Add(ellipse);
}
private void DrawAllElements()
{
if (startPoint.HasValue)
{
DrawPoint(startPoint.Value);
}
if (endPoint.HasValue)
{
DrawPoint(endPoint.Value);
DrawLine(startPoint.Value, endPoint.Value);
}
if (secondStartPoint.HasValue)
{
DrawPoint(secondStartPoint.Value);
}
if (secondEndPoint.HasValue)
{
DrawPoint(secondEndPoint.Value);
DrawLine(secondStartPoint.Value, secondEndPoint.Value);
}
}
private void btnClear_Click(object sender, RoutedEventArgs e)
{
startPoint = null;
endPoint = null;
secondStartPoint = null;
secondEndPoint = null;
lineCount = 0;
canvas.Children.Clear();
}
private void DrawAngle(float angle, Point position)
{
TextBlock angleText = new TextBlock
{
Text = $" { angle:F2} °",Foreground = Brushes.Red,FontWeight = FontWeights.Bold};
Canvas.SetLeft(angleText, position.X + 10);
Canvas.SetTop(angleText, position.Y - 20);
canvas.Children.Add(angleText);
if (angleTextBlock != null)
{
angleTextBlock.Text = $" { angle:F2} °";
}
}
}
private float CalculateAngle(Point p1, Point p2)
{
double deltaY = p2.Y - p1.Y;
double deltaX = p2.X - p1.X;
return (float)(Math.Atan2(deltaY, deltaX) * (180.0 / Math.PI));
}
private float CalculateIntersectionAngle(Point p1, Point p2, Point p3, Point p4)
{
float angle1 = CalculateAngle(p1, p2);
float angle2 = CalculateAngle(p3, p4);
float intersectionAngle = Math.Abs(angle1 - angle2);
if (intersectionAngle >= 90)
{
intersectionAngle = 180 - intersectionAngle;
}
if (intersectionAngle < 0)
{
intersectionAngle = -intersectionAngle;
}
if (intersectionAngle == 90)
{
return 90;
}
if (intersectionAngle > 90)
{
intersectionAngle = 180 - intersectionAngle;
}
return intersectionAngle;
}
}
}
MainWindow.xaml
markup:
<Window x:Class="CobbAngleMeasurement.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Medición del Ángulo de Cobb" Height="900" Width="1024"
>
<Grid>
<Canvas
Name="canvas" Width="800" Height="800"
MouseDown="Canvas_MouseDown"
MouseMove="Canvas_MouseMove"
MouseEnter="Canvas_MouseEnter"
MouseLeave="Canvas_MouseLeave" Margin="0,0,94,77"
>
<Canvas.Background>
<ImageBrush ImageSource="/Cobbs_angulo.jpg" Stretch="None"/>
</Canvas.Background>
<!-- Aquí se dibujarán las líneas y puntos -->
</Canvas>
<TextBlock Name="angleTextBlock" Foreground="Red" FontWeight="Bold" FontSize="16"
Margin="10,10,0,0" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<Button Name="btnCalculateAngle" Content="Calcular Ángulo" Width="120" Height="40"
HorizontalAlignment="Right" VerticalAlignment="Bottom"
Margin="0,0,20,20" Click="btnCalculateAngle_Click"/>
<Button Content="Limpiar" Click="btnClear_Click" Width="120" Height="40"
HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="0,0,20,70"/>
</Grid>
</Window>
2
Answers
It is a bit unclear how exactly your line creation and drawing is supposed to work.
You may perhaps want to start drawing a line on mouse down and end drawing it on mouse up. A first mouse down/move/up sequence would draw the first line and a second such sequence would draw the second line. A third mouse down would clear the drawing.
The drawing code could be much simplified by using a single
Path
element as a child element the Canvas:The code behind would look like shown below.
It updates the
Data
property of thePath
with aGeometryGroup
containing the lines and points. Note that the mouse should be captured and that theIsCaptured
state of the mouse is used to determine whether a line is currently drawn or not.The
UpdateDrawing
method would also update the existing TextBlock namedangleText
.The "2 clicks" is a problem; try capturing the points on "down and up" instead:
(Tap line to delete?)