block by kenpenn 6aafbc47f453ef11ec2fa7e37a29482f

line intersections

Full Screen

Given two lines, calculate the intersection point, if any

Adapted from How to check if two line segments intersect

Cloned with blockbuilder.org

index.html

<!doctype html>
<html>
<head>
  <meta charset='utf-8'>
  <title>line intersection demo</title>
  <style>
    * { box-sizing: border-box; }

    body {
      margin: 0;
      padding: 10px;
    }

    .viz-box {
      background-color: #efefef;
      border: 1px solid #aaa;
      border-radius: 5px;
      width: calc(100vw - 20px);
      height: calc(100vh - 20px);
      margin: auto;
      overflow: scroll;
    }
  </style>
</head>
<body>

  <div class="viz-box"></div>

<script src='https://d3js.org/d3.v4.min.js'></script>
<script>
  var vizBox = d3.select('.viz-box'),
      vizDimensions = vizBox.node().getBoundingClientRect(),
      svgWidth = vizDimensions.width,
      ctrX = svgWidth * 0.5,
      svgHeight = vizDimensions.height,
      ctrY = svgHeight * 0.5,
      colors = [
        'hsl-341', 'hsl-359', 'hsl-18',  'hsl-35',  'hsl-52',
        'hsl-83',  'hsl-127', 'hsl-160', 'hsl-190', 'hsl-212',
        'hsl-227', 'hsl-242', 'hsl-259', 'hsl-273', 'hsl-296'
      ],
      svg = vizBox.append('svg')
        .attr('width', svgWidth)
        .attr('height', svgHeight),
      leftX = 50,
      rightX = svgWidth - 50,
      topY = 50,
      bottomY = svgHeight - 50,
      marginArgs = [
        { id: 'top', x1: leftX, y1: topY, x2: rightX, y2: topY },
        { id: 'right', x1: rightX, y1: topY, x2: rightX, y2: bottomY },
        { id: 'bottom', x1: leftX, y1: bottomY, x2: rightX, y2: bottomY },
        { id: 'left', x1: leftX, y1: topY, x2: leftX, y2: bottomY}
      ],
      margins = [],
      offset = 270, // make first color line vertical
      increment = 360 / colors.length;

  marginArgs.forEach(function(margin) {
    margins.push(
      svg.append('line')
        .attr('id', 'margin-' + margin.id)
        .attr('x1', margin.x1)
        .attr('y1', margin.y1)
        .attr('x2', margin.x2)
        .attr('y2', margin.y2)
        .attr('stroke', '#aaa')
    )
  });

  colors.forEach(function(color, clx) {
    var degrees, radians, x2, y2, bgLine,
        mgx = 0,
        mgLen = margins.length,
        hsl = color.replace( '-', '(' ) + ', 100%, 50%)';

    degrees = offset + (increment * clx);
    radians = degrees * ( Math.PI / 180 );
    x2 = ctrX + (svgWidth * Math.cos(radians))
    y2 = ctrY + (svgWidth * Math.sin(radians))
    bgLine = svg.append('line')
      .attr('id', 'bgline-' + color)
      .attr('x1', ctrX.toFixed(2))
      .attr('y1', ctrY.toFixed(2))
      .attr('x2', x2.toFixed(2))
      .attr('y2', y2.toFixed(2))
      .attr('stroke', '#ddd');

    for (mgx; mgx < mgLen; mgx += 1) {
      intersects = getIntersects(margins[mgx], bgLine);

      if (intersects.x && intersects.y && intersects.onLineA && intersects.onLineB) {
        svg.append('line')
          .attr('id', 'line-' + color)
          .attr('x1', ctrX.toFixed(2))
          .attr('y1', ctrY.toFixed(2))
          .attr('x2', intersects.x.toFixed(2))
          .attr('y2', intersects.y.toFixed(2))
          .attr('stroke', hsl)
          .attr('stroke-width', 1.5);

        svg.append('circle')
          .attr('id', 'circ-' + color)
          .attr('cx', intersects.x.toFixed(2))
          .attr('cy', intersects.y.toFixed(2))
          .attr('r', 5)
          .attr('fill', hsl);

        break;
      }
    }
  });

  function getIntersects(lineA, lineB) {
    // if the lines intersect,
    // var 'intersect' contains the x and y of the intersection (treating the lines as infinite)
    // and booleans for whether line segment A or line segment B contain the point
    // adapted from https://martin-thoma.com/how-to-check-if-two-line-segments-intersect/
    var a, b, numeratorA, numeratorB, denominator,
        lineAx1 = +lineA.attr('x1'),
        lineAy1 = +lineA.attr('y1'),
        lineAx2 = +lineA.attr('x2'),
        lineAy2 = +lineA.attr('y2'),
        lineBx1 = +lineB.attr('x1'),
        lineBy1 = +lineB.attr('y1'),
        lineBx2 = +lineB.attr('x2'),
        lineBy2 = +lineB.attr('y2'),
        intersect = { x: null, y: null, onLineA: false, onLineB: false };

    denominator = ((lineBy2 - lineBy1) * (lineAx2 - lineAx1)) - ((lineBx2 - lineBx1) * (lineAy2 - lineAy1));

    if (denominator == 0) { return intersect; }

    a = lineAy1 - lineBy1;
    b = lineAx1 - lineBx1;
    numeratorA = ((lineBx2 - lineBx1) * a) - ((lineBy2 - lineBy1) * b);
    numeratorB = ((lineAx2 - lineAx1) * a) - ((lineAy2 - lineAy1) * b);
    a = numeratorA / denominator;
    b = numeratorB / denominator;

      // if we cast these lines infinitely in both directions, they intersect here:
    intersect.x = lineAx1 + (a * (lineAx2 - lineAx1));
    intersect.y = lineAy1 + (a * (lineAy2 - lineAy1));

      // it is worth noting that this should be the same as:
      // x = lineBx1 + (b * (lineBx2 - lineBx1));
      // y = lineBx1 + (b * (lineBy2 - lineBy1));

      // if lineA is a segment and lineB is infinite, they intersect if:
    if (a > 0 && a < 1) {
      intersect.onLineA = true;
    }
      // if lineB is a segment and lineA is infinite, they intersect if:
    if (b > 0 && b < 1) {
      intersect.onLineB = true;
    }
      // if line1 and line2 are segments, they intersect if both of the above are true
    return intersect;
  }
</script>
</body>
</html>