in public/bubblesets.js [966:1097]
function connectItem(nonMembers, item, visited) {
var scannedLines = [];
var linesToCheck = [];
var itemCenter = new Point(item.centerX(), item.centerY());
var closestNeighbour = null;
var minLengthSq = Number.POSITIVE_INFINITY;
// discover the nearest neighbour with minimal interference items
visited.forEach(function(neighbourItem) {
var nCenter = new Point(neighbourItem.centerX(), neighbourItem.centerY());
var distanceSq = itemCenter.distanceSq(nCenter);
var completeLine = new Line(itemCenter.x(), itemCenter.y(), nCenter.x(), nCenter.y());
// augment distance by number of interfering items
var numberInterferenceItems = countInterferenceItems(nonMembers, completeLine);
// TODO is there a better function to consider interference in nearest-neighbour checking? This is hacky
if(distanceSq * (numberInterferenceItems + 1) * (numberInterferenceItems + 1) < minLengthSq) {
closestNeighbour = neighbourItem;
minLengthSq = distanceSq * (numberInterferenceItems + 1) * (numberInterferenceItems + 1);
}
});
// if there is a visited closest neighbour, add straight line between
// them to the positive energy to ensure connected clusters
if(closestNeighbour) {
var completeLine = new Line(itemCenter.x(), itemCenter.y(), closestNeighbour.centerX(), closestNeighbour.centerY());
// route the edge around intersecting nodes not in set
linesToCheck.push(completeLine);
var hasIntersection = true;
var iterations = 0;
var intersections = [];
intersections.length = 4;
var numIntersections = 0;
while(hasIntersection && iterations < maxRoutingIterations) {
hasIntersection = false;
while(!hasIntersection && linesToCheck.length) {
var line = linesToCheck.pop();
// resolve intersections in order along edge
var closestItem = getCenterItem(nonMembers, line);
if(closestItem) {
numIntersections = Intersection.testIntersection(line, closestItem, intersections);
// 2 intersections = line passes through item
if(numIntersections === 2) {
var tempMorphBuffer = morphBuffer;
var movePoint = rerouteLine(closestItem, tempMorphBuffer, intersections, true);
// test the movePoint already exists
var foundFirst = pointExists(movePoint, linesToCheck) || pointExists(movePoint, scannedLines);
var pointInside = isPointInsideNonMember(movePoint, nonMembers);
// prefer first corner, even if buffer becomes very small
while(!foundFirst && pointInside && (tempMorphBuffer >= 1)) {
// try a smaller buffer
tempMorphBuffer /= 1.5;
movePoint = rerouteLine(closestItem, tempMorphBuffer, intersections, true);
foundFirst = pointExists(movePoint, linesToCheck) || pointExists(movePoint, scannedLines);
pointInside = isPointInsideNonMember(movePoint, nonMembers);
}
if(movePoint && (!foundFirst) && (!pointInside)) {
// add 2 rerouted lines to check
linesToCheck.push(new Line(line.x1(), line.y1(), movePoint.x(), movePoint.y()));
linesToCheck.push(new Line(movePoint.x(), movePoint.y(), line.x2(), line.y2()));
// indicate intersection found
hasIntersection = true;
}
// if we didn't find a valid point around the
// first corner, try the second
if(!hasIntersection) {
tempMorphBuffer = morphBuffer;
movePoint = rerouteLine(closestItem, tempMorphBuffer, intersections, false);
var foundSecond = pointExists(movePoint, linesToCheck) || pointExists(movePoint, scannedLines);
pointInside = isPointInsideNonMember(movePoint, nonMembers);
// if both corners have been used, stop; otherwise gradually reduce buffer and try second corner
while(!foundSecond && pointInside && (tempMorphBuffer >= 1)) {
// try a smaller buffer
tempMorphBuffer /= 1.5;
movePoint = rerouteLine(closestItem, tempMorphBuffer, intersections, false);
foundSecond = pointExists(movePoint, linesToCheck) || pointExists(movePoint, scannedLines);
pointInside = isPointInsideNonMember(movePoint, nonMembers);
}
if(movePoint && (!foundSecond)) {
// add 2 rerouted lines to check
linesToCheck.push(new Line(line.x1(), line.y1(), movePoint.x(), movePoint.y()));
linesToCheck.push(new Line(movePoint.x(), movePoint.y(), line.x2(), line.y2()));
// indicate intersection found
hasIntersection = true;
}
}
}
} // end check of closest item
// no intersection found, mark this line as completed
if(!hasIntersection) {
scannedLines.push(line);
}
iterations += 1;
} // end inner loop - out of lines or found an intersection
} // end outer loop - no more intersections or out of iterations
// finalize any that were not rerouted (due to running out of
// iterations) or if we aren't morphing
while(linesToCheck.length) {
scannedLines.push(linesToCheck.pop());
}
// try to merge consecutive lines if possible
while(scannedLines.length) {
var line1 = scannedLines.pop();
if(scannedLines.length) {
var line2 = scannedLines.pop();
var mergeLine = new Line(line1.x1(), line1.y1(), line2.x2(), line2.y2());
// resolve intersections in order along edge
var closestItem = getCenterItem(nonMembers, mergeLine);
// merge most recent line and previous line
if(!closestItem) {
scannedLines.push(mergeLine);
} else {
linesToCheck.push(line1);
scannedLines.push(line2);
}
} else {
linesToCheck.push(line1);
}
}
scannedLines = linesToCheck;
}
return scannedLines;
}