Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
The math behind a “drag to scale this line” operation.
extension Line {
/** Returns a scaled line according to the projection of `scalingPoint` onto the receiver.
- parameter scalingPoint: A point interpreted as a vector used for scaling this line. This point should be something like the location of a touch or cursor during a "drag to scale" operation, but the point doesn't have to lie along the line itself. The point **must** be in the same coordinate space as the line.
func lineByScalingToTouchPoint(scalingPoint: Point) -> Line {
// This algorithm works by
// - treating the incoming point as a vector,
// - treating the line (self) as a vector along an infinite line
// - offsetting both vectors by the start point of the line
// - so the line has 0 as its origin to make math easier
// - projecting the point vector onto the line
// - finally offseting the projected vector BACK by the start amount of the line.
// Projecting onto self is like casting a shadow of the scaling point onto our line.
// That shadow is our scaled line.
// Subtract the start from the end of the line and scaling point, so the line has a 0 origin.
let lineVector = end - start
let scalingPointOffsetByStart = scalingPoint - start
// Projection(scalingVector) = constant * lineVector = ((scalingVector dot-product lineVector) / (lineVec dot-product itself)) * lineVec
let vectorScalingConstantNumerator = scalingPointOffsetByStart.dotProduct(lineVector)
let vectorScalingConstantDenomenator = lineVector.dotProduct(lineVector)
let vectorScalingConstant = vectorScalingConstantNumerator / vectorScalingConstantDenomenator
// see
let projectionVector = vectorScalingConstant * lineVector
return Line(start: start, end: projectionVector + start)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment