2
votes

I'm trying to utilize SVG's to dynamically draw lines onto a cylindrical looking surface. Because it's cylindrical, any straight line between two points will actually follow an elliptical slice of the cylinder, and therefore need to be rendered as a section of an elliptical arc.

arc section AB of cylinder

As the docs state, SVG arcs are defined as: "A rx ry x-axis-rotation large-arc-flag sweep-flag x y"

Please tell me where I'm going wrong in deriving this arc (AB).

  1. So obviously I know my starting and ending points (AB).
  2. I am assuming that ry in both the cylinder and slice are equal.
  3. rx is half the slice's hypotenuse.
  4. θ is the x-axis rotation... I think?

    • This is where I think I'm getting into trouble. As you can see in the second image when I try converting all my line paths into arcs, some of the ars turn out perfect when they are not rotated (they are parallel to the x-axis in the rectangular plane, meaning they follow the arc path of the primary cylindrical ellipse), however, something funny is happening with the rotation. When I turn on the large-arc-flag it's pretty evident that the ellipses being drawn are not at all lining up with my cylinder.

    • I'm fairly confident that the hypotenuse is being calculated correctly, as setting θ to zero gives me the cylinder's diameter, so I'm a bit flummoxed as to where I'm going wrong.

drawing star seems to be causing over/under rotation errors

TLDR: Given the data I provided in image 1, how would you go about drawing the arc AB.

EDIT 1: Here is an SVG of a cylinder and a line to play around with. Again, I'm trying to make the line into an arc to fit on the cylinder surface so it matches the ellipse arc formed by a slice through the cylinder.

<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <path id="cylinder" fill="none" stroke="#000000" stroke-width="2" d="M 0 32 a148 32 0 0 0 296 0 a148 32 0 0 0 -296 0 v185 a148 32 0 0 0 296 0 v-185"/>
    <path id="line_should_become_arc" fill="none" stroke="#000000" stroke-width="2" d="M 37 106 L 259 148"/>
</svg>
3
So what exactly data you have? Also please show short code to play with.MBo
I have all the data listed in the picture: rx & ry of the cylinder and the slice, as well as x,y coordinates of points A and B both on the cylinder, and in rectangular coordinates that have not been transferred to the cylinder (imagine 'unrolling' the cylinder so lines are straight). With that, I'm calculating θ using the law of cosines based on the rectangular coordinates.toakleaf
FYI, the straight lines on the cylinder are not obtained by slicing it with a plane and getting circles or ellipses. They are helices: en.wikipedia.org/wiki/Helix . So if you insist on the ellipses, I do not understand how you decide which plane through A and B you chose to slice the cylinder with to find the ellipse. There are infinitely many of them. What is the "canonnical one"? Do you want the ellipse for which the segment AB is parallel to the large axis of the ellipse?Futurologist
@toakleaf I actually realized what you are trying to do, you were emulating the projection of a 3D picture onto a 2D plane, and that the measurements you provide on your picture are in fact the mesurments of the two dimensional objects, not of the 3D original objects. However, even with the elipses, the problem is interesting, because, given cylinder rx and ry, the minor axis ry of the other ellipse and the 2D coordinates of the points A and B, you have to find the values of the angle thet, the major axis of the tilted ellipse ry and the location of the tilted ellipse's center.Futurologist
@toakleaf Is your original 3D cylinder straight circular cylinder? If you want to do this with helices, you will have to first build things in 3D and then project onto 2D. Helices, projected into 2D are probably not so easy to handle. So when you draw the 2D points A and B, you will have to calculate where on the 3D they are, find the formula of the 3D helix and project it back. But I think, it can be done. However, I do not know how this SVG works. I only know how to do the geometry.Futurologist

3 Answers

1
votes

I am assuming the following: you have a 3D coordinate system Oxyz and a right circular cylinder of radius a whose axis, running along the middle of the cylinder, coincides with the Oy axis. Then the circular base of the cylinder is perpendicular to the axis Oy and is therefore parallel to (or coincides with) the coordinate plane Oxz. In this coordinate system, the cylinder can described as all 3D points with the property

[x; y; z] such that x^2 + z^2 = a^2, while y is arbitrary or D <= y < = U.  

I am assuming that the cylinder is projected from the 3D Oxyz coordinate system onto the Oxy coordinate plane so that the circle, obtained by the intersection of the cylinder with the coordinate plane Oxz, is projected as an ellipse with major axis of length a, aligned with the axis Ox, and minor axis of length b, aligned with the axis Oz. On your picture a = cylinder rx and b = ry.

This information allows us to determine the direction of projection:

direction = [0; b; a]

i.e. for any point P = [x_3D; y_3D; z_3D] in the 3D system Oxyz, we take the line through P and parallel to the vector direction, and its intersection with Oxy is the projection of P on Oxy. The formula for this is

[x_3D; y_3D; z_3D] ---> [x_3D; y_3D - (b/a)*z_3D]

i.e.
x = x_3D
y = y_3D - (b/a)*z_3D

(there is another option for the direction: direction = [0; - b; a] if projection is done "from under" the Oxz axis instead of "over", but let us stick with "over") Conversely, if we are given a point [x; y] on the Oxy 2D coordinate plane, one can recover two points on the surface of the cylinder, which project onto [x; y]:

[x; y] ---> [x; y + (b/a)*sqrt(a^2 - x^2);  sqrt(a^2 - x^2)]

which is the point on the cylinder in the half space where the axis Oz is positive, and

[x; y] ---> [x; y - (b/a)*sqrt(a^2 - x^2);  - sqrt(a^2 - x^2)]

which is on the cylinder in the half space where the axis )z is negative.

The surface of the cylinder can be paramtrized by taking a flat planar rectangle and bend it in 3D by gluing together two of its parallel edges to form the right circular cylinder. This transformation can be written as

[s; y] ---> [a*cos(s/a);  y;  a*sin(s/a)]

i.e.
x = a*cos(s/a)
y = y
z = a*sin(s/a) 

Then a generic straight line on the flat square

y = y0 + m*(s - s0)

turns into the 3D curve lying on the surface of the cylinder

x = a*cos(s/a)
y = y0 + m*(s - s0)
z = a*sin(s/a) 

which is a helix.

Now, you are given as input

a, b, A = [xA; yA], B = [xB; yB]

Your goal is to find the equation of the curve in Oxy, that passes through A and B, and which is the projection of a helix on the cylinder in 3D.

Step1: Recover the 3D points A_3D and B_3D on the cylinder that project to A and B respectively. Using the formulas from above (and assuming that, let's say, A_3D and B_3D are on the positive side of Oz)

A_3D = [xA; yA + (b/a)*sqrt(a^2 - xA^2);  sqrt(a^2 - xA^2)];
B_3D = [xB; yB + (b/a)*sqrt(a^2 - xB^2);  sqrt(a^2 - xB^2)];

Step2: Represent A_3D and B_3D in the [s; y] surface coordinates of the cylinder:

s_A = a*arccos(xA);
y_A = yA + (b/a)*sqrt(a^2 - xA^2);

s_B = a*arccos(xB);
y_B = yB + (b/a)*sqrt(a^2 - xB^2);

Step 3: Construct the straight line in the [s; y] coordinates:

m = (y_B - y_A) / (s_B - s_A) 
  = (yB + (b/a)*sqrt(a^2-xB^2) - yA - (b/a)*sqrt(a^2-xA^2)) / (a*arccos(xB) - a*arccos(xA))
  = ((yB - yA) + (b/a)*(sqrt(a^2-xB^2) - sqrt(a^2-xA^2))) / (a*arccos(xB) - a*arccos(xA));

y = y_A + m*(s - s_A);

Step 4: Represent it in 3D as a helix:

x_3D = a*cos(s/a)
y_3D = y_A + m*(s - s_A)
z_3D = a*sin(s/a)

Step 5: Project the helix from the cylinder onto the coordinate Oxy plane, along the direction = [0; b; a]:

x = a*cos(s/a)    
y = y_A + m*(s - s_A) - b*sin(s/a)  
2
votes

Perhaps you want something like this
(I am not familiear with JS, so don't know how to provide calculated parameters for SVG curves)

Angle of AB in rectangular coordinates is 15 degrees, 1/cos(15)=1.035 - coefficient for rx. Y-coordinates of blue arc are intentionally shifted by 10 pixels

enter image description here

<svg width="400" height="400" xmlns="http://www.w3.org/2000/svg">
  <path d="M50 50 L50 250" stroke="black" fill="transparent"/>
  <path d="M50 250 A100 40 0 0 0 250 250" stroke="black" fill="transparent"/>
  <path d="M250 250 L250 50 A100 40 0 0 0 50 50 A100 40 0 0 0 250 50" stroke="black" fill="transparent"/>
  <path d="M50 150 A103.5 40 15 0 0 250 200 A103.5 40 15 0 0 50 150" stroke="black" fill="transparent"/>
  <path d="M100 210 A103.5 40 15 0 0 200 232" stroke="blue" fill="transparent"/>
</svg>
1
votes

It's pretty much plug and play. The only thing that required some effort is the two flags in the Arc command.

I started with the two path endpoints from your example SVG, and got rx and ry from the arc that forms the top of the cylinder. But you didn't provide any theta, so I picked one, and had to adjust the endpoints so that the slice lined up with the cylinder walls.

var arc = document.getElementById("line_should_become_arc");
var slice = document.getElementById("slice");

var Ax = 57, Ay = 126;
var Bx = 279, By = 168;

var rx = 148;
var ry = 32;
var theta = 14;  // 14 deg

var slice_rx = rx / Math.cos(theta * Math.PI / 180);

arc.setAttribute("d", ['M', Ax,Ay, 'A', slice_rx, ry, theta, 0, 0, Bx,By].join(' '));

slice.setAttribute("d", ['M', Ax,Ay, 'A', slice_rx, ry, theta, 1, 1, Bx,By].join(' '));
<svg width="400" height="400">
    <path id="cylinder" fill="none" stroke="#000000" stroke-width="2" d="M 0 32 a148 32 0 0 0 296 0 a148 32 0 0 0 -296 0 v185 a148 32 0 0 0 296 0 v-185"/>
    <path id="slice" fill="none" stroke="#000000" stroke-width="2" d="M 0,0"/>
    <path id="line_should_become_arc" fill="none" stroke="#f00" stroke-width="2" d="M 0,0"/>
</svg>