Created
August 3, 2016 23:13
-
-
Save jbrennan/d6abb08d0d25b736eb11e0ffea81314c to your computer and use it in GitHub Desktop.
The math behind a “drag to scale this line” operation.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 https://www.youtube.com/watch?v=27vT-NWuw0M | |
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