Checks a line or outline taken from other linework before a landscape object uses it, and detects when that source has moved.
How the precision is kept
Points are copied as 64-bit numbers without rounding. The checks use fixed absolute tolerances: repeated points within 1e-9 are dropped and an outline is closed when its ends are within 1e-6. A change of the source is detected from a hash of the coordinates rounded to 1e-6, so a move smaller than that is not followed.
Calculations under this policy
3 calculations in 1 source file. The code is read from the released app when this page loads. It is the calculation people run, not a copy kept for this page.
A short code that changes when the source linework moves, so the linked object knows to update.
hash of round(x × 10⁶), round(y × 10⁶) for every point
Coordinates are rounded to 1e-6 only for the hash; the points themselves are not changed. A source that moves by less than about half of 1e-6 keeps the same fingerprint and the linked object is not updated.
landscape/links.js
lines 28 to 33
CivilCraft 0.8.1
export function fingerprint(pts) {
let h = 0x811c9dc5;
const s = (pts || []).map((p) => `${Math.round(p.x * 1e6)},${Math.round(p.y * 1e6)}`).join(';');
for (let i = 0; i < s.length; i++) { h ^= s.charCodeAt(i); h = Math.imul(h, 0x01000193) >>> 0; }
return h.toString(16).padStart(8, '0');
}
Clean and check a line or outline
64-bit
Drops repeated points, closes an outline and refuses linework that is too short, too far away, open or self-crossing.
Kept points are exact copies of the input. A point is dropped when it is within 1e-9 of the one before it, and an outline's last point is removed when it is within 1e-6 of the first; both distances are absolute, not scaled to the size of the line.
Whether any two edges of the line cross away from their ends.
Uses segCross in both directions and counts a crossing only when both parameters are strictly inside 1e-9 and 1 − 1e-9, so edges that only touch at an end do not count. Lines with more than 2000 points return false without being tested.
landscape/links.js
lines 54 to 67
CivilCraft 0.8.1
function crossesItself(pts, closed) {
const n = pts.length, m = closed ? n : n - 1;
if (n > 2000) return false; // big lines: checked by the host's own import limits, not here (O(n²))
for (let i = 0; i < m; i++) {
const a = pts[i], b = pts[(i + 1) % n];
for (let j = i + 2; j < m; j++) {
if (closed && i === 0 && j === n - 1) continue;
const c = pts[j], d = pts[(j + 1) % n];
const t = segCross(a, b, c, d);
if (t !== null && t > 1e-9 && t < 1 - 1e-9) { const u = segCross(c, d, a, b); if (u !== null && u > 1e-9 && u < 1 - 1e-9) return true; }
}
}
return false;
}
In detail
Method
Repeated points are removed, an outline is closed, and every pair of non-neighboring edges is tested for a proper crossing with segCross from areas.js. The fingerprint is FNV-1a over the coordinates rounded to 1e-6, as the file header says.
Number types
64-bit numbers; a 32-bit integer hash for the fingerprint only.
Tolerances
Repeated point: distance of 1e-9 or less, absolute. Closing tolerance 1e-6, absolute. A crossing counts only when it lies between 1e-9 and 1 − 1e-9 along both edges. Coordinates beyond ±1e9 are refused. Fingerprint resolution 1e-6.
Limits
Lines with more than 2000 points are not tested for self-crossing here.
Approach: 64-bit, direct
Every value is a 64-bit number from input to result. Formulas are closed form or a direct solve, and every comparison uses a stated tolerance.