Skip to content

Instantly share code, notes, and snippets.

@kenpenn
Last active August 28, 2016 21:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kenpenn/6aafbc47f453ef11ec2fa7e37a29482f to your computer and use it in GitHub Desktop.
Save kenpenn/6aafbc47f453ef11ec2fa7e37a29482f to your computer and use it in GitHub Desktop.
line intersections
license: gpl-3.0
border: yes
height: 560
scrolling: yes
<!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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment