Drawing an arrow with an outline - c#

I'm trying to draw multiple arrows with a colored outline on a form, so far I'm using the following code:
Pen arrow_pen = new Pen(Color.FromArgb(r, g, b), 8);
arrow_pen.EndCap = System.Drawing.Drawing2D.LineCap.ArrowAnchor;
GraphicsPath arrow = new GraphicsPath();
arrow.AddLine(
coordinates1,
coordinates2,
coordinates3,
coordinates4
);
obj.DrawPath(arrow_pen, arrow);
obj.FillPath(Brushes.White, arrow);
arrow.Dispose();
arrow_pen.Dispose();
It draws the outline for the arrow, but then it dosent fill the path with white (obj.FillPath does nothing), am I missing something or am I doing this the wrong way?

The documentation says:
Fills the interior of a GraphicsPath.
Your path is only a single line and not closed. Therefore, according to the documentation:
If the path represented by the path parameter is not closed, an additional segment is added from the last point to the first point to close the path.
So now you have two identical lines spanning an area of exactly nothing. That area is filled but you cannot see it, as it's zero pixels wide. You need to define a closed graph that has an area greater than zero to actually fill something.

Related

WPF align Adorner with GuidelineSet

I want to create a simple Adorner that marks a selected element with a bounding box. I want it to be sharp and be exactly one pixel outside the target content. I found some useful code, but it does not work exactly as I wanted it to.
The OnRender method of my SelectAdorner is:
Rect adornedElementRect = new Rect(AdornedElement.DesiredSize);
SolidColorBrush renderBrush = new SolidColorBrush(Colors.Transparent);
Pen renderPen = new Pen(new SolidColorBrush(Colors.LightBlue), 1);
double halfPenWidth = renderPen.Thickness / 2;
// Create a guidelines set
GuidelineSet guidelines = new GuidelineSet();
guidelines.GuidelinesX.Add(adornedElementRect.Left + halfPenWidth);
guidelines.GuidelinesX.Add(adornedElementRect.Right + halfPenWidth);
guidelines.GuidelinesY.Add(adornedElementRect.Top + halfPenWidth);
guidelines.GuidelinesY.Add(adornedElementRect.Bottom + halfPenWidth);
drawingContext.PushGuidelineSet(guidelines);
drawingContext.DrawRectangle(renderBrush, renderPen, adornedElementRect);
The problem is that the bounding box is not aligned properly around the content (see second and third item). I want the result to be the one from the bottom item in the picture below.
Any ideas how can I achieve what I want?
Also, it would be nice to have the Adorner working in the same way with renderPen.Thickness greater than 1, in case I need that.
Thanks for the help.
The following code needs to be added before creating the GuidelineSet:
adornedElementRect = new Rect(
adornedElementRect.Left - halfPenWidth,
adornedElementRect.Top - halfPenWidth,
adornedElementRect.Width + renderPen.Thickness,
adornedElementRect.Height + renderPen.Thickness
);
The problem comes from the fact that adornedElementRect overlaps AdornedElement when the pen thickness is taken into consideration. The value of AdornedElement.DesiredSize corresponds to the outer edge of the border. That means that the Adorner is drawn half over the border and half outside the border. The GuidelineSet then aligns the right side of vertical edges and the bottom side of horizontal edges to pixel boundaries. This makes the drawing look sharp, however the edges are moved either to the inside, either to the outside of the AdornedElement. This is what creates the artifact.
When adding the specified changes to adornedElementRect, then it is drawn outside and right next to the AdornedElement. Now, the GuidelineSet aligns the Adorner similarly with the Border control. See this link for more information about how WPF draws the content on the screen.

Remove separated objects in an Image C#

your help is much appreciated. I am using C# and EmguCV for image processing
I have tried noise removal, but nothing happens. I also tried image median filter, and it only works on the first image, but it does not work on the second image. It only makes the second image blurry and the objects larger and more square-like.
I want to remove obviously distinct objects(green ones) in my image below so that it would turn all black because they are obviously separated and are not grouped unlike the second image below.
Image 1:
At the same way, I want to do it in my image below, but remove only those objects -- (the black ones) -- that are not grouped/(lumped?) so that what remains on the image are the objects that are grouped/larger in scale?
Image 2:
Thank you
You may try Gaussian Blur and then apply Threshold to the image.
Gaussian Blur is a widely used effect in graphics software, typically to reduce image noise and reduce detail, which I think matches your requirements well.
For your 1st image:
CvInvoke.GaussianBlur(srcImg, destImg, new Size(0, 0), 5);//You may need to customize Size and Sigma depends on different input image.
You will get:
Then
CvInvoke.Threshold(srcImg, destImg, 10, 255, Emgu.CV.CvEnum.ThresholdType.Binary);
You will get:
For your 2nd image:
CvInvoke.GaussianBlur(srcImg, destImg, new Size(0, 0), 5);//You may need to customize Size and Sigma depends on different input image.
You will get:
Then
CvInvoke.Threshold(srcImg, destImg, 240, 255, Emgu.CV.CvEnum.ThresholdType.Binary);
You will get:
Hope this help!
You should first threshold the image using Otsus method. Second run connected component analysis on the threshold image. Third go over all the component that you found and for the ones that have a size that is smaller than some min size delete it from the original image.
cvThreshold (with CV_THRESH_BINARY/CV_THRESH_BINARY_INV(choose according to the image) + CV_THRESH_OTSU) http://www.emgu.com/wiki/files/1.3.0.0/html/9624cb8e-921e-12a0-3c21-7821f0deb402.htm + http://www.emgu.com/wiki/files/1.3.0.0/html/bc08707a-63f5-9c73-18f4-aeab7878d7a6.htm
CvInvoke.FindContours (RetrType == External,ChainApproxNone)
For each contour that we found in 2 calculate CvInvoke.ContourArea
If area is smaller than minArea draw on the original image(The one you want to filter) the value you want for them(0 I suppose) in the original image using CvInvoke.DrawContours with the current contour and with thickness==-1 to fill the inside of the contour.

How to handle overlapping of dynamically placed labels

I am creating a visualizer in WPF to display flowfield information for a game I am writing and have come across a problem with some labels being very close to each other.
In the above screen shot, sector (0,0) is the top left. In sector (1,1) I have highlighted two labels with arrows that are very close to each other. In sector (2,1) I have circled two labels that overlap completly. I need to be able to place labels in a way so that they do not overlap and have a margin of distance. I am after preferably a simple algorithm that allows me to place labels on a contended spot.
The blue/black cells are virtualized items on an Items Control with a canvas as the ItemsPanel. The red sector squares are on one adorner while the green lines, boxes, bezier curves and red cost labels are on a second adorner. Both adorners use the drawing context with everything dynamically created upon render.
var typeface = new Typeface(new FontFamily("Segoe UI"), FontStyles.Normal, FontWeights.Normal, FontStretches.Normal);
var formattedText = new FormattedText(curve.Cost.ToString(), CultureInfo.CurrentUICulture, FlowDirection.LeftToRight, typeface, 12, Brushes.Red, null, TextFormattingMode.Display);
var textLocation = new Point(midPoint2.X - (formattedText.WidthIncludingTrailingWhitespace / 2), midPoint2.Y - formattedText.Height);
drawingContext.DrawText(formattedText, textLocation);
A suggestion:
The Voronoi diagram of a set of geometric entities is the partition of the plane into regions where points are closer to a given entity than to all others.
If you construct the Voronoi diagram of your curves, and if you place the labels wholly in the corresponding regions, this solves your problem.
Assuming that all labels have the same extent (same bounding box), you can find suitable empty spaces by applying an erosion operation, i.e. removing layers of pixels on the region outlines for the desired width/height. The remaining pixels are possible centers for the labels.
In the general case, computing a Voronoi diagram by geometric means is extremely difficult. But if you work with a digital image, it suffices to draw the geometric entities and compute the distance map from them.
This requires that you be somewhat familiar with the techniques of digital image processing.
After considering several ways to place labels includng
Word Clouds, physics based approaches, voronoi diagrams. I decided to base my approach on An Empirical Study of Algorithms
for Point-Feature Label Placement as I could see a simple and quick way to position labels. Page 2 gave me the idea of having four possible locations for a desired point and I built my own implementation with very simple rules.
I created a class called PointLabelPlacer with two methods
AddLabel
ComputeNewPositions
I would send all my labels along with the point to the AddLabel method.
Once I was done and ready I would call ComputeNewPositions. This would for all four possible locations count the number of locations from another labels that have overlapped.
I would also flag a location if it overlapped an original point of another label.
If two labels overlapped exactly, i would again choose the first one without overlaps, but I would mark all the other labels location as used
Then I would just choose the first one I found with the lowest number of overlaps and did not overlap another point and was not marked as used.
If after all that no alternate location could be found, I default to the top left and allow overlaps.
This is with the alternate locations displayed in yellow
This is the final result

Getting the top left coordinates of a WPF UIElement

I am working on extending the Microsoft resize Adorner example and need to be able to reposition the element after say the bottom left drag handle has been dragged.
So if I have a textbox of say 150 wide, 35 high postitioned on my form, and the bottom left drag handle changes the width to 200 wide, the right hand of the text box remains unchanged but the left hand edge moves to the left.
So I need to know the top left coordinates of the UIElement. I have tried Canvas.GetLeft and Canvas.GetTop but they return NaN which is confusing.
I just tried VisualTreeHelper.GetOffset which does return an offset but when you try and use it in the arrange method of the element it disappears, presumably as the values in the offset are too high.
In the days before Wpf the coordinate system was quite simple, wpf has overcomplicated things I think.
And if someone just wants the control's screen coordinates:
Point targetLoc = targetCtrl.PointToScreen(new Point(0, 0));
(this doesn't match the thread's description, but it does match the title. Figured it might help people coming in off search results)
You can transform coordinates of the UIElement to its parent. In your case it's a form. Here is an example of a method that returns coordinates of a visual:
private Point GetPosition(Visual element) {
var positionTransform = element.TransformToAncestor(MyForm);
var areaPosition = positionTransform.Transform(new Point(0, 0));
return areaPosition;
}

Finding out max available print area

I'm trying to find out max available area on my printer. I've printed a simple rectangle trying out different boundary variables. My question is, why doesn't first two work correctly? They don't print a full rectangle on the paper, only the left and top sides are drawn. Why does only the third one prints a full rectangle? I was under the impression of that all three should be working correctly. What am I missing?
Here's my code:
this.printDocument1.DefaultPageSettings.Margins = new Margins(0, 0, 0, 0);
...
private void PrintPage(object sender, PrintPageEventArgs e)
{
//Method 1, no right and bottom sides are printed
e.Graphics.DrawRectangle(new Pen(Color.Black, 1), e.PageBounds);
//Method 2, same as Method 1
e.Graphics.DrawRectangle(new Pen(Color.Black, 1), e.MarginBounds);
//Method 3, works correctly
e.Graphics.DrawRectangle(new Pen(Color.Black, 1), new Rectangle((int)e.Graphics.VisibleClipBounds.X, (int)e.Graphics.VisibleClipBounds.Y, (int)e.Graphics.VisibleClipBounds.Width, (int)e.Graphics.VisibleClipBounds.Height));
}
First one doesn't work because you are trying to print out of margins. Second one fails because you are trying to print over the margin, so the right and bottom lines fall 1 pixel off the bounds. Now 3rd one works IMO because, printing rectangle coordinates are floating point, and you are casting them to integers, thus rounding them down, so the rectangle falls inside the print area.
EDIT
Some additional info I found regarding your comment:
"If the Graphics object is using a nondefault PageUnit,[2] then VisibleClipBounds will be in different units than PageBounds (which is always in units of 100 dpi). To handle these variables, it's useful to have a helper method to return the "real" page bounds in a consistent unit of measure"
Check out this article , I believe it covers everything.
My first guess is that 2 is working with the margins you set previously (0,0,0,0) and that your printer doesn't actually support no-margin printing (most don't). Because of the fact that most printers require at least some margins, #1 will pretty much never work.
Method 3 appears to actually be querying the driver for the printable area of the page and then using that, so, it works.
As for why you get the top and left instead of nothing with #1 and #2, this is because you're just saying "print me a rectangle of these dimensions starting at the upper left hand corner of the printable area" not "print me a rectangle with these dimensions and start in the far upper left hand corner of the page where you can't actually print" so it's trying to, but it goes off the edge of the page since it's bigger than the printable area of the page.

Categories