Wednesday, June 19, 2024

How to use CSS to draw a line between any two points given their coordinates

This doesn't fit my blog at ALL but I've spent the last day and change doing the geometric proof and teaching myself the corresponding CSS for it. Some guy on a video said, "there's no CSS function where you can just input the four coordinates and have it draw a line between them," and I said, "try me." And I built one. And now I'm committing it to the internet in case someone else needs it someday, because surely this has been done before but no one bothered to lay it out for those of us who can't remember their trigonometry, and also because I'll surely need it again.

For any two points (x1,y1) and (x2,y2) on the CSS pixel grid:

<!DOCTYPE html>
<html>
<head>
<style>
div.a {
--x1: 130px;
--y1: 500px;
--x2: 300px;
--y2: 460px;
--xlen: calc(var(--x2) - var(--x1));
--ylen: calc(var(--y2) - var(--y1));
--hyp: hypot(var(--xlen), var(--ylen));
--theta: atan2(var(--ylen), var(--xlen));
  height: 0px;
  border: 3px solid black;
  position: absolute;
 width: var(--hyp);
 top: var(--y1);
  left: var(--x1);
  transform-origin: top left;
  transform: rotate(var(--theta));
}

div.relative {
position:relative;
}
</style>
</head>
<body>

<div class="relative">
<div class="a"></div>
</div>

</body>
</html>

If you want to do the math yourself for cleaner CSS, the pseudocode goes like this:

Calculate xlen as the difference between the x-coordinates and ylen as the difference between the y-coordinates. It's fine if one's negative.

Do the Pythagorean Theorem on xlen and ylen (square root of the sum of the squares) to get the length  ("width" of the rectangle) of your line.

Calculate the arctangent of (ylen/xlen). That's your angle.

Pick one point's coordinates to be your (x1,y1).

Then you can trim the definition of "a" down to the following:

div.a {
  height: 0px;
  border: 3px solid black;
  position: absolute;
 width: 200; /*length of the line*/
 top: 36; /*y1*/
  left: 300; /*x1*/
  transform-origin: top left;
  transform: rotate(45deg); /*the angle*/
}

 

Proof:

Two points determine a line segment (and a line, but that's mostly irrelevant).

A line segment not parallel to an axis determines a right triangle, where the line segment is the hypotenuse and the legs are parallel to the axes respectively. (And if it IS parallel to an axis, you don't need to be doing all this math--a horizontal line doesn't need to be rotated at all, and a vertical line needs to be rotated 90 degrees.) (It actually determines two right triangles, but either one will do. Alternate interior angles between parallel lines are equivalent.)

The Pythagorean Theorem can be used to calculate the length of the hypotenuse.

By subtraction, the length of the segment parallel to the y-axis (now called Y) can be calculated from the difference between the y-coordinates of the two points, and the length of the segment parallel to the x-axis (now called X) can be calculated from the difference between the x-coordinates of the two points. (If the line is tilted up, Y will be negative--that's fine.)

The untransformed line is parallel to the x-axis. Therefore, the transformation can be considered as beginning with a line on leg X and rotated by "dragging" one end up or down, through the angle formed by leg X and the hypotenuse (now called θ)

Given the trigonometric definition of tangent as tan θ = Y/X and arctangent as its inverse, we can conclude that the arctan (Y/X) = θ.  

The line is defined as a rectangle with a border, setting height to 0 and width to the length of the hypotenuse. The "top" and "left" functions are set fixing one point as (x1,y1). 

(Positioning elements only works like this if position: absolute, and if you want the positioning to be relative to an area instead of the whole page, you need to put those absolutes inside a container with position: relative.)

Fix transform-origin to the (x1,y1) point to save yourself a metric ton of extra math, and rotate by θ.