raw Math
RAW Math Geometry Computational Geometry

Reduce the Length of a Line Segment by a Certain Amount

Robert Eisele

Given a line segment from \(\mathbf{p}_1\) to \(\mathbf{p}_2\), reducing its length means moving one or both endpoints along the segment without changing its direction. The same formula covers trimming the start, the end, or both ends equally.

The dashed line shows the original segment; the solid line is the shortened result.

A Single Formula for All Three Cases

Let

\[ \mathbf{v}=\mathbf{p}_2-\mathbf{p}_1, \qquad L=\lVert\mathbf{v}\rVert, \qquad \hat{\mathbf{v}}=\frac{\mathbf{v}}{L}. \]

The unit vector \(\hat{\mathbf{v}}\) points from \(\mathbf{p}_1\) to \(\mathbf{p}_2\). If an amount \(a\) is removed from the start and an amount \(b\) from the end, the new endpoints are

\[ \boxed{ \mathbf{p}_1'=\mathbf{p}_1+a\hat{\mathbf{v}}, \qquad \mathbf{p}_2'=\mathbf{p}_2-b\hat{\mathbf{v}} }. \]

The vector of the shortened segment is therefore

\[ \begin{aligned} \mathbf{p}_2'-\mathbf{p}_1' &=\mathbf{p}_2-\mathbf{p}_1-(a+b)\hat{\mathbf{v}}\\ &=\left(L-a-b\right)\hat{\mathbf{v}}. \end{aligned} \]

Its length is exactly \(L-a-b\), provided that \(a\ge 0\), \(b\ge 0\), and \(a+b\le L\). The direction remains unchanged because the result is still a nonnegative multiple of the original unit vector.

Trimming One End

To shorten the segment by \(r\) at \(\mathbf{p}_2\), choose \(a=0\) and \(b=r\):

\[ \boxed{ \mathbf{p}_2'=\mathbf{p}_2-r\frac{\mathbf{p}_2-\mathbf{p}_1}{\lVert\mathbf{p}_2-\mathbf{p}_1\rVert} }. \]

To shorten it at \(\mathbf{p}_1\), choose \(a=r\) and \(b=0\):

\[ \boxed{ \mathbf{p}_1'=\mathbf{p}_1+r\frac{\mathbf{p}_2-\mathbf{p}_1}{\lVert\mathbf{p}_2-\mathbf{p}_1\rVert} }. \]

Trimming Both Ends Equally

If \(r\) denotes the total reduction, each endpoint moves by \(r/2\). Substituting \(a=b=r/2\) gives

\[ \boxed{ \begin{aligned} \mathbf{p}_1'&=\mathbf{p}_1+\frac{r}{2}\hat{\mathbf{v}},\\ \mathbf{p}_2'&=\mathbf{p}_2-\frac{r}{2}\hat{\mathbf{v}}. \end{aligned} } \]

The midpoint is preserved: the two endpoint displacements cancel when the new endpoints are averaged.

Robust JavaScript Implementation

A general function is less repetitive than maintaining three independent implementations. It also gives the degenerate and over-trimming cases one explicit contract.

function trimSegment(p1, p2, startAmount, endAmount) {
  if (![startAmount, endAmount].every(Number.isFinite) ||
      startAmount < 0 || endAmount < 0) {
    throw new RangeError("Trim amounts must be finite and nonnegative");
  }

  const dx = p2.x - p1.x;
  const dy = p2.y - p1.y;
  const length = Math.hypot(dx, dy);
  const total = startAmount + endAmount;

  if (total > length) {
    throw new RangeError("Trim amounts exceed the segment length");
  }
  if (length === 0) {
    return [{ ...p1 }, { ...p2 }];
  }

  const ux = dx / length;
  const uy = dy / length;

  return [{
    x: p1.x + startAmount * ux,
    y: p1.y + startAmount * uy
  }, {
    x: p2.x - endAmount * ux,
    y: p2.y - endAmount * uy
  }];
}

function reduceEnd(p1, p2, amount) {
  return trimSegment(p1, p2, 0, amount);
}

function reduceStart(p1, p2, amount) {
  return trimSegment(p1, p2, amount, 0);
}

function reduceEqually(p1, p2, amount) {
  return trimSegment(p1, p2, amount / 2, amount / 2);
}

When the total trim equals the original length, both returned endpoints coincide. A larger value is rejected rather than silently reversing the segment. A zero-length segment is valid only with zero total trimming; the early return avoids division by zero.

The formulas work unchanged in three or more dimensions: compute the Euclidean length from all components, normalize the direction vector, and apply the same endpoint offsets. For reusable two-dimensional vector operations, Vector2.js provides normalization, scaling, and addition directly.