Implementing Geometric Collision Detection in Chemical Drawing Software
These articles are AI-generated summaries. Please check the original sources for full details.
Building a ChemDraw clone with DDD (Part III): Interacting with Atoms and Bonds
Fredi Manuel Barraza Hernández details the development of a collision engine for a chemical drawing tool. The system employs vector math and clamping to ensure that bond selection is restricted to physical line segments rather than infinite trajectories.
Why This Matters
In complex graphical editors, the technical reality of interaction often conflicts with simple geometric models. Using standard infinite line equations for bond selection results in ‘ghost collisions’ across the canvas, necessitating a projection-based approach that enforces physical boundaries. This implementation highlights the trade-off between intuitive UX and the architectural risk of creating ‘God Objects’ when mixing domain logic with spatial geometry.
Key Insights
- Atom selection is achieved by calculating the Euclidean distance between the cursor and node coordinates, selecting the candidate with the minimum distance within a radius.
- Bond selection addresses the ‘infinite line’ problem by projecting mouse coordinates onto a segment and calculating a projection factor (t).
- Clamping logic using Math.max(0, Math.min(1, t)) is critical to restrict collision detection to the area between Atom A and Atom B.
- The ‘Atom Priority’ business rule ensures that atoms hijack selection events over bonds to prevent accidental bond manipulation in overlapping regions.
- Mixing 2D geometry formulas with chemical valence logic leads to architectural debt, transforming the Molecule class into a God Object.
Working Examples
Method to find the closest atom using Euclidean distance and sorting candidates by proximity.
private findClosestAtom(
x: number,
y: number,
radius: number,
): { atom: Atom; distance: number } | null {
const candidates = [...this._atoms.values()]
.map((atom) => ({
atom,
distance: Math.sqrt(Math.pow(atom.x - x, 2) + Math.pow(atom.y - y, 2)),
}))
.filter(({ distance }) => distance <= radius)
.sort((a, b) => a.distance - b.distance);
return candidates[0] || null;
}
Vector projection algorithm with clamping to determine the distance between a point and a specific line segment.
private pointToSegmentDistance(
px: number, py: number,
ax: number, ay: number,
bx: number, by: number
): number {
const abx = bx - ax;
const aby = by - ay;
const apx = px - ax;
const apy = py - ay;
const abLengthSq = abx * abx + aby * aby;
if (abLengthSq === 0) return Math.sqrt(apx * apx + apy * apy);
const t = Math.max(0, Math.min(1, (apx * abx + apy * aby) / abLengthSq));
const closestX = ax + t * abx;
const closestY = ay + t * aby;
return Math.sqrt(Math.pow(px - closestX, 2) + Math.pow(py - closestY, 2));
}
Practical Applications
- Chemical Editor Interaction: Implementing Euclidean distance for node selection allows users to target atoms precisely in dense molecular structures. Pitfall: Failing to implement distance-based sorting can cause selection jitter when multiple atoms are within the search radius.
- Line Segment Collision: Using clamped scalar projection for bond selection prevents ‘ghost hits’ along a bond’s infinite trajectory. Pitfall: Neglecting to clamp the projection factor (t) allows users to select bonds from anywhere on the canvas if they align with the trajectory.
References:
Continue reading
Next article
Implementing Approval-Gated AI for High-Stakes Personal Data
Related Content
Optimizing Keyboard Ergonomics with Home-Bottom Row Modifier Clusters
The Kenkyo layout utilizes Kanata to implement Home-Bottom Row Modifier Clusters, reducing finger strain by overloading letter keys.
Implementing Factur-X: Building Compliant EU E-Invoices from Scratch in TypeScript
Learn to implement the Factur-X hybrid PDF+XML format for the EU's 2026 e-invoicing reform using TypeScript and zero paid dependencies.
The Engineering Behind Mouse Sensors: From Rubber Balls to Laser Speckle Patterns
Modern optical mice process up to 12,000 images per second using computer vision algorithms to calculate precise cursor movement.