I have certain objects in a 2D plane.I'm using a drawing techonology(drawing visuals) which draws the elements like it pushes them on a stack,first element is on the bottom second - on top of it and so on.Now,the problem is that I need all objects, except one of them(the background), on the same Z level because in the current state that my program is it happens to rotate in a sort of 3D way everything and it is supposed to rotate it in a 2D way.I understand that this explanation is NOT good that's why please refer to the images below.
Before rotating by theta angle :
After rotating by a theta angle :
You can see how the two lines start to overlap and this mustn't happen.They do get closer to each other as I rotate the figure and there's a certain angle where the two lines become fully overlapped and they look like one line.I want to avoid that.
The forumlae that I use for the rotation:
foreach(var item in Visuals){
var p = new Point(item.Position.X - center.X, item.Position.Y - center.Y);
var xnew = p.X * cos - p.Y * sin;
var ynew = p.X * sin + p.Y * cos;
p.X = xnew + center.X;
p.Y = ynew + center.Y;
item.Update(p.X, p.Y);
}
This is how I get the sin and cos of the angle
var pos = new Point(position.Y - center.Y, position.X - center.X);
var rad = Math.Atan2(pos.Y, pos.X);
var deg = rad.ToDegrees();
var diff = RotationLastAngle - deg;//The last angle that we rotated to.
RotationLastAngle = deg;
var ans = diff.ToRadians();
Host.Representation.Rotate(Math.Cos(ans), Math.Sin(ans), center);
Update() basically sets the coordinates of item in a single line.
What I think is causing the issue is that the DrawingVisual renders items on layers and thus one of the lines is higher than the other one(Correct me if I'm wrong).I need to find a way to avoid this.
This is how I draw the lines :
var dx = FromAtom.Atom.X - ToAtom.Atom.X;
var dy = FromAtom.Atom.Y - ToAtom.Atom.Y;
var slope = dy / dx;
if (slope > 0)
{
context.DrawLine(new Pen(Brushes.Black, Thickness), new Point(FromAtom.Position.X + 3, FromAtom.Position.Y + 3), new Point(ToAtom.Position.X + 3, ToAtom.Position.Y + 3));
context.DrawLine(new Pen(Brushes.Black, Thickness), new Point(FromAtom.Position.X - 3, FromAtom.Position.Y - 3), new Point(ToAtom.Position.X - 3, ToAtom.Position.Y - 3));
}
else
{
context.DrawLine(new Pen(Brushes.Black, Thickness), new Point(FromAtom.Position.X + 3, FromAtom.Position.Y - 3), new Point(ToAtom.Position.X + 3, ToAtom.Position.Y - 3));
context.DrawLine(new Pen(Brushes.Black, Thickness), new Point(FromAtom.Position.X - 3, FromAtom.Position.Y + 3), new Point(ToAtom.Position.X - 3, ToAtom.Position.Y + 3));
}
Taken from Adam Nathan's WPF 4.5 Unleashed says : "
Later drawings are placed on top of earlier drawings,so they preserve proper Z ordering
which refers to GeometryDrawing but I think this holds for drawing lines too.
In your case the result fails because you are offsetting the lines by 3 pixels vertically or horizontally with no consideration of the actual orientation of the line. The offset vector needs to be rotated with the line in order to achieve this effect. The problem then is that the lines will not meet together and have gaps or overlaps. To solve that problem you will need some math.
Drawing a parallel lines (or polygon) is not a trivial problem. See this answer for how to do it, but it your case you might be able to use compound lines.
Parallel Line Example
Related
I would like to draw a rectangle made of Mesh.
I enter the A starting point and B ending point.
The width of the rectangle is known in advance and is equal to H.
How to correctly determine the coordinates of corner points? (Everything happens in 3D)
There are a lot of theoretical entries on the net (but mostly for 2D) and trying something like this:
var vAB = B - A;
var P1 = new Vector3(-vAB.z, vAB.y, vAB.x) / Mathf.Sqrt(Mathf.Pow(vAB.x, 2) + Mathf.Pow(vAB.y, 2) + Mathf.Pow(vAB.z, 2)) * 0.5 * H;
But I can't find the correct solution
The simple way should be to use the cross product. The cross product of two vectors is perpendicular to both input vectors. You will need to define the normal of your rectangle, In this I use vector3.up. A-B cannot be parallel to the normal vector, or you will get an invalid result.
var l = B - A;
var s = Vector3.Normalize(Vector3.Cross(l, Vector3.up));
var p1 = A + s * h/2;
var p2 = A - s * h/2;
var p3 = B - s * h/2;
var p4 = B + s * h/2;
Here's a quick explanation of the trig involved. There are other tools which will reduce the boilerplate a bit, but this should give you an understanding of the underlying maths.
I've tweaked your problem statement slightly: I'm just showing the XY plane (there's no Z involved), and I've rotated it so that the line AB forms a positive angle with the horizontal (as it makes explaining the maths a bit easier). A is at (x1, y1), B is at (x2, y2).
The first step is to find the angle θ that the line AB makes with the horizontal. Draw a right-angled triangle, where AB is the hypotenuse, and the other two sides are parallel to the X and Y axes:
You can see that the horizontal side has length (x2 - x1), and the vertical side has length (y2 - y1). The angle between the base and the hypotenuse (the line AB) is given by trig, where tan θ = (y2 - y1) / (x2 - x1), so θ = arctan((y2 - y1) / (x2 - x1)).
(Make sure you use the Math.Atan2 method to calculate this angle, as it makes sure the sign of θ is correct).
Next we'll look at the corner P1, which is connected to A. As before, draw a triangle with the two shorter sides being parallel at the X and Y axes:
This again forms a right-angled triangle with hypotenuse H/2. To find the base of the triangle, which is the X-distance between P1 and A, again use trig: H/2 * sin θ. Similarly, the Y-distance between P1 and A is H/2 cos θ. Therefore P1 = (x1 + H/2 sin θ, y2 - H/2 cos θ).
Repeat the same trick for the other 3 corners, and you'll find the same result, but with differing signs.
My approach requires you to use a Transform.
public Transform ht; // assign in inspector or create new
void calculatePoints(Vector3 A, Vector3 B, float H)
{
Vector3 direction = B - A;
ht.position = A;
ht.rotation = Quaternion.LookRotation(direction, Vector3.Up);
Vector3 P1 = new Vector3(A + ht.right * H/2f);
Vector3 P2 = new Vector3(A + ht.left * H/2f);
Vector3 P3 = new Vector3(B + ht.right * H/2f);
Vector3 P4 = new Vector3(B + ht.left * H/2f);
}
I think it's intuitive to think with "left" and "right" which is why I used the Transform. If you wanted to define a point along the way, you'd be using ht.forward * value added to A, where value would be something between 0 and direction.magnitude.
So, I am not very proficient in C#/.Net/PDFSharp functions and I cannot seem to find any suitable answer to solve my issue.
Basically, I have a simple program that has to draw contour of an object(it can be curved and etc.) from user input.
I have Radius and Degrees of an angle to draw 2 arc lines.
In 360 degrees one circle radius is smaller by X amount of thickness that user inputs so the "inside" of the two circles is the same thickness as the whole draft.
Finally, the thing I need the program to do is to draw two lines, on the 2 sides of the arc lines to "connect them" to make it a proper contour, I can manage the starting Line easily enough since it is not dynamic however the End line is dependent on Radius and degrees of an angle.
How do I properly find the End coordinates I suppose, of the Arcs, so that it successfully draws the End line at the end regardless of the Users inputted Radius/Thickness/Angle.
Here is some code for how I drawn the Arcs, the Start line and my failure try of finding the End line (which just draws the line too far from the whole draft).
Input is User Control where User can input the variables (needed multiple but ended up with one, so it sounds waste to use User Control + Form window).
mmradius, thickness, mmangle are all user inputs, Innerradius is the innercircle radius.
var innerradius = Input.mmradius- Input.thickness;
gfx.DrawArc(pen, start_x, start_y, mmradius*2, mmradius*2, 0, mmangle);
gfx.DrawLine(pen, start_x + mmradius+ innerradius, start_y + mmradius,
(start_x + mmradius) + mmradius, start_y + mmradius);
gfx.DrawArc(pen, (start_x + mmradius) - innerradius,
(start_y + mmradius) - innerradius, innerradius*2, innerradius*2, 0, mmangle);
var CenterX = start_x + mmradius;
var CenterY = start_y + mmradius;
double degrees = mmangle * (Math.PI / 180);
var end_x = mmradius + CenterX * Math.Cos(degrees);
var end_y = mmradius + CenterY * Math.Sin(degrees);
gfx.DrawLine(pen, end_x, end_y, end_x - innerradius, end_y - 2);
Hope it's understandable of what I want if not I'll try my best to clarify!
Maybe it works better like this:
var end_x = CenterX + mmradius * Math.Cos(degrees);
var end_y = CenterY + mmradius * Math.Sin(degrees);
Cannot try to run it, so maybe some more changes are needed.
Given a list of x,y coordinates and a known width & height how can the NUMBER of the enclosed areas be determined (in C#)?
For Example:
In this image 5 ENCLOSED AREAS are defined:
Face (1)
Eyes (2)
Nose (1)
Right of face (1)
The list of x,y points would be any pixel in black, including the mouth.
You can use this simple algorithm, based on idea of flood fill with helper bitmap:
// backColor is an INT representation of color at fillPoint in the beginning.
// result in pixels of enclosed shape.
private int GetFillSize(Bitmap b, Point fillPoint)
{
int count = 0;
Point p;
Stack pixels = new Stack();
var backColor = b.GetPixel(fillPoint.X, fillPoint.Y);
pixels.Push(fillPoint);
while (pixels.Count != 0)
{
count++;
p = (Point)pixels.Pop();
b.SetPixel(p.X, p.Y, backColor);
if (b.GetPixel(p.X - 1, p.Y).ToArgb() == backColor)
pixels.Push(new Point(p.X - 1, p.Y));
if (b.GetPixel(p.X, p.Y - 1).ToArgb() == backColor)
pixels.Push(new Point(p.X, p.Y - 1));
if (b.GetPixel(p.X + 1, p.Y).ToArgb() == backColor)
pixels.Push(new Point(p.X + 1, p.Y));
if (b.GetPixel(p.X, p.Y + 1).ToArgb() == backColor)
pixels.Push(new Point(p.X, p.Y + 1));
}
return count;
}
UPDATE
The code above works only this quadruply-linked enclosed areas. The following code works with octuply-linked enclosed areas.
// offset points initialization.
Point[] Offsets = new Point[]
{
new Point(-1, -1),
new Point(-0, -1),
new Point(+1, -1),
new Point(+1, -0),
new Point(+1, +1),
new Point(+0, +1),
new Point(-1, +1),
new Point(-1, +0),
};
...
private int Fill(Bitmap b, Point fillPoint)
{
int count = 0;
Point p;
Stack<Point> pixels = new Stack<Point>();
var backColor = b.GetPixel(fillPoint.X, fillPoint.Y).ToArgb();
pixels.Push(fillPoint);
while (pixels.Count != 0)
{
count++;
p = (Point)pixels.Pop();
b.SetPixel(p.X, p.Y, Color.FromArgb(backColor));
foreach (var offset in Offsets)
if (b.GetPixel(p.X + offset.X, p.Y + offset.Y).ToArgb() == backColor)
pixels.Push(new Point(p.X + offset.X, p.Y + offset.Y));
}
return count;
}
The picture below clearly demonstates what I mean. Also one could add more far points to offset array in order to able to fill areas with gaps.
I've had great success using OpenCV. There is a library for .net called Emgu CV
Here is a question covering alternatives to Emgu CV: .Net (dotNet) wrappers for OpenCV?
That library contains functions for identifying contours and finding properties about them. You can search for cvContourArea to find more information.
If you are looking for a quick solution to this specific problem and want to write your own code rather than reusing others, I do not have an algorithm I could give that does that. Sorry.
There are a couple special cases in the sample image. You would have to decide how to deal with them.
Generally you will start by converting the raster image into a series of polygons. Then it is a fairly trivial matter to calculate area (See Servy's comment)
The special cases would be the side of the face and the mouth. Both are open shapes, not closed. You need to figure out how to close them.
I think this comes down to counting the number of (non-black) pixels in each region. If you choose one pixel that is not black, add it to a HashSet<>, see if the pixels over, under, to the left of and to the right of your chosen pixel, are also non-black.
Everytime you find new non-black pixels (by going up/down/left/right), add them to your set. When you have found them all, count them.
Your region's area is count / (pixelWidthOfTotalDrawing * pixelHeightOfTotalDrawing) multiplied by the area of the full rectangle (depending on the units you want).
Comment: I don't think this looks like a polygon. That's why I was having the "fill with paint" function of simple drawing software on my mind.
I have an application for drawing and editing vector graphics in WinForms
I have images, rectangles, ellipses, regions etc. and I know how to resize them by mouse move. But I don't know how to rotate them by mouse move.
I draw objects into Graphics.
I've tried this, but it didn't work.
g.TranslateTransform((float)(this.Rectangle.X + this.Rectangle.Width / 2), (float)(this.Rectangle.Y + this.Rectangle.Height / 2));
g.RotateTransform(this.Rotation);
g.TranslateTransform(-(float)(this.Rectangle.X + this.Rectangle.Width / 2), -(float)(this.Rectangle.Y + this.Rectangle.Height / 2));
//g.TranslateTransform(-(float)(rect.X + rect.Width / 2), -(float)(rect.Y + rect.Height / 2));
g.DrawImage(img, rect);
g.ResetTransform();
This didn't work, because I don't know how to find corners of objects in new (rotated) position, so I'm not able to resize that...
You need to apply high school trigonometry. There are lots of articles if you google "graphics.drawimage rotation"
But to start with, you should NOT be transforming the Graphics object itself. You are just looking to get the new bounding box of your image. To do this:
Take the bounding box of the image centered on the origin. Remember this is defined as three points for the benefit of DrawImage(Image, Point[])
Point[] boundingBox = { new Point(-width /2, -height/2),
new Point(width/2, -height/2),
new Point(-width/2, height/2) };
Use trig to rotate it. Feed each point through the following function:
Point rotatePointAroundOrigin(Point point, float angleInDegrees) {
float angle = angleInDegrees * Math.PI/180; // get angle in radians
return new Point( point.X * Math.Cos(angle) - point.Y * Math.Sin(angle),
point.X * Math.Sin(angle) + point.Y * Math.Cos(angle));
}
Translate the boundind box to where it has to go. Add the width/2 and height/2 to each of its points, plus whatever extra amount you want.
Call DrawImage(image, boundingBox)
I am an architecture student trying to solve a spatial problem with C# in Grasshopper for Rhino.
The space I am trying to create is an exhibition space in an airport. The space will be made up of elements of similar length. The idea is to connect them with a hinge and thereby allow them to create spaces of different layout and size according to how many elements are used.
As you can see from the illustration I would like the space to end with an opening an element length away from the starting point.
My first attempt has been to create equilateral triangles depending on the number of segments (walls) needed.
In short, from the starting point, triangles are created, and then the sides of the triangle that form the outer border are added to a list of points. This point list is returned to the Grasshopper application, which draws lines between the points. A little point is that I made the creation of the next triangle randomly either from the side AC or BC from the last triangle.
Here is an example of the spaces created (for 12 - 8 - 14 - 20 elements):
Here is the source code that creates these point lists:
private void RunScript(double radius, int walls, ref object A)
{
//
List<Point3d> pointList = new List<Point3d>();
List<Point3d> lastList = new List<Point3d>();
bool alternate = true;
bool swapped = false;
Random turn = new Random();
// set up the first part of the triangle
Point3d point1 = new Point3d(0, 0, 0);
Point3d point2 = new Point3d(0, radius, 0);
pointList.Add(point1);
pointList.Add(point2);
Point3d calcPoint;
for(int i = 0; i < walls - 1; i++) // walls - 1, is because I need one less triangle to get to the amount of walls
{
// use the method to create two similar circles and return the intersection point
// in order to create an equilateral triangle
calcPoint = FindCircleIntersections(point1.X, point1.Y, point2.X, point2.Y, radius, alternate);
// random generator: will decide if the new triangle should be created from side BC or AC
bool rotate = turn.Next(2) != 0;
Print("\n" + rotate);
// set the 2nd and 3rd point as 1st and 2nd - depending on random generator.
if(rotate)
{
point1 = point2;
if(swapped == true)
swapped = false;
else
swapped = true;
}
// if the direction is swapped, the next point created will not be a part of the outer border
if(swapped)
lastList.Add(calcPoint);
else
pointList.Add(calcPoint);
point2 = calcPoint;
// swap direction of intersection
if(rotate)
{
if(alternate)
alternate = false;
else
alternate = true;
}
}
lastList.Reverse();
foreach (Point3d value in lastList)
{
pointList.Add(value);
}
A = pointList;
}
// Find the points where the two circles intersect.
private Point3d FindCircleIntersections(
double cx0, double cy0, double cx1, double cy1, double rad, bool alternate)
{
// Find the distance between the centers.
double dx = cx0 - cx1;
double dy = cy0 - cy1;
double dist = Math.Sqrt(dx * dx + dy * dy);
// Find a and h.
double a = (rad * rad - rad * rad + dist * dist) / (2 * dist);
double h = Math.Sqrt(rad * rad - a * a);
// Find P2.
double cx2 = cx0 + a * (cx1 - cx0) / dist;
double cy2 = cy0 + a * (cy1 - cy0) / dist;
// Get the points P3.
if(alternate)
return new Point3d((double) (cx2 + h * (cy1 - cy0) / dist), (double) (cy2 - h * (cx1 - cx0) / dist), 0);
else
return new Point3d((double) (cx2 - h * (cy1 - cy0) / dist), (double) (cy2 + h * (cx1 - cx0) / dist), 0);
}
What I would like to do, is to vary the creation of these shapes, so that they are not only corridors, but resemble my initial sketches. I would like an algorithm to take an input of segments (number and length) and then propose different space layouts which are possible to create with this number of segments. I guess because of tesselation the space would have to be created with triangles, squares or hexagons? Do you think I should look into this "maximum area" algorithm : Covering an arbitrary area with circles of equal radius here on stackoverflow?
I would greatly appreciate any help on this algorithm. Cheers, Eirik
If you're merely interested in a program to generate instances to be externally evaluated (and not all such instances), you could "inflate" your curve. For example, in the 14-segment instance in your second image, there is a place where the curve goes inward and doubles back -- so your list of points has one point repeated. For curves like this you could cut out everything between the two (identical) points (A and B), as well as one of the surrounding points (A or B), and you have reclaimed some points to expand your curve - possibly resulting in a non-corridor structure. You may have to work some magic to ensure it is a "closed" curve, though, buy alternately adding segments to the front and the back of the curve.
Another opportunity: if you can identify the curve's interior (and there are algorithms for this), then anywhere that two segments form a concave angle with respect to your curve, you could blow it out to make a non-corridorish area. E.g. the second and third segments of your 14-segment curve above could be blown out to the left.
Successively applying these two methods to your corridor-like curve should generate many of the shapes you're looking for. Good luck!