algorithm - How do I find the (x, y) coordinates of the point q on a closed 2D composite Beziér curve closest to the (x, y) coordinates of some arbitrary point p? -
i have set of 2d cartesian points [b]
, proceed clockwise start , form closed shape. each 1 of these has own companion 2d cartesian points q0
, q1
define beziér curve around point (along preceding , succeeding points). together, these points define closed 2d composite beziér curve.
i have separate point p
arbitrary 2d cartesian point on same plane. is there simple algorithm finding (x, y)
coordinates of new 2d cartesian point q
closest point on path p
?
as illustrated here, have points b[0]
b[3]
, handles b[n].q0
, b[n].q1
, , have arbitrary point p
. i'm trying calculate point q
, not floating-point position along curve, pair of (x, y)
coordinates.
i tried searching this, seemed for small curve, others way on head abstract mathematics , scientific research papers.
any leads me toward algorithmic solution appreciated, if can translated c-like language rather pure math in above answers.
by adapting the algorithm posted tatarize, came solution in swift, should translatable other languages:
struct bezierpoint { let q0: cgpoint let point: cgpoint let q1: cgpoint } struct simplebeziercurve { let left: bezierpoint let right: bezierpoint } class bezierpath { var pathpoints = [bezierpoint]() func findclosestpoint(to targetpoint: cgpoint) -> cgpoint { let segments = allsegments() guard segments.count > 0 else { return targetpoint } var closestpoint = (distance: cgfloat.infinity, point: cgpoint(x: cgfloat.infinity, y: cgfloat.infinity)) segments.foreach{ curve in let thispoint = bezierpath.findclosestpoint(to: targetpoint, along: curve) let distance = finddistance(from: targetpoint, to: thispoint) if (distance < closestpoint.distance) { closestpoint = (distance: distance, point: thispoint) } } return closestpoint.point } func allsegments() -> [simplebeziercurve] { guard pathpoints.count > 0 else { return [] } var segments = [simplebeziercurve]() var prevpoint = pathpoints[0] in 1 ..< pathpoints.count { let thispoint = pathpoints[i] segments.append(simplebeziercurve(left: prevpoint, right: thispoint)) prevpoint = thispoint } segments.append(simplebeziercurve(left: prevpoint, right: pathpoints[0])) return segments } static func findclosestpoint(to point: cgpoint, along curve: simplebeziercurve) -> cgpoint { return findclosestpointtocubicbezier(to: point, slices: 10, iterations: 10, along: curve) } // adapted https://stackoverflow.com/a/34520607/3939277 static func findclosestpointtocubicbezier(to target: cgpoint, slices: int, iterations: int, along curve: simplebeziercurve) -> cgpoint { return findclosestpointtocubicbezier(iterations: iterations, to: target, start: 0, end: 1, slices: slices, along: curve) } // adapted https://stackoverflow.com/a/34520607/3939277 private static func findclosestpointtocubicbezier(iterations iterations: int, to: cgpoint, start: cgfloat, end: cgfloat, slices: int, along curve: simplebeziercurve) -> cgpoint { if iterations <= 0 { let position = (start + end) / 2 let point = self.point(for: position, along: curve) return point } let tick = (end - start) / slices var best = cgfloat(0) var bestdistance = cgfloat.infinity var currentdistance: cgfloat var t = start while (t <= end) { //b(t) = (1-t)**3 p0 + 3(1 - t)**2 t p1 + 3(1-t)t**2 p2 + t**3 p3 let point = self.point(for: t, along: curve) var dx = point.x - to.x; var dy = point.y - to.y; dx *= dx; dy *= dy; currentdistance = dx + dy; if (currentdistance < bestdistance) { bestdistance = currentdistance; best = t; } t += tick; } return findclosestpointtocubicbezier(iterations: iterations - 1, to: to, start: max(best - tick, 0.0), end: min(best + tick, 1.0), slices: slices, along: curve); } static func point(for t: cgfloat, along curve: simplebeziercurve) -> cgpoint { // had broken avoid "expression complex" error let x0 = curve.left.point.x let x1 = curve.left.q1.x let x2 = curve.right.q0.x let x3 = curve.right.point.x let y0 = curve.left.point.y let y1 = curve.left.q1.y let y2 = curve.right.q0.y let y3 = curve.right.point.y let x = (1 - t) * (1 - t) * (1 - t) * x0 + 3 * (1 - t) * (1 - t) * t * x1 + 3 * (1 - t) * t * t * x2 + t * t * t * x3 let y = (1 - t) * (1 - t) * (1 - t) * y0 + 3 * (1 - t) * (1 - t) * t * y1 + 3 * (1 - t) * t * t * y2 + t * t * t * y3 return cgpoint(x: x, y: y) } } // possibly in file func finddistance(from a: cgpoint, b: cgpoint) -> cgfloat { return sqrt(pow(a.x - b.x, 2) + pow(a.y - b.y, 2)); }
Comments
Post a Comment