Breaking a line segment into two where it encounters a rectangle I have developed a function that takes a list of line segments and a rectangle that breaks the line segment into two where it encounters the rectangle. Here is a picture demonstration of what it does:

And here is a full demo code representation here:


var ctx = document.querySelector("canvas").getContext("2d");

function drawSegments(segments) {
  for (let seg of segments) {
    ctx.beginPath();
    ctx.moveTo(seg.x1, seg.y1);
    ctx.lineTo(seg.x2, seg.y2);
    ctx.stroke();
  }
}

function drawRectangles(rectangles) {
  for (let r of rectangles) {
    ctx.beginPath();
    ctx.rect(r.x, r.y, r.w, r.h);
    ctx.stroke();
  }
}

// Returns the point (x, y) where two line segments intersect
function getLineIntersection(p0_x, p0_y, p1_x, p1_y, p2_x, p2_y, p3_x, p3_y) {
  let s1_x = p1_x - p0_x;
  let s1_y = p1_y - p0_y;
  let s2_x = p3_x - p2_x;
  let s2_y = p3_y - p2_y;

  let s = (-s1_y * (p0_x - p2_x) + s1_x * (p0_y - p2_y)) / (-s2_x * s1_y + s1_x * s2_y);
  let t = ( s2_x * (p0_y - p2_y) - s2_y * (p0_x - p2_x)) / (-s2_x * s1_y + s1_x * s2_y);

  if (s &gt;= 0 &amp;&amp; s &lt;= 1 &amp;&amp; t &gt;= 0 &amp;&amp; t &lt;= 1) { 
    return { x: p0_x + (t * s1_x), y: p0_y + (t * s1_y) };
  }

  return false;
}

function breakSegments(segments, rect) {
  let rLeft = rect.x;
  let rTop = rect.y;
  let rRight = rect.x + rect.w;
  let rBot = rect.y + rect.h;

  for (let i = 0; i &lt; segments.length; i++) {
    let s = segments[i];

    let nIntersection = getLineIntersection(s.x1, s.y1, s.x2, s.y2, rLeft, rTop, rRight, rTop);
    let wIntersection = getLineIntersection(s.x1, s.y1, s.x2, s.y2, rLeft, rTop, rLeft, rBot);
    let eIntersection = getLineIntersection(s.x1, s.y1, s.x2, s.y2, rRight, rTop, rRight, rBot);
    let sIntersection = getLineIntersection(s.x1, s.y1, s.x2, s.y2, rLeft, rBot, rRight, rBot);

    var closeIntersection, farIntersection;

    if (nIntersection &amp;&amp; sIntersection) {
      let nXDelta = nIntersection.x - s.x1;
      let nYDelta = nIntersection.y - s.y1;

      let sXDelta = sIntersection.x - s.x1;
      let sYDelta = sIntersection.y - s.y1;

      if (nXDelta * nXDelta + nYDelta * nYDelta &lt; sXDelta * sXDelta + sYDelta * sYDelta) {
        closeIntersection = nIntersection;
        farIntersection = sIntersection;
      } else {
        closeIntersection = sIntersection;
        farIntersection = nIntersection;
      }
    } else if (wIntersection &amp;&amp; eIntersection) {
      let wXDelta = wIntersection.x - s.x1;
      let wYDelta = wIntersection.y - s.y1;

      let eXDelta = eIntersection.x - s.x1;
      let eYDelta = eIntersection.y - s.y1;

      if (wXDelta * wXDelta + wYDelta * wYDelta &lt; eXDelta * eXDelta + eYDelta * eYDelta) {
        closeIntersection = wIntersection;
        farIntersection = eIntersection;
      } else {
        closeIntersection = eIntersection;
        farIntersection = wIntersection;
      }
    } else {
      continue;
    }

    segments.splice(i + 1, 0, { x1: farIntersection.x, y1: farIntersection.y, x2: s.x2, y2: s.y2 });

    s.x2 = closeIntersection.x; 
    s.y2 = closeIntersection.y;

    break;
  }
}


let lineSegments = [
  { x1: 0, y1: 50, x2: 250, y2: 50 },
  { x1: 250, y1: 50, x2: 250, y2: 250 },
  { x1: 250, y1: 250, x2: 100, y2: 250 },
  { x1: 100, y1: 250, x2: 80, y2: 75 },
  { x1: 400, y1: 550, x2: 525, y2: 550 },
  { x1: 525, y1: 550, x2: 550, y2: 350 },
  { x1: 550, y1: 350, x2: 300, y2: 350 },
];

let rects = [
  { x: 200, y: 240, w: 16, h: 16 },
  { x: 500, y: 340, w: 16, h: 16 }
]

breakSegments(lineSegments, rects[0]);
breakSegments(lineSegments, rects[1]);

drawSegments(lineSegments);
drawRectangles(rects);
&lt;canvas width="800" height="800"&gt;



As you can see it already works. What I don't like is how complex it is. I feel like this should be able to be accomplished with less code. Specifically, breakSegments is very verbose in how it determines which intersection is closer, as I use the distance formula to determine this. Is there no better way of accomplishing this?
General points

Use const when a variable does not change.

Avoid using underscore in JavaScript names

Making names too long makes code hard to read, especial when the relevant part of the name is tiny. eg nIntersection, eIntersection, sIntersection, wIntersection when in busy lines the n,e,w,s can easily be overlooked.

The function getLineIntersection returns a point or false. Rather than return a point return the value t.
This value represents the unit distance along the line of the intersection. It is easier to work with this value (finding closest point to start of line). See example

In the same function you calculate the cross product of the two lines as vectors. There is room for improvement.

You calculate this value twice. You need do it only once.

When the line are parallel the cross of the vectors will be zero. Lines will not intercept when parallel. Also it means you will be dividing by zero.

You calculate the unit distance along each line then you check if the intercept point is on both lines. You can improve the performance by checking each line as you go. No need to find the unit distance on the second line if it does not cross the first.




Rendering
When drawing paths that all have the same style move the ctx.beginPath outside the loop. ctx.beginPath forces a GPU state change and on many systems this can slow the whole device down, not just your page. (see Rewrite)
Vector Math
Consider creating or using a vector library to reduce long messy lines like let t = ( s2_x * (p0_y - p2_y) - s2_y * (p0_x - p2_x)) / (-s2_x * s1_y + s1_x * s2_y);
Expressions like that are very prone to typos and errors. Using a vector library gives readable abstraction to the operation. eg the above line may becomes t = s2.cross(line1.p1.sub(line2.p1)) / s1.cross(s2)
Even using a very basic library to define points and lines will help reduce the amount of code. see Rewrite.
Problems?
Hard to know if these are problems or if there are constraints that mean the following will not be of issue.

A line segment may start, end, or be completely inside a box. There is no code that take this into account

You stop clipping line segments on the first line clipped. This means that is more than one segment crosses the box the second etc will not get clipped.

Assuming that the code is working, then there is no need to clip when the line segment ends on the box's  edge. Thus the test if (s &gt;= 0 &amp;&amp; s &lt;= 1 &amp;&amp; t &gt;= 0 &amp;&amp; t &lt;= 1) {  can be if (s &gt; 0 &amp;&amp; s &lt; 1 &amp;&amp; t &gt; 0 &amp;&amp; t &lt; 1) { 

You only clip lines is they cross the box, If lines  cross the corner of a box you do not clip it?


Rewrite
I have added 4 very basic objects, Point2, Vector2, Line2, and  Rectangle2 (2 stands for 2D) and use these to store and do the calculations.
getLineIntersection returns the unit distance along the line segment rather than a point. The unit distance is push to an array. If 2 units are in that array then the segment will be clipped. The units distances are sorted to get the first and second intercepts.
The behavior remains the same as your original code apart from clipping only crossing opposite sides of the box. The rewrite will clip if line crosses adjoining box edges. The data includes this as a 3rd box.


const ctx = canvas.getContext("2d");


const Point2 = (x = 0, y = 0) =&gt; ({x, y});
const Vector2 = Point2;
const Line2 = (p1, p2) =&gt; ({p1, p2});
const Rectangle2 = (x, y, w, h) =&gt; ({p1: Point2(x, y), size: Vector2(w, h)});

function drawSegments(segments) {
    ctx.beginPath();
    for (const seg of segments) {
        ctx.moveTo(seg.p1.x, seg.p1.y);
        ctx.lineTo(seg.p2.x, seg.p2.y);
    }
    ctx.stroke();
}

function drawRectangles(rectangles) {
    ctx.beginPath();
    for (const r of rectangles) {
        ctx.rect(r.p1.x, r.p1.y, r.size.x, r.size.y);
    }
    ctx.stroke();
}

function getLineIntersection(l1, l2) {
    const s1 = Vector2(l1.p2.x - l1.p1.x, l1.p2.y - l1.p1.y);
    const s2 = Vector2(l2.p2.x - l2.p1.x, l2.p2.y - l2.p1.y);
    const df = Vector2(l1.p1.x - l2.p1.x, l1.p1.y - l2.p1.y);
    const cross = s1.x * s2.y - s1.y * s2.x;
    if (cross !== 0) {
        const u = (s1.x * df.y - s1.y * df.x) / cross;
        if (u &gt; 0 &amp;&amp; u &lt; 1) {
            const u = (s2.x * df.y - s2.y * df.x) / cross;
            if (u &gt; 0 &amp;&amp; u &lt; 1) { return u }
        }
    }
    return false;
}

function breakSegments(segments, rect) {
    const r1 = rect.p1;
    const r2 = Point2(r1.x + rect.size.x, r1.y);
    const r3 = Point2(r2.x, r2.y + rect.size.y);
    const r4 = Point2(r1.x, r3.y);
    
    const boxLines = [
        Line2(r1, r2),
        Line2(r2, r3),
        Line2(r3, r4),
        Line2(r4, r1),
    ];
    const units = [];
    var i = 0;
    while (i &lt; segments.length) {
        units.length = 0;
        const line = segments[i++];
        for (const bLine of boxLines) {
            const u = getLineIntersection(line, bLine);
            u &amp;&amp; units.push(u);
        }
    
        if (units.length === 2) {
            units[0] &gt; units[1] &amp;&amp; ([units[0], units[1]] = [units[1], units[0]]);
            const [u1, u2] = units;
            const p1 = line.p1, p2 = line.p2;
            const vx = p2.x - p1.x;
            const vy = p2.y - p1.y;
            line.p2.x = line.p1.x + vx * u1;
            line.p2.y = line.p1.y + vy * u1;
            segments.splice(i - 1, 0, Line2(
                Point2(line.p1.x + vx * u2, line.p1.y + vy * u2),
                Point2(line.p1.x + vx, line.p1.y + vy)
            ));
            break;
        }
    }
}

const lineSegments = [
    Line2(Point2(10,   60),  Point2(500, 60)),
    Line2(Point2(100, 10), Point2(100, 120)),
    Line2(Point2(220, 170), Point2(300, 110)), // cross corner segment
    //Line2(Point2(256, 0), Point2(256, 120)),  // these lines do not get clipped
    //Line2(Point2(263, 120), Point2(263, 0)),
];

const rects = [
    Rectangle2(250, 50, 16, 16),
    Rectangle2(90, 80, 16, 16),
    Rectangle2(250, 140, 36, 36)  // cross corner box
];
breakSegments(lineSegments, rects[0]);
breakSegments(lineSegments, rects[1]);
breakSegments(lineSegments, rects[2]);
drawSegments(lineSegments);
drawRectangles(rects);
&lt;canvas id="canvas" width="500" height="500"&gt;&lt;/canvas&gt;