Clip an ellipse in WPF - positioning off - c#

I am trying to 'cut' of part of several ellipses, in what is quite a big program. Since I had troubles making this work, I have started a new project in which I tried to solve the solution on a small scale. Again, I get some very weird results - which I expect have to do with positioning. Please see the below code for a minimal working example.
The program creates an ellipse, gives it a pretty colour and places it on the stage. It then proceeds to create what is called a 'RectangleGeometry', which we will use for the clipping. Please note that the geometry is placed at 0,0 with a width of 40 and height of 200. The result can be seen in the following screenshot;
My end goal is this: to be able to place a RectangleGeometry at any position (lets say 200,300) and have it clip the ellipse at exactly that position.
Ellipse abcEllipse = new Ellipse{
Margin = new Thickness(100, 100, 0, 0),
Fill = Brushes.HotPink,
Height = 80,
Width = 60
};
DrawCanvas.Children.Add(abcEllipse);
RectangleGeometry clipRectangle = new RectangleGeometry {
Rect = new Rect(0, 0, 40, 200)
};
GeometryGroup myGeometryGroup1 = new GeometryGroup();
myGeometryGroup1.Children.Add(clipRectangle);
Path myPath1 = new Path { Stroke = Brushes.Black, StrokeThickness = 1 };
SolidColorBrush mySolidColorBrush = new SolidColorBrush();
mySolidColorBrush.Color = Color.FromArgb(255, 204, 204, 255);
myPath1.Opacity = 0.2;
myPath1.Fill = mySolidColorBrush;
myPath1.Data = myGeometryGroup1;
DrawCanvas.Children.Add(myPath1);
abcEllipse.Clip = clipRectangle;
Update: Some more clarification might indeed be required; I want to achieve the following effect - as seen in the attached image. An ellipse is placed on the stage, it is rotated and the top of the ellipse is cut off. (In such a way that it 'respects' the new rotation and thus cuts at what is now the new top)
However; this is the result I get when I apply the knowledge that "the clip geometry assigned to the shape will be relative to that shape;". As you can see, it doesn't really cut at the 'new' top of the ellipse.
Update 2: Added the code used to rotate the ellipse;
TransformGroup tg = new TransformGroup();
tg.Children.Add(new RotateTransform(40));
abcEllipse.RenderTransform = tg;

Related

WPF C# draw smooth rectangles on mouse move without lacking with Black outer area

I am drawing rectangles for screen captures but when I draw it on mouse move it looks like it lacking and following mouse and not drawing smoothly.
But I want Drawing with smoothness
My code:
I have four rectangles and when I started drawing I give them margins between them and show our selected area in original color.
But when we remove outer four rectangles which make black area(outside of the rectangles) it works fast from before but I also want black outer areas as well.
My Code for outer rects:
rect_topleft.Visibility = Visibility.Visible;
rect_topleft.Margin = new Thickness(0, 0, 0, h - sY);
rect_bottomleft.Visibility = Visibility.Visible;
rect_bottomleft.Margin = new Thickness(0, startY, w - startX, -25);
rect_bottomright.Visibility = Visibility.Visible;
rect_bottomright.Margin = new Thickness(startX, endY, pictureBox2.Width - endX, 0); //Here picturebox2 is grid
rect_topright.Visibility = Visibility.Visible;
rect_topright.Margin = new Thickness(endX, startY, 0, -25);
EDIT:-
After Using Clemens Code :
it works fine on seperate project as shown in answer but it still lag in my code when i integrate it
Any solution for my code or any other method for doing this :

How to render InkStroke on Canvas using BezierSegment in UWP C#

I am porting an application from javascript to UWP c# and I am struggling with the new InkCanvas. If you are familiar with the new InkCanvas in UWP, I would truly appreciate your help.
This is the code I wrote that takes renders an InkStroke onto a Canvas.
public static void Bezier(Canvas canvas, InkStroke stroke)
{
var segments = stroke.GetRenderingSegments();
PathFigure pthFigure = new PathFigure() { StartPoint = new Point(segments[0].Position.X, segments[0].Position.Y)};
for (int i = 1; i < segments.Count; i++)
{
var segment = segments[i];
var bezier = new BezierSegment();
bezier.Point1 = new Point(segment.BezierControlPoint1.X, segment.BezierControlPoint1.Y);
bezier.Point2 = new Point(segment.BezierControlPoint2.X, segment.BezierControlPoint2.Y);
bezier.Point3 = new Point(segment.Position.X, segment.Position.Y);
pthFigure.Segments.Add(bezier);
}
PathGeometry pthGeometry = new PathGeometry();
pthGeometry.Figures.Add(pthFigure);
Path path = new Path();
//path.Stroke = new SolidColorBrush(stroke.DrawingAttributes.Color);
//path.StrokeThickness = stroke.DrawingAttributes.Size.Height;
path.Stroke = new SolidColorBrush(Colors.Red);
path.StrokeThickness = 1;
path.Data = pthGeometry;
canvas.Children.Add(path);
}
Unfortunately, the control points seem to be messed up, and I do not understand why.
You can see below an image of what I get running this code.
The black stroke is the one originally rendered in the InkCanvas, and the red stroke is the one rendered on the Canvas from the code above.
The black stroke is the one originally rendered in the InkCanvas, and the red stroke is the one rendered on the Canvas from the code above
Anyone has any idea of what I am doing wrong?
I found the problem! I was setting drawingAttributes.FitToCurve = false and this caused the control points to be zero. I assumed this setting was only affecting the rendering, but now it makes sense because I was calling GetRenderingSegments. I was able to draw the shape by using GetInkPoints and then draw a polyline.

Set UI Element Position to Mouse Position

I wrote a little Program that should display a Ellipse at the exact mouse position. The Problem is that, the way Iam doing it right now , The Mouse and Ellipse Position are only exact at the center of the Screen. If I put the mouse further away to the windowborder they drift further and further away.
I use the MouseOver Element to Update the Mouse Position.
Here is my code:
private void Window_MouseMove(object sender, MouseEventArgs e)
{
Main_Grid.Children.Clear();
MousePos_Ellipse = new Ellipse();
Point MousePos_Point = new Point();
MousePos_Point = Mouse.GetPosition(Main_Grid);
Main_Grid.Children.Remove(MousePos_Ellipse);
SolidColorBrush mySolidColorBrush = new SolidColorBrush();
mySolidColorBrush.Color = Color.FromArgb(55, 255, 255, 0);
MousePos_Ellipse.Fill = mySolidColorBrush;
MousePos_Ellipse.StrokeThickness = 2;
MousePos_Ellipse.Stroke = Brushes.Black;
// Set the width and height of the Ellipse.
MousePos_Ellipse.Width = 15;
MousePos_Ellipse.Height = 15;
// At this Point I do my Positioning
MousePos_Ellipse.Margin = new Thickness(left: MousePos_Point.X - ( Main_Grid.ActualWidth / 2) , top: MousePos_Point.Y - ( Main_Grid.ActualHeight / 2 ), right: 0 , bottom: 0);
//base.AddVisualChild(_circle);
// Add the Ellipse to the Grid
Main_Grid.Children.Add(MousePos_Ellipse);
}
I propose to use a Canvas instead of a grid.
With a canvas you can simply set the ellipse position like that:
Canvas.SetLeft(MousePos_Ellipse, MousePos_Point.X);
Canvas.SetTop(MousePos_Ellipse, MousePos_Point.Y);
The Grid control will do automatic positioning and sizing of child elements and is therefore not so suitable for your goal.
Disclaimer; while the answers above will fix your issue, the actual question is not properly resolved. The problem you are facing derives from your interpretation of the issue compared to how the computer sees it.
The position of your cursor relative to the grid is not the same as the position relative to your screen (i.e. different resolutions return different values). This is why our x and y values will be further off the further you get off center. You could fix this by defining that you want your X and Y position relative to the form, f.ex, like so:
var relativePoint = this.PointToClient(Cursor.Position);
The noticable difference here is that here, we Point to the client, and therefore get the Cursor's relative position within the form.

C# WPF -> How to get the coordinates from a stretched(uniform) polygon on a grid

I searched a lot to find a solution for my problem. Couldn't find anything.
This is what I do:
I render a polygon via point array on a grid. The polygon autoresizing itself to match the grids size(uniform). I get the new stretched coordinates with the following lines and create the ellipses with a method:
var transform = myPolygon.RenderedGeometry.Transform;
foreach (var point in myPolygon.Points)
{
var transformedPoint = transform.Transform(point);
CreateEllipse(5, 5, Convert.ToDouble(Convert.ToString(transformedPoint).Split(';')[0]), Convert.ToDouble(Convert.ToString(transformedPoint).Split(';')[1]), gridPoly);
}
Ellipse CreateEllipse(double width, double height, double desiredCenterX, double desiredCenterY, Grid grid)
{
Ellipse ellipse = new Ellipse { Width = width, Height = height };
double left = desiredCenterX - (width / 2);
double top = desiredCenterY - (height / 2);
ellipse.Stroke = System.Windows.Media.Brushes.Black;
ellipse.Fill = System.Windows.Media.Brushes.DarkBlue;
ellipse.Margin = new Thickness(left, top, 0, 0);
grid.Children.Add(ellipse);
return ellipse;
}
The problem is, if I put the polygon on a grid and the ellipses afterwards, they will render exact the half size and not at the correct position, not even relative. Same happens if I set stretch to 'none'.
If I render my polygon and ellipses on a canvas, they render exact on each polygon point and everything is perfect. Beside, canvas doesn't support stretch.Uniform. And I need to dynamically fit the polygon into its parent object.
My question is, how do I render ellipses on an auto resizing polygon using the point positiondata?
You have to take two more things into account.
First, the Grid may align (i.e. move) the whole stretched Polygon according to its HorizontalAlignment and VerticalAlignment properties. If you don't want to calculate that by yourself, you could get an appropriate GeneralTransform object by myPolygon.TransformToAncestor(grid):
var polygonGeometryTransform = myPolygon.RenderedGeometry.Transform;
var polygonToGridTransform = myPolygon.TransformToAncestor(gridPoly);
foreach (var point in myPolygon.Points)
{
var transformedPoint = polygonToGridTransform.Transform(
polygonGeometryTransform.Transform(point));
CreateEllipse(5, 5, transformedPoint.X, transformedPoint.Y, gridPoly);
}
Second, when you do an absolute positioning of the Ellipses relative to the upper left corner of the Grid, you would have to set their HorizontalAlignment to Left and the VerticalAlignment to Top:
ellipse.HorizontalAlignment = HorizontalAlignment.Left;
ellipse.VerticalAlignment = VerticalAlignment.Top;
grid.Children.Add(ellipse);
That said, you wouldn't typically set an element's Margin for absolute positioning. It would be much cleaner to use a separate Canvas for the Ellipses and set their Canvas.Left and Canvas.Top properties.

System.Drawing.Bitmap GetBounds GraphicsUnit.Inch

Can anybody tell me how to get a rectangle back from GetBounds in any units OTHER than pixels? The following code - lifted directly off the MSDN documentation for this function - returns a rectangle that is pretty obviously in pixels rather than points (1/72 of an inch). (Unless icons come in a size of 32/72"x32/72" rather than 32x32 pixels like I think). I am most interested in working with a rectangle in inches, but I would settle for simply seeing the GetBounds pageUnit parameter cause a change in the returned rectangle.
Bitmap bitmap1 = Bitmap.FromHicon(SystemIcons.Hand.Handle);
Graphics formGraphics = this.CreateGraphics();
GraphicsUnit units = GraphicsUnit.Point;
RectangleF bmpRectangleF = bitmap1.GetBounds(ref units);
Rectangle bmpRectangle = Rectangle.Round(bmpRectangleF);
formGraphics.DrawRectangle(Pens.Blue, bmpRectangle);
formGraphics.Dispose();
The Information is a little sparse on this, I was able to find this MSDN Forum posting that suggests since the Bitmap is already created the units have already been set and are not changable. Since the GraphicsUnit is being passed by a reference, it you look at it after the call you will find it set back to Pixel from Inch. If you actually want to change the size that the rectangle is drawn at set the Graphics.PageUnit Property on formGraphics to the GraphicsUnit you want to draw the Rectangle at.
From above Link:
In this sample, the parameters of Image.GetBounds method don’t change the result, because the bound of Bitmap has been decided. The parameters only determine the unit length to deal with the range, inch by inch or point by point. But the parameters will not influence the result.
emphasis mine
A bit late answering this one, but I thought I would do so because I found it in Google when trying to answer the question "how many mm can I fit in my picture box?", it would have saved me a lot of time not having to work out how to do it!. GetBounds is useless (if you wanted it in pixels...) but it is possible to find the relation between drawing units and display pixels using the Graphics.TransformPoints method:
private void Form1_Load(object sender, EventArgs e)
{
Bitmap b;
Graphics g;
Size s = pictureBox1.Size;
b = new Bitmap(s.Width, s.Height);
g = Graphics.FromImage(b);
PointF[] points = new PointF[2];
g.PageUnit = GraphicsUnit.Millimeter;
g.PageScale = 1.0f;
g.ScaleTransform(1.0f, 1.0f);
points[0] = new PointF(0, 0);
points[1] = new PointF(1, 1);
g.TransformPoints(CoordinateSpace.Device, CoordinateSpace.Page, points);
MessageBox.Show(String.Format("1 page unit in {0} is {1} pixels",g.PageUnit.ToString(),points[1].X));
points[0] = new PointF(0, 0);
points[1] = new PointF(1, 1);
g.TransformPoints(CoordinateSpace.Page, CoordinateSpace.World, points);
MessageBox.Show(String.Format("1 page unit in {0} is {1} pixels",g.PageUnit.ToString(),points[1].X));
g.ResetTransform();
pictureBox1.Image = b;
SolidBrush brush = new SolidBrush(Color.FromArgb(120, Color.Azure));
Rectangle rectangle = new Rectangle(10, 10, 50, 50);
// Fill in the rectangle with a semi-transparent color.
g.FillRectangle(brush, rectangle);
pictureBox1.Invalidate();
}
This will display the basic mm to display pixels (3.779527 in my case) - the world coordinates are 1 mm per pixel, this would change if you applied graphics.ScaleTransform.
Edit: Of course, it helps if you assign the bitmap to the pictureBox image property (and keep the Graphics object to allow changes as required).
Add label
In class Form1 Add field
PointF[] cooridates;
Form1.cs [design] look for lighting bolt in properties double click Paint create handler
Form1_Paint(object sender,PaintEventArgs)
{
e.Graphics.PageUnit = GraphicsUnit.Inch;
if (cooridates != null)
e.Graphics.TransformPoints(CoorinateSpace.World,
CoorinateSpace.Device,cooridates);
}
Create handler again for Form1.MouseMove
Form1_MouseMove(object sender,MouseEventArgs e
{
cooridates[0].X = e.Location.X;
cooridates[0].Y = e.Location.Y;
this.Refresh();
label1.Text = $"X = {cooridates[0].X} Y = {
{ cooridates[0].Y } ";
}
Form1_Load(object sender,MouseEventArgs)
{
cooridates = new PointF[1] { new PointF(0f,0f) };
}
Move mouse to get cooridates in Inches

Categories