Calculate SVG Arc between two points - javascript

I've got points X and A, A',... with coordinates:
X [x,y] (is the starting point)
A [a,b], A'[a',b'], etc. (are the ending points)
I also know angle XCA, XCA', etc.
I cannot find formula to calculate arcs, similar to ones on the picture, that connect point X with other A-points.
I tried to calculate radius X and radius Y using abs(x - a) and abs(y - b). It looked good only when using angles like 90, 180, 270.
Then I tried to calculates radius of inscribed circle that cuts X and A. But arc still does not look "natural" while flowing from X to A.
Thanks a lot in advance.
PS: Image above shows somekind of desired status (I've drawn it using free hand). Gray lines are used just for positioning.
EDIT ==========================
I've found out that the inscribed circle option works quite well but I have no idea how to determine when to use large-arc-flag as 1 and when as 0 (same problem with sweep-flag)

It looks to me like quadratic Bezier curves would be the easiest solution. Here's an example:
<?xml version="1.0" ?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 400 400" width="400" height="400" version="1.0">
<g transform="translate(200 200)">
<circle cx="0" cy="0" r="150" stroke="#000" fill="none" />
<path d="M0,-150 Q0,0 106.066,-106.066" fill="none" stroke="red" stroke-width="3" />
<path d="M0,-150 Q0,0 106.066,106.066" fill="none" stroke="red" stroke-width="3" />
<path d="M0,-150 Q0,0 -106.066,106.066" fill="none" stroke="red" stroke-width="3" />
<path d="M0,-150 Q0,0 -106.066,-106.066" fill="none" stroke="red" stroke-width="3" />
</g>
</svg>
The curves are defined by the path attribute Mx0,y0 Qx1,y1 x2,y2 where x0,y0 is the start point (X), x1,y1 is the control point (i.e., the centre of the circle, C), and x2,y2 is the end point A.
If you want more control over the curvature, use cubic Bezier curves instead. These have two control points, which should be placed on the straight lines connecting between X and C, and between C and A. If you put them closer to C, you'll get a tighter curve.
[JSFiddle link]

Here is the formula for calculation all the arc's point between two points.
double x = centerX + length * Math.Cos(angle * Math.PI / 180);
double y = centerY + length * Math.Sin(angle * Math.PI / 180);
Where length will be the length from circle to your point. You can increase angle to +1 and get other point and stop till your end angle.

Related

Making svg path with rounded edge responsive

I am working on a svg graphic that will represent health bar in a game, thus far it is looking like this https://jsfiddle.net/8ds9hpuv
Concept is to have responsive bar that decreases / increases in width based on character health.
Right now I can't figure out how to decrease this path in width, but maintain that rounded edge on the right side all the time.
Ideally I would like to make it's height responsive as well
<svg width="428" height="35">
<path d="M0 0h414.333785C423.444595 9.346449 428 15.179782 428 17.5c0 2.320218-4.555405 8.153551-13.666215 17.5H0V0z" fill="red"/>
</svg>
I've modified the path by changing every command to lowercase (using this tool: Convert SVG path to all-relative or all-absolute ) but I've left the last H command to uppercase since H0 is going back to x="0"
Next I'm replacing the first h command with the variable healthIndicator
I'm assuming that the tip of the arrow has only an aesthetical function.
For the sake of the demp I'm using an input type range to change the value of the healthIndicator. I hope this is what you need.
itr.addEventListener("input",()=>{
let healthIndicator = itr.value;
let d = `M0,0 h${healthIndicator}c9.111,9.346,13.666,15.18,13.666,17.5c0,2.320218,-4.555405,8.153551,-13.666215,17.5H0v-35z`;
thePath.setAttributeNS(null,"d", d);
})
svg{border:1px solid}
<svg viewBox="0 0 550 35" >
<path id="thePath" d="M0,0
h414
c9.111,9.346,13.666,15.18,13.666,17.5
c0,2.320218,-4.555405,8.153551,-13.666215,17.5
H0z" fill="red"/>
</svg>
<input type="range" id="itr" value="414" min="0" max="500" />

How properly shift arrow head on cubic bezier in SVG to its center

There is a cubic bezier with arrow marker:
<defs>
<marker id="arrow" orient="auto" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" refX="10" refY="6">
<path d="M0,0 L0,12 L12,6 z"></path>
</marker>
</defs>
<path marker-end="url(#arrow)" d="M 220 104 C 220 144 400 184 400 224" stroke-width="2"></path>
I want to shift arrow head to the center of curve, but when I mutating refX attr of marker arrow shifts not across the curve but straight to top (http://prntscr.com/ikzuol). It works perfectly with quadratic bezier curves but not with cubic.
Is there a way to display an arrow head at the center of cubic bezier curve with correct orientation using marker?
I know there is an option to get coordinates at length, calculate angle to rotate and do positioning and rotation by myself but I would like to avoid such calculations.
UPD: I've created a codepen to demonstrate the issue: https://codepen.io/onatolich/pen/LQqYvr
UPD2: one more codepen to demonstrate that it's working with quadratic bezier curves: https://codepen.io/onatolich/pen/NyoxQv
refX and refY are points in the coordinate system the marker is defined in and have no relation to the direction of the path. They simply define the point that is considered the origin of the marker and wich will be placed on the end of the path.
There is no way to define a marker along a computed path. Markers can only be placed at the vertices of a path. So the seemingly straight-forward way would be to find a mid-point, place a vertex there with appropriate cubic bezier control points...
<svg width="500" height="500">
<defs>
<marker id="arrow" orient="auto" markerUnits="userSpaceOnUse"
markerWidth="12" markerHeight="12" refX="3" refY="6">
<path d="M0,0 L0,12 L12,6 z"></path>
</marker>
</defs>
<path marker-mid="url(#arrow)"
d="M 220,104 C 220,124 265,143 310,162.5 355,182 400,202 400,224"
stroke-width="2" stroke="black" fill="transparent"></path>
</svg>
...but that is obviously a lot of computation, also.
Here is a hack that uses the <animateMotion> element, not to animate anything, but because it can move an object to every point along a path. The movement just starts, ends, and freezes in the middle of the path.
The "marker" does not have its own viewport, which means there is no way to define a refX/refY. The point to be placed on the path is always at (0,0) of its userspace coordinates. That is the reason the marker has to be moved in the opposite direction of these values.
<svg width="500" height="500" >
<path id="path1" d="M 220 104 C 220 144 400 180 400 224"
fill="none" stroke-width="2" stroke="black" />
<path d="M0,0 L0,12 L12,6 z" transform="translate(-3,-6)">
<animateMotion dur="0s" rotate="auto" fill="freeze"
keyTimes="0;1" keyPoints="0.5;0.5" calcMode="linear" >
<mpath xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#path1"/>
</animateMotion>
</path>
</svg>
The hack will not work with Edge/IE, which do not implement SMIL animations.

Strange behaviour of dasharray property in a SVG circle

I'm trying to create an infinite animation loop of an SVG circle.
I want to create 12 equal pieces and separate it by some gap.
To calculate value of circle pieces I used k coefficient from an table below
So I did 0,25782 * 160 (diameter of my circle) and I got: 41.2512 (it's should be a value of my pieces).
After that I created strokeDasharray prop via that value: 40 1.2512 I thought that it should be correct value.
Looks like it is but when I changed the strokedashOffset prop I saw some artifact at the right side. I don't know why it's happened and how I can fix it (and where I did an mistake)?
Thanks for any help.
Demo here (just change the strokedashOffset to a 408 value and you will see that issue).
https://jsfiddle.net/q8enje9o/
Here my pure svg code
<svg version="1.1" id="svgout_dasharray" baseProfile="full" width="500"
height="500" viewBox="0, 0, 500, 500" xmlns="http://www.w3.org/2000/svg" style="border: 1px solid black">
<defs></defs>
<circle cx="150" cy="150" r="80" fill="none" stroke="#ff0000" style="stroke-width: 30;stroke-dasharray: 40, 1.2512;stroke-dashoffset: 380;"></circle>
</svg>
Table of k coef. n - count of circle pieces
Here is the formula how you can calculate those coef. by itself
n - count of pieces
360 - 2*PI (a whole circle)
k - our coef. that we want to find
P.S. Here is a demo with the issue after update (Chrome latest & Windows 10)
The circumference of the circle / sum of the stroke-dasharray values needs to be an integer if you want evenly spaced lines and it isn't in your case.
So you probably want something like stroke-dasharray: 40, 1.8879; which should work with any stroke-dashoffset value.
Your question is a little confusing because you talk a lot about how you are calculating the dash array, but complain about things looking funny when you change the dash offset.
The circumference of a circle is 2 * PI * radius. If you want a n sections in your circumference, then the "dash + gap" in your dash array needs to sum to:
(2 * PI * radius) / n
so for 12 sectors, and a radius of 80, that value would be
(2 * PI * 80) / 12 = 41.8879
As Robert said, `stroke-dashoffset="40 1.8879" should work. And indeed it does.
<svg width="500" height="500" style="border: 1px solid black">
<circle cx="150" cy="150" r="80" fill="none" stroke="#ff0000"
style="stroke-width: 30;stroke-dasharray: 40 1.8879;"/>
</svg>
Now you also talk about dash offset. I don't know why you want to change the dash offset. I guess you are trying to make the dashes rotate around the circumference of something. Is that right?
If so, then that should work - as long as you are accurate with your dash array values. See below.
<svg width="500" height="500" style="border: 1px solid black">
<circle cx="150" cy="150" r="80" fill="none" stroke="#ff0000"
style="stroke-width: 30;stroke-dasharray: 40 1.8879; stroke-dashoffset: 408;"/>
</svg>

Double arc shape with CSS jquery or javascript

How can I create an arc shape like this:
With CSS or jquery or javascript
You don't even need CSS/JS to draw this. Just use an <svg> element.
<svg width="270" height="120">
<path
d="M 49.155517,102.32765 C 127.54837,40.541934 209.51266,103.2205 209.51266,103.2205 l 0,0 C 259.33409,50.363364 259.15552,50.363364 259.15552,50.363364 126.68749,-56.114356 2.1861831,50.204194 2.1861831,50.204194 z"
stroke-width="3"
stroke="#A5423A"
fill="none"
/>
</svg>
You could use SVG for this. There is an arc path command which you could use.
As your comment states, you want to place content inside the arc and you want them to rotate.
Content like text or image could be placed inside the svg.
Rotation can be achieved with transform=rotate(..).
If you want to do more animations with SVG you could have a look at D3.js. If you just want to create some arcs, you possibly can do the math on your own for computing the SVG path string.
#AlliterativeAlice is correct.
But for this shape I would use two arcs instead of a lot of C paths.
I also prefer to use relative paths instead of absolute one.
So my solution used arcs and lines instead of only Bezier Curves.
<svg width="300px" height="300px" viewBox="0 0 100 100">
<path d="m 10,60
a 50 50 0 0 1 80,0
l -10,10
a 40 40 0 0 0 -60 0Z" stroke-width="1" stroke="#A5423A" fill="none" />
</svg>

SVG and SMIL animate Ball bouncing 3 times and collision with wall

I need to animate a circle that bounce 3 times and hits a wall and return. it should follow a given path. i have tried it with animateMotion. This is how it is so far,
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>animation</title>
<!--<rect x="15" y="5" rx="5" ry="5" width="20" height="10" style="fill:#CCCCFF;stroke:#000099">-->
<circle cx="0" cy="50" r="15" fill="blue" stroke="black" stroke-width="1">
<animateMotion dur="6s" repeatCount="indefinite" rotate="auto">
<mpath xlink:href="#path1"/>
</animateMotion>
</circle>
<!--</rect>-->
<path id="path1" d="m21,39c0,0 46,-44 79,-1c33,43 62,58 97,26c35,-32 86,-30 86,
-31c0,-1 61,-9 29,43c-32,52 -19,51 -87,51c-68,0 -158,-5 -158,-6c0,-1 -40,-11 -41,-12 Z"
stroke-width="5" stroke="#000000" fill="none"/>
</svg>
actually something like below is what i am expecting, as i am new to the area, appreciate any guidance or support.
In order to create the impression that the ball is bouncing organically, you probably want a trajectory that looks a bit more like this:
In addition, you do not want a linear timing function. A linear timing function means the element will move at the same speed throughout the animation. But, the closer a ball is to the apex of a bounce, the slower the ball is going. Also consider that it is going faster at the start and end of the tallest bounce than it is at the start and end of the shortest bounce.
Based on the aforementioned info about the behaviour of a bouncing ball, we might guess that the timing function for one bounce should look something like this:
It starts out fast, slows down, and then speeds up again.
This diagram represents a cubic-bezier curve, also known as a spline. This particular cubic-bezier can be written as 0.1 0.8 1 0.3 - the x and y coords of the first control point (P1) followed by the x and y coords of the second control point (P2).
Combining the trajectory and the timing function:
jsfiddle. The timing needs to be refined a bit, but that's the general idea.
The <animateMotion> element requires four additional attributes to make this happen:
keyPoints="0;0.5;0.75;1"
keyTimes="0;0.35;0.6;1"
calcMode="spline"
keySplines="0.3 0.5 0.8 0.7;0.3 0.5 0.8 0.7;0.3 0.5 0.8 0.7"
what these mean:
keyPoints="0;0.5;0.75;1" - I've decided to split up the path into three sections, each leg of which is associated with a start and end time (from keyTimes) and a timing function (from keySplines). The values are between 0 and 1, separated by semi-colons, where 0 is start of path and 1 is end of path.
keyTimes="0;0.35;0.6;1" - the total duration of the animation is segmented according to these times. There should be exactly as many keyTimes times as there are keyPoints. The values are between 0 and 1, separated by semi-colons, where 0 is start and 1 is the total duration of the animation.
calcMode="spline" - indicates that we want the timing function to be cubic-bezier. setting calcMode to spline means we also have to set keySplineson this element.
keySplines="0.3 0.5 0.8 0.7;0.3 0.5 0.8 0.7;0.3 0.5 0.8 0.7" - each leg of the trajectory gets its own timing function. (So, there should be one fewer keySplines value than there are keyTimes. Here, I've used the same timing function for each, 0.3 0.5 0.8 0.7. Since there are four keyTimes, there are three keySplines values because the trajectory has three legs. (Again, the actual values that I used need to be refined but that's the general idea.)
If you want your circle to follow the path, you need the circle's position to be (0,0), since the motion animation will be relative to the circle's current position:
<circle cx="0" cy="0"...
You can also remove rotate="auto" since it isn't of any use in this case.
See jsfiddle

Categories