Skip to main content

On This Page

Implementing Geometric Collision Detection in Chemical Drawing Software

3 min read
Share

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