How to get a path's outline? - path

I have some point like below:
[[1,4],[2,8]]
[[5,4],[3,8],[5,4]]
Then I make a line (lat's say the width is 2) , I want to get the line's outline(path), see the Example Image's black line.
In one world, I have wall corner's coordinate, I want to figure out the outline's path of the wall(In the Example image, the red part is the coordinate, and the black lines are what I want to get )
Example Image

I've been working on a similar project. I decided to use vectors to get the parallel lines along the path by getting the cross product of the UP vector and the vector of points 1 to 2 which will get you a vector that is pointing away from the line that you can use to get the begin and end point of the parallel line
CVec3 a, v1, v2, zero, up(0, 1.0f, 0);
zero.set(0,0,0);
v1.sub(points[1], points[0]);
v2.sub(points[2], points[1]);
a.crossProduct(up, v1);
a.normalise();
result[0] = result[1] = a;
result[0].mult(wallwidth).add(points[0]);
result[1].mult(-wallwidth).add(points[0]);
result[2].set(result[0]).add(v1);
result[3].set(result[1]).add(v1);
result[0] to result[2] is line in one side while result[1] to result[3] will be the other one. I haven't figure out how to connect the lines so they make corner without deforming the line width

Related

FMX: Strange glitch with TCanvas.DrawPath. Why?

I draw a path consisting of 2 lines going up and then back down to the same spot, or almost the same spot, but the first line is drawn too high. If I then draw the same lines using DrawLine I don't see the issue. Why is this happening?
Below is an example. Just drop a 400x400 TImage on a blank multiplatform form. The code draws 2 red paths, one with close to a 180 degree angle between the lines and one with less of an angle. The same lines are then drawn using DrawLine in blue. If the DrawPath function works correctly then the blue lines should completely cover the red lines, but they don't. In this example with a scale of 1.5 the path extends 7 pixels too high for the first path. The extent of the error reduces as the lines get further apart. The issue still happens with a scale of 1, but is less obvious.
procedure TForm1.FormActivate(Sender: TObject);
var
LPath1, LPath2 : TPathData;
i : Integer;
begin
// A path of 2 lines going up and then back down to almost the same spot
LPath1 := TPathData.Create;
LPath1.MoveTo(PointF(100,200));
LPath1.LineTo(PointF(100,50 ));
LPath1.LineTo(PointF(105,200));
// A path of 2 lines going up and then back down at a wider angle
LPath2 := TPathData.Create;
LPath2.MoveTo(PointF(200,200));
LPath2.LineTo(PointF(200,50 ));
LPath2.LineTo(PointF(260,200));
Image1.Bitmap.BitmapScale := 1.5; // The issue shows up more at larger scales
Image1.Bitmap.SetSize(Trunc(Image1.Width), Trunc(Image1.Height));
with Image1.Bitmap.Canvas do if BeginScene then begin
Clear(TAlphaColorRec.White);
// Draw the paths using DrawPath in red
Stroke.Color := TAlphaColorRec.Red;
Stroke.Thickness := 1;
DrawPath(LPath1, 1);
DrawPath(LPath2, 1);
// Draw the paths using DrawLine in blue over the top
// The red lines should be completely hidden under the blue
Stroke.Color := TAlphaColorRec.Blue;
for i := 1 to LPath1.Count - 1 do
DrawLine(LPath1.Points[i-1].Point, LPath1.Points[i].Point, 1);
for i := 1 to LPath2.Count - 1 do
DrawLine(LPath2.Points[i-1].Point, LPath2.Points[i].Point, 1);
EndScene;
end;
LPath1.Free;
LPath2.Free;
Image1.Bitmap.SaveToFile('test.png');
end;
Result of the code when run in Windows 10. I'm using Delphi 11, but the same issue happens with Delphi 10. I've tried switching GPU but the same issue occurs.
Enlarged view:
I've come to the conclusion that this isn't a glitch at all. It's because the default setting of TCanvas.Stroke.Join is TStrokeJoin.Miter. The artefact seen is just the sharp spike of the mitred corner. Using MoveTo before each line segment when constructing the path does solve the issue (because there's no join between the separate line segments) but so does setting the TCanvas.Stroke.Join parameter to TStrokeJoin.Round or TStrokeJoin.Bevel.
Note that at very sharp angles approaching 180 degrees, the miter join would become infinite. However, it appears to be limited somehow, perhaps in proportion to the stroke thickness. I don't think there's a way to change this miter limit in delphi.
This is because by default TPath is making smooth transitions between different path segments. I'm guessing it might be using Quadratic interpolation for making these smooth transitions.
Yes making smooth transition between two lines doesn't seem logical but it looks this is how it is implemented.
Now you can avoid this by telling the TPath that your two lines are not connected and thus should be treated as two separate lines even thou in reality they are connected. And you can do this by simply calling Path.MoveTo which is intended to shift position so you can create another unconnected line that dos not continue from your last path point.
Here is how modified code for your first sharp cornered line would look like:
NOTE that I'm specifying the exact same position for MoveTo command that was used for rendering of previous path line since you don't want the new line to start at new position.
// A path of 2 lines going up and then back down to almost the same spot
LPath1 := TPathData.Create;
LPath1.MoveTo(PointF(100,200));
LPath1.LineTo(PointF(100,50 ));
//Add move to command to prevent Path from trying to make smooth transitions between two lines
LPath1.MoveTo(PointF(100,50));
LPath1.LineTo(PointF(105,200));

set images in circle view with rotation in ios

I am trying to achieve a view in which Images will placed on edge of circle.I tried to make it with CAShapeLayer and added UIImageViews but I want to create it dynamically. Any help will be appreciated.I am adding one image for example.
I would suggest you to refer this answer given by #rob_mayoff
You should create a circular bezier path with the image you want to display.Please not that this method only works if you want to have same image distributed evenly on the circle.If you want to have different image distributed evenly on circle, then you should do put more effort .
You can try in two ways:
Draw every circular bezier path by calculating their centers . And arrange them in a circular manner. You should do a little math. Please note that you have the center point (x,y) for the main circle, and place the sub circles around the center point (x,y) in such a way that distance from each sub circle center to main circle center should be same. To get the exact coordinates of lines which divide the circle , please refer to the answer. Once you get the exact coordinate, you can place the subcircle at these positions.
I will try to give a rough idea about doing this:
Consider you have a main circle whose center is at (x0,y0). And you wish to place images on this circle by dividing the circle into 'n' parts. so that you can place 'n' number of imageViews on this main circle. The 'n' parts are denoted by green lines in the below picture.
The angle between each of the green lines is 360deg/n
No we need the end point of the each green line. Which can be obtained from:
sub.x = x0 + r * cos(angle);
sub.y = y0 + r * sin(angle);
where r is the radius of main circle.
This is for one sub circle. In yoour case you have 'n' number of sub circles, so let's do a loop to get all sub circle centerpoints:
for(i = 1 to n)
{
angle = i * (360/n);
sub.x = x0 + r * cos(angle);
sub.y = y0 + r * sin(angle);
}
Now you can draw a circular bezier path at each of the 'n' sub (x,y) points
using the addArcWithCenter:center where center would be the calculated sub (x,y)
I think 1 is again the best way if you want to do everything dynamically.

iOS CG dashed double line path

I want draw underground way symbol on map.
Actually the same path is drawed two times.
first with:CGContextSetLineDash(ctx, 0, lengths, cnt);
and second, no dashed, more tiny as first
result is:
Question is: how to draw double line as one path (not two with offset), dashed, transparent in middle?

Force points to be on a line

I have a line identify by x1,y1,x2,y2 which are double values. Then I have
several graphical objects (Let's name the class TShape) which cordinates
are Left, Top, Right, Bottom: double. Only Top and Left properties are writable
value. When dragging the TShape around the top and left values are updated.
I am using a function to discovery when TShape is near a Line. The function
definition is:
function NearLine(const Target: TPoint; X1, Y1, X2, Y2: double; Off: integer = 5): boolean;
NearLine returns true if point specified by Target is near the line specified by Point1
and Point2. The point must be at the distance specified by Off.
I use the function with Off = 0;
In my implementation Target is the center of the TShape which I keep updated
calculating it from Top and Left properties. Because Target
is TPoint I do:
1-
CPoint.X := Trunc(Center.X);
CPoint.Y := Trunc(Center.Y);
2-
and when the function NearLine above is true I force the mouse to release with:
3-
Mouse_Event(MOUSEEVENTF_ABSOLUTE or MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
1,2 an 3 are called within an event UpdateMove which is called while
dragging the shape.
This allow me to "stick" the TShape almost near the line however it's not
exactly what I want to achive... obviously there is an error due the Trunc function.
The error is:
deltaX := Frac(Center.X);
deltaY := Frac(Center.Y);
After releasing the mouse programmatically how I can force all the center
therefore all the shapes to be perfectly lined (collinear) with the
line?
Any help? :(
You're asking the question wrong, that's why you cant' see the answer your self. If 3 points aren't collinear, you're not going to "force" them collinear unless you change the laws of math and/or physics.
What you probably want is to find a point on the line defined by two points that's closest to your point of reference. That's pretty simple geometry: The closest point is as at the "foot" of a perpendicular drawn from your third point to the line defined by the first two! You can solve that using the Pythagoran theory alone, you don't even need fancy analytic geometry.

Problem rotating simple line image

It is stated, that to rotate a line by a certain angle, you multiply its end point coordinates by the matrix ({Cos(a), Sin(a)} {-Sin(a) Cos(a)}), where a is rotation angle. The resulting two numbers in matrix will be x and y coordinates of rotated line's end point. Rotation goes around line's start point.
Simplifying it, new coordinates will be {x*Cos(a) - y*Sin(a)} for x and {x*Sin(a) + y*Cos(a)} for y.
Task is to rotate a triangle, using this method. But the following code that uses this method, is giving out some crap instead of rotated image (twisted form of original triangle, rotated by "random" angle):
x0:=200;
y0:=200;
bx:=StrToInt(Edit1.Text);
by:=StrToInt(Edit2.Text);
cx:=StrToInt(Edit4.Text);
cy:=StrToInt(Edit5.Text);
a:=StrToInt(Edit3.Text);
//Original triangle
Form1.Canvas.Pen.Color:=clBlue;
Form1.Canvas.MoveTo(x0,y0);
Form1.Canvas.LineTo(bx,by);
Form1.Canvas.LineTo(cx,cy);
Form1.Canvas.LineTo(x0,y0);
//New triangle
Form1.Canvas.Pen.Color:=clGreen;
Form1.Canvas.MoveTo(x0,y0);
b1x:=Round(bx*cos(a*pi/180)-by*sin(a*pi/180));
b1y:=Round(bx*sin(a*pi/180)+by*cos(a*pi/180));
c1x:=Round(cx*cos(a*pi/180)-cy*sin(a*pi/180));
c1y:=Round(cx*sin(a*pi/180)+cy*cos(a*pi/180));
Form1.Canvas.LineTo(b1x,b1y);
Form1.Canvas.MoveTo(x0,y0);
Form1.Canvas.LineTo(c1x,c1y);
Form1.Canvas.LineTo(b1x,b1y);
end;
Well, I'm out of ideas. What am I doing wrong?
Thanks for your time.
The formula you are using rotates a point around (0, 0). To achieve the required result change your calculation to:
b1x:=x0 + Round((bx-x0)*cos(a*pi/180)-(by-y0)*sin(a*pi/180));
b1y:=y0 + Round((bx-x0)*sin(a*pi/180)+(by-y0)*cos(a*pi/180));
c1x:=x0 + Round((cx-x0)*cos(a*pi/180)-(cy-y0)*sin(a*pi/180));
c1y:=y0 + Round((cx-x0)*sin(a*pi/180)+(cy-y0)*cos(a*pi/180));
You appear to be rotating each individual line round its initial start point coordinates. So line 1 will get rotated about its start point (x0,y0); then line 2 will get rotated about bx,by; then line 3 will get rotated round cx. This will result in a twisted triangle. Instead you will need to rotate all three lines round the start point of the first line.

Resources