C# drawing with a restriction - c#

I'm working with a project that gets information out of a DXF file(a drawing file from autocad or any other drawing program). I get information from it and it shows the borders of a panel. In this panel I need to set lines, but when there are lines with coordinates OUTSIDE the panel I'm not allowed to draw them.
Below you can find an image, maybe it makes a bit more clear.
So the white lines are bricks and they need glue lines(yellow), The aquamarine line is the contour line. Outside this line it is not allowed to draw Glue lines(the yellow lines)
As you can see on brick with NR 4BA87 it draws glue lines outside the panel.
I need some sort of check if a point is inside my contour/panel I can draw it else don't draw it. How can I do this?
edit:
I draw these lines from a list so it would be great if I could remove(or not add them) the gluelines from my list that are outside my panel.
I've got a list with Points that are the contourpoints:
public List<PointF> ListContourPoints = new List<PointF>();
And I've got a list with glue lines:
List<GlueLine> glueLines = new List<GlueLine>();
my GlueLine class has 2 PointFin it, StartPosition and EndPosition.
It would be nice if I get something like:
if(GlueLines.StartPosition is INSIDE panel && GlueLines.EndPosition is INSIDE panel)
{
glueLines.Add(gl);
}

You have got the positional data of all the Points that make up the corners of the panel. So all you need to do is find out the rectangular shapes that make up the panel and then do positional checking on the lines.
An example function to create the rectangles would look something like the below, Note however that this function is created under the assumption that the panel is in the same shape as the example one provided, some more logic will be needed to create one which encompasses all of the variations:
private static Rectangle[] SplitPointsIntoRectangles(Point[] pa)
{
pa = pa.OrderBy(p => p.X).ToArray();
Point[] leftmost = pa.Select(p => p).Where(p => p.X == pa[0].X).ToArray();
Point[] rightmost = pa.Select(p => p).Where(p => p.X == pa[pa.Length - 1].X).ToArray();
pa = pa.OrderBy(p => p.Y).ToArray();
Point[] topmost = pa.Select(p => p).Where(p => p.Y == pa[0].Y).ToArray();
Point[] bottommost = pa.Select(p => p).Where(p => p.Y == pa[pa.Length - 1].Y).ToArray();
List<Point> edges = new List<Point>();
edges.AddRange(leftmost);
edges.AddRange(rightmost);
edges.AddRange(topmost);
edges.AddRange(bottommost);
Point middlePixel = pa.FirstOrDefault(p => !edges.Contains(p));
Rectangle[] ra = new Rectangle[2];
ra[0] = new Rectangle(leftmost[0].X, leftmost.Min(p => p.Y), rightmost[0].X - leftmost[0].X, bottommost[0].Y - leftmost[0].Y);
ra[1] = new Rectangle(topmost.Min(p => p.X), topmost[0].Y, rightmost[0].X - middlePixel.X, middlePixel.Y - topmost[0].Y);
return ra;
}
Once we have the rectangles we then need to make sure the lines are not outside of them, for this we can use the Rectangle.Intersects method. For this we use the points of each of the glue lines to create a rectangle, like so (assuming all these lines are horizontal):
Rectangle glueLineRect = new Rectangle(glueLineStartPoint.X, glueLineStartPoint.Y, glueLineEndPosition.X - glueLineStartPoint.X, 1);
If the following function returns an empty rectangle then the two rectangles do not intersect, else they do. Note: this function will return a none empty rectangle if the rectangles partially intersect.
Rectangle intersectionRect = Rectangle.Intersect(MainRect, glueLineRect);
This can then be extended to test on all of the rectangles contained within the panel and if it fails on all of them then the glue line resides outside of the panel.
I created this answer to get you started, the SplitPointsIntoRectangles method needs to be made generic. Also the looping needs to created to test whether the Glue Lines reside inside of the panel or not.

Related

How to change ILNumerics plot marker type?

I have set of random generated points in 3D Scene, and in runtime I want to change the type of point markers to, for example, triangles, as in the picture:
Is it possible? How can I achieve this? Also I need change color for some points.
Scene initialization code below:
ILArray<float> points = ILMath.tosingle(ILMath.randn(3, 1000));
var scene = new ILScene
{
new ILPlotCube(twoDMode: false)
{
new ILPoints
{
Positions = points,
Color = null,
Size = 2
}
}
};
Markers (ILMarker) and 'points' (ILPoints) are very different beasts. Markers are much more flexible configurable, mostly look nicer and are much more expensive to render. They commonly consist out of a border (line shape) and a filled area (triangles shape) and come with a number of predefined looks.
ILPoints on the other hand are designed to be fast and easy. one can easily create millions of points without decreasing the plotting performance. Don't try this with markers! But such points are what they are: filled circles. It's it. No borders, no different shapes.
However, if you want to give it a try - even for the 1000 points in your questions - you can do so. Just use an ILLinePlot instead and configure a marker for it. You may set the line color to Color.Empty to have the markers showing up alone.
new ILLinePlot(points, lineColor: Color.Empty, markerStyle: MarkerStyle.TriangleDown)
In order to get individual colors for individual point markers you would split your markers (points) up into individual set of points. Create an ILLinePlot for each set of points using the scheme described above.
The part of your question dealing with 'dynamic' is also easy: You can change the type of markers as well as any other property at runtime. Here is one simple example which toogles the markers between red triangle markers and white rectangle markers by clicking anywhere on the scene:
ilPanel1.Scene.MouseClick += (_s, _a) => {
if (_a.DirectionUp) return;
var lp = ilPanel1.Scene.First<ILLinePlot>();
if (lp != null) {
if (lp.Marker.Style == MarkerStyle.TriangleDown) {
lp.Marker.Style = MarkerStyle.Rectangle;
lp.Marker.Fill.Color = Color.White;
} else {
lp.Marker.Style = MarkerStyle.TriangleDown;
lp.Marker.Fill.Color = Color.Red;
}
lp.Configure();
ilPanel1.Refresh();
}
};

Create Shape from Lines 2D c#

I have a project with a graphic object called GraphicsLine. What it does is simply draw line on every mousedown and stop on every mouseup, nothing complicated. It stores coordinates for the start and the end of the line (x,y). Now what I want to know is whenever a shape is created. For example, you create 4 lines that forms a square, I want to be able to run an algorithm that can tell me that there is a square in the drawing.
Note that the shape can be anything that is "closed". Not only square, rectangle or triangle.
The goal of this is to calculate the area of the created shapes.
Is there something that already exists for doing this? I've been struggling to find something that could fit my needs.
EDIT 1:
I added some additionnal information :
Lines are either "cliped" to another line start or end point or they are not. There is no close closure, it is on the same point or not closed at all. 1 line can be used in multiple shapes.
EDIT 2 :
So basically, I want something that can give me an array of "GraphicsLine" that forms a shape. So if we have 6 lines in the drawing but 4 of them forms a square, I want something that returns those 4 lines so I can create another object from it.
Thanks in advance.
Please check this question How do I calculate the area of a 2d polygon? it is probably what you need, you just have to port it to C# :)
Edit: from #chmike answer:
Where x and y are the arrays of coordinates
var x = [10,10,20,20];
var y = [10,20,20,10];
var n = x.Length;
x[n] = x[0];
x[n+1] = x[1];
y[n] = y[0];
y[n+1] = y[1];
// compute area
int area = 0;
for(var i = 1; i <= n; ++i ) {
area += x[i]*( y[i+1] - y[i-1] );
}
Console.Write(area /= 2);
Take a look at this tutorial of AForge.NET Shape Checker
If your GraphicsLine object is drawing on a PictureBox or if you can convert that object to a bitmap, you can then run the code below.
You can easily try detection of quadrilaterals, this code here will actually highlight the detected objects, you do change the loop and make it do whatever you want instead:
// if you are using a PictureBox to draw the shapes, then convert it to a bitmap
Bitmap bitmap = new Bitmap(pictureBox1.Image)
// locate objects using blob counter
BlobCounter blobCounter = new BlobCounter( );
blobCounter.ProcessImage( bitmap );
Blob[] blobs = blobCounter.GetObjectsInformation( );
// create Graphics object to draw on the image and a pen
Graphics g = Graphics.FromImage( bitmap );
Pen bluePen = new Pen( Color.Blue, 2 );
// check each object and draw circle around objects, which are recognized as circles
for ( int i = 0, n = blobs.Length; i < n; i++ )
{
List<IntPoint> edgePoints = blobCounter.GetBlobsEdgePoints( blobs[i] );
List<IntPoint> corners = PointsCloud.FindQuadrilateralCorners( edgePoints );
g.DrawPolygon( bluePen, ToPointsArray( corners ) );
}
bluePen.Dispose( );
g.Dispose( );
I know it is 2 years + later, but I found a way with a recursive function to know when the shapes are "closed". You start from any points of the last drawed line, then you check if the other point is connected to another line. You do this until you reach the starting point. I save all the lines into another class called Polygon. This way, they keep all the lines with the start and finish that forms the polygon. Then to calculate the area, I do as Eduardo Cobuci said in his answer.
Hope this helps.

How to determine actual object corner points in image with transparent background

I have a set of images of various objects of different shapes and sizes. They have transparent backgrounds set on them but the full dimension of the image is a square. I want to calculate a box of coordinates (upper left x/y, lower right x/y) that encompasses the object in the image while ignoring as much of the transparent background as possible. And I need to do this on the fly in code.
Is there an example, or a library, available for C# that would allow me to do this? I am using these in a website where several objects are dynamically overlaid into a single image and I want to calculate an image map with coordinates for each object in the merged image. Using the full size of the square image creates huge overlaps in the coordinate sets and often the last in coordinates hide the lower object from being clickable.
Well, using System.Drawing.Bitmap this is not too hard (the following certainly is not the most performant way):
// we will store actual bounds in here
int left, right, top, bottom;
using (Bitmap b = ...) // open image here
{
var pixelsX = Enumerable.Range(0, b.Width);
var pixelsY = Enumerable.Range(0, b.Height);
left = pixelsX.FirstOrDefault(
x => pixelsY.Any(y => b.GetPixel(x, y).A != 0));
right = pixelsX.Reverse().FirstOrDefault(
x => pixelsY.Any(y => b.GetPixel(x, y).A != 0));
top = pixelsY.FirstOrDefault(
y => pixelsX.Any(x => b.GetPixel(x, y).A != 0));
bottom = pixelsY.Reverse().FirstOrDefault(
y => pixelsX.Any(x => b.GetPixel(x, y).A != 0));
}
Notice that all these 4 coordinates are "inclusive" bounds (meaning: the row/column of pixels they represent does contain at least one non-transparent pixel), so if you should calculate width and height of your new bounds do it like this:
int width = right - left + 1;
int height = bottom - top + 1;
By the way, for an entirely transparent image, all 4 coordinates should be 0, as a result width and height will both be 1 - I guess this is not a problem for you.

getting vertex points of GeometryModel3D to draw a wireframe

I've loaded a 3d model using Helix toolking like this
modelGroupScull = importer.Load("C:\\Users\\Robert\\Desktop\\a.obj");
GeometryModel3D modelScull = (GeometryModel3D)modelGroupScull.Children[0];
and I also have _3DTools that can draw lines from point-to-point in 3d space. now to draw a wireframe of my GeometryModel3D I guess I have to cycle to its vertexes and add them to ScreenSpaceLines3D.
ScreenSpaceLines3D wireframe = new ScreenSpaceLines3D();
// need to cycle through all vertexes of modelScull as Points, to add them to wireframe
wireframe.Points.Add(new Point3D(1, 2, 3));
wireframe.Color = Colors.LightBlue;
wireframe.Thickness = 3;
Viewport3D1.Children.Add(wireframe);
But... how do I actually get this vertex points?
EDIT:
Thanks for the answer. It did add the points
ScreenSpaceLines3D wireframe = new ScreenSpaceLines3D();
MeshGeometry3D mg3 = (MeshGeometry3D)modelScull.Geometry;
foreach (Point3D point3D in mg3.Positions)
{
wireframe.Points.Add(point3D);
}
wireframe.Color = Colors.LightBlue;
wireframe.Thickness = 1;
Viewport3D1.Children.Add(wireframe);
but the wireframe is messed up )
(source: gyazo.com)
maybe someone knows of other ways to draw wireframes? )
Normally the trigons are drawn with index buffers (to prevent extra rotations of vertices) Take a look at the TriangleIndices:
if you do something like this: (not tested it)
MeshGeometry3D mg3 = (MeshGeometry3D)modelScull.Geometry;
for(int index=0;index<mg3.TriangleIndices.Count; index+=3)
{
ScreenSpaceLines3D wireframe = new ScreenSpaceLines3D();
wireframe.Points.Add(mg3.Positions[mg3.TriangleIndices[index]]);
wireframe.Points.Add(mg3.Positions[mg3.TriangleIndices[index+1]]);
wireframe.Points.Add(mg3.Positions[mg3.TriangleIndices[index+2]]);
wireframe.Points.Add(mg3.Positions[mg3.TriangleIndices[index]]);
wireframe.Color = Colors.LightBlue;
wireframe.Thickness = 1;
Viewport3D1.Children.Add(wireframe);
}
But, this can create some overdraw (2 lines on the same coordinates) and probably very slow.
If you put each side into a list and use something like a Distinct on it, it will be better.
The problem with the ScreenSpaceLines3D is that will continue the line, instead of create 1 line (start/end).
If you can manage a algoritm that tries to draw you model with 1 line, it will go faster.
Wireframes are very slow in WPF. (because they are created with trigons)
You should find the vertex points in MeshGeometry3D.Positions Property
foreach (var point3D in modelScull.Geometry.Positions)

Determine which control is closest to the mouse pointer

In my C# (.NET 2) app I'd like to determine which control is closet to the mouse.
I can think of a few ways to do this that won't quite work right. I could use the Control.Location property, but that just gives me top/left, and the mouse might be on the other side of the control. I could calculate the center point of a control, but large controls would skew this (being near the edge of a control counts as being close to the control).
So basically I have a bunch of rectangles on a canvas and a point. I need to find the rectangle nearest to the point.
(Ideally I'd like to actually know the distance between the point and rectangle, too).
Any ideas?
You need to find the following:
- Distance to the closest corner
- Distance to the closest edge
- (optionally) distance to the center
Basically, you want the smaller of these three values. Take the min of that for two controls to determine which is closer.
Begin when you load the form by iterating all the controls on the form and creating a collection of the class below.
To find the closest control to a point, iterate the collection (see code at bottom). Keep track of the control with the minimum distance you've found so far. You can test for ContainsPoint() if you want... if you find a control where the point falls within the control bounds, you've got your control (so long as you don't have overlapping controls). Else, when you get to the end of the collection, the control you found with the shortest distance to the center/edge is your control.
public class HitControl {
public Control ThisControl;
private Rectangle ControlBounds;
private Point Center;
public HitControl (Control FormControl) {
ControlBounds = FormControl.Bounds;
Center = new Point(ControlBounds.X + (ControlBounds.Width/2), ControlBounds.Y + (ControlBounds.Height/2));
}
// Calculate the minimum distance from the left, right, and center
public double DistanceFrom(Point TestPoint) {
// Note: You don't need to consider control center points unless
// you plan to allow for controls placed over other controls...
// Then you need to test the distance to the centers, as well,
// and pick the shortest distance of to-edge, to-side, to-corner
bool withinWidth = TestPoint.X > ControlBounds.X && TestPoint.X < ControlBounds.X + ControlBounds.Width;
bool withinHeight = TestPoint.Y > ControlBounds.Y && TestPoint.Y < ControlBounds.Y + ControlBounds.Height;
int EdgeLeftXDistance = Math.Abs(ControlBounds.X - TestPoint.X);
int EdgeRightXDistance = Math.Abs(ControlBounds.X + ControlBounds.Width - TestPoint.X);
int EdgeTopYDistance = Math.Abs(ControlBounds.Y - TestPoint.Y);
int EdgeBottomYDistance = Math.Abs(ControlBounds.Y + ControlBounds.Height - TestPoint.Y);
int EdgeXDistance = Math.Min(EdgeLeftXDistance, EdgeRightXDistance);
int EdgeYDistance = Math.Min(EdgeTopYDistance, EdgeBottomYDistance);
// Some points to consider for rectangle (100, 100, 100, 100):
// - (140, 90): Distance to top edge
// - (105, 10): Distance to top edge
// - (50, 50): Distance to upper left corner
// - (250, 50): Distance to upper right corner
// - (10, 105): Distance to left edge
// - (140, 105): Distance to top edge
// - (105, 140): Distance to left edge
// - (290, 105): Distance to right edge
// - (205, 150): Distance to right edge
// ... and so forth
// You're within the control
if (withinWidth && withinHeight) {
return Math.Min(EdgeXDistance, EdgeYDistance);
}
// You're above or below the control
if (withinWidth) {
return EdgeYDistance;
}
// You're to the left or right of the control
if (withinHeight) {
return EdgeXDistance;
}
// You're in one of the four outside corners around the control.
// Find the distance to the closest corner
return Math.Sqrt(EdgeXDistance ^ 2 + EdgeYDistance ^ 2);
}
public bool ContainsPoint (Point TestPoint) {
return ControlBounds.Contains(TestPoint);
}
}
// Initialize and use this collection
List<HitControl> hitControls = (from Control control in Controls
select new HitControl(control)).ToList();
Point testPoint = new Point(175, 619);
double distance;
double shortestDistance = 0;
HitControl closestControl = null;
foreach (HitControl hitControl in hitControls) {
// Optional... works so long as you don't have overlapping controls
// If you do, comment this block out
if (hitControl.ContainsPoint(testPoint)) {
closestControl = hitControl;
break;
}
distance = hitControl.DistanceFrom(testPoint);
if (shortestDistance == 0 || distance < shortestDistance) {
shortestDistance = distance;
closestControl = hitControl;
}
}
if (closestControl != null) {
Control foundControl = closestControl.ThisControl;
}
First check the point is in any rectangle. If not, you can find the distance between your point and each line segment with the algorithm in this.
You can also find the 4 segments of your control, so you have a list (initiated first time) of four segments (determining the control sides) and now you can find the nearest segment, its nearest rectangle.
You have to think in terms of rectangles :)
Test: Is mouse within control?
If not: How far away from any single edge?
Then you have to know which controls you are interested in, the form is, for example, a control..
For starters, create method that will calculate distance from the rectangle edge to some arbitrary point. Signature for this method should be:
double DistanceFrom(Rect r, Point p);
Then, for the simplest try, iterate all controls, calculate distance, remeber the minimum distance and control that provided it.
For rectangle distance, check this out.
EDIT:
In fact, you can maintain a sorted list of controls so you can always have first one that is closer on top, and maintain that list as the mouse moves - it may prove to be more efficient in the terms of speed. Interesting issue though :)
I agree with Daniel that we need:
double DistanceFrom(Rect r, Point p);
But before that, we need:
double DistanceFrom(Line r, Point p);
and
double AngleBetweenPoints(Point p1, Point p2);

Categories

Resources