Svg simple transformations - delphi

I have a simple svg with 3 paths: red, blue and yellow.
I want to: scale all shapes (eg. 0.3) and rotate only the red + blue(eg.90 deg).
The rotation point should be the middle of red path.
After these operations I want the yellow shape to have the distance to the red path the original distance scaled by 0.3.
My attempt was:
compute the middle of the red path;
translate in the origin (0,0),by translated with (-redCenterPoint.x, - redCenterPoint.y)
Scale red path by 0.3
move back red path by translate(redCenterPoint.x, redCenterPoint.y)
repeat the same for blue and yellow by computing blueCenter, yellowCenter
My question is: How can I keep the original image structure but scaled by 0.3 and rotate by 90? - blue path to be in touch with red path and yellow to have original distance scaled by 0.3.
I saw that if I consider the redCenterPoint for all 3 shapes then the group looks the same as original but scaled, looks correct.
I want to know to do the same but with the first method.
svg file:
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
style="opacity:1;fill:#ff0000"
d="m 146.98669,417.50473 561.36408,0 0,206.40686 -561.36408,0 z"
id="red"
inkscape:label="#table" />
<path
style="opacity:1;fill:#0000ff"
d="m 641.11218,339.32031 65.67491,0 0,82.87548 -65.67491,0 z"
id="blue" />
<path
style="opacity:1;fill:#ffff00"
d="m 764.69525,515.63883 55.28473,-55.28473 46.43917,46.43918 -55.28473,55.28472 z"
id="yellow"
inkscape:connector-curvature="0"
inkscape:label="#yellow" />
code in delphi using Riversoft component for renderin SVG:
redBounds: TSVGRect;
redCenterPoint: TPointF;
redMatrix: TSVGMatrix
redBounds := (svgDoc.SVG.AllItems['red'] as TSVGGraphicElement).BoundsRect;
redCenterPoint.x := bDiamond.Left + (bDiamond.Width) / 2;
redCenterPoint.y := bDiamond.Top + (bDiamond.Height) / 2;
redMatrix := CreateTranslateRSMatrix(-redCenterPoint.x, -redCenterPoint.y);
redMatrix := RSMatrixMultiply(redMatrix,
CreateRotationRSMatrix(TPoint(0,0), DegToRad(90)));
redMatrix := RSMatrixMultiply(redMatrix,
CreateScaleRSMatrix(0.3, 0.3));
redMatrix := RSMatrixMultiply(redMatrix,
CreateTranslateRSMatrix(redCenterPoint.x, redCenterPoint.y));
(svgDoc.SVG.AllItems['red'] as TSVGGraphicElement)
.Matrix := mainMatrix;

What you are doing is keeping the centre constant for each individual shape. That is not what you want to do. You want to keep the centre of the whole shape constant. That is what you are doing in your second attempt where you apply the red centre to all 3 shapes, which is why it looks better. You might want to choose a different centre, and there are various algorithms for doing that, or even leave the origin where it is (not bothering to move the centre at all). But you must apply the same centre, whatever you choose, to all shapes to achieve what you want, and that applies to rotation too (although in your case you only apply rotation to two of the shapes). The centre for rotation, though, does not have to be the same as the centre for scaling.
For the most effective operation for calculating your new centre in each case I thing I would use centre of gravity calculations, but just simple average of centres would work well too.

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));

What do the coordinates mean in love.graphics.polygon

I don't know which numbers do what in the coordinates example here. I imagine they mean things like place the top left corner at this position and the bottom right corner at this position, but I don't know which number corresponds to which position.
I've been trying to fool around with the numbers to get a small green rectangle but keep getting weird results like the following, and don't know which numbers need to be what is order to make the rectangle symmetrical and at the bottom
This is what the rectangle should look like
The height of the rectangle is 50, the height of the screen is 1000, and the width of the screen is 1700.
Here's my draw function
function love.draw()
love.graphics.setColor(0.28, 0.63, 0.05) -- set the drawing color to green for the ground
love.graphics.polygon("fill", objects.ground.body:getWorldPoints(objects.ground.shape:getPoints())) -- draw a "filled in" polygon using the ground's coordinates
-- These are the grounds coordinates. -11650 950 13350 950 13350 1000 -11650 1000
love.graphics.setColor(0.76, 0.18, 0.05) --set the drawing color to red for the ball
love.graphics.circle("fill", objects.ball.body:getX(), objects.ball.body:getY(), objects.ball.shape:getRadius())
love.graphics.setColor(0.20, 0.20, 0.20) -- set the drawing color to grey for the blocks
love.graphics.polygon("fill", objects.block1.body:getWorldPoints(objects.block1.shape:getPoints()))
love.graphics.polygon("fill", objects.block2.body:getWorldPoints(objects.block2.shape:getPoints()))
print(objects.block1.body:getWorldPoints(objects.block1.shape:getPoints()))
end
As described at https://love2d.org/wiki/love.graphics, Löve's coordinate system has (0, 0) at the upper left corner of the screen. X values increase to the right, Y values increase down.
The polygon function expects the drawing mode as it's first parameter, and the the remaining (variable) parameters are the coordinates of the vertices of the polygon you wish to draw. Since you want to draw a rectangle you need four vertices/eight numbers. You do not have to list the upper left corner of the rectangle first, but that's probably the easiest thing to do.
So in your case, you want something like:
love.graphics.polygon('fill', 0, 950, 0, 1000, 1700, 1000, 1700, 950)
I've not worked with the physics system, so I'm not quite sure how it's coordinate system relates to "screen" coordinates. The values you show in the comment in your code listing seem like they should give a rectangle (although x = -11650 wouldn't be on screen). You might try experimenting without the physics system first.
Also, since the physics system in Löve is just a binding to Box2D, you might want to read its documentation (http://box2d.org/about/). Not really sure what you're trying to do with feeding shape:getPoints into body:getWorldPoints.

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.

Indesign Bug? Choosing Fancy Corners Reverses Function of Align Stroke Inside/Outside

I think that this might be a bug in InDesign but I thought that it might be handy to know.
Here's how to recreate: Create a rectangle 200px x 200px, with a 4pt stroke. Set Align stroke to INSIDE. Set the top left xy pos of the rectangle to 100px,100px so that you can easily see changes, and leave the Reference Point in the top left. Now when you change stroke widths, everything is fine, and change corner types, everything is fine, EXCEPT the Fancy Corners. So if you choose Fancy Corners, the x/y becomes 96,96 and total width/height changes to 208 x 208 which is incorrect. Now change the Align Stroke to "OUTSIDE", and the x/y changes back to 100,100 and the rectangle size changes back to 200 x 200. Completely backwards, but just for the Fancy Corners.
Please comment if this is an expected response.

Cropping the liquid region of a bottle for processing

Basically what I want to do is to filter out only the liquid region of the bottle for further processing. So the next processes would apply only for that region.
I've tried various methods for months but didn't have any luck. I can filter out the region between the top liquid boundary and the top of the bottom dark region. But that doesn't serve my purpose as I need the areas at the sides of the dark region at the bottom of the bottles too. Im trying to do this in openCV/EmguCV.
help please...
You may upload the images you have already obtained (code as well), along with the regions that your result failed to include. Currently I am not quite sure which part of liquid would you like to get. I tried some easy processing, and get a liquid region. Please let me know if there is some problem.
(1) Remove the region with (i) intensity of 255 at either R,G or B space, or (ii) all the 3 space with intensity of less than 100, shown in upper left as I0;
(2) HSV space. Remove the region with Hue value equal to 0.5 or 1, shown in upper middle as I1;
(3) Remove the region with Saturation value no less than 0.5, shown in upper right as I2;
(4) I2&I1, remove the region with small areas, fill in the holes, shown in lower left as I3;
(5) I0(:,:,1)&I3 where I0(:,:,1) is its channel 1. Fill in the holes, smooth the edges, shown in lower middle;
(6) Use (5)'s result as a mask on the original image, shown in the lower right.
I think you can also get the liquid region with the dark side at the bottom shown as a hole, you can use cvFloodFill() to fill the holes, and get a intact liquid region.

Resources