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?

four blue points labeled b[0] through b[4], each 2 child green points labeled b[n].q0 , b[n].q1 connected blue parent grey lines, forming red path basic shape dictated positions of blue points, , curvature green ones. above curve there lies orange point p, connected grey line purple point q, lies on red path @ closest point 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)); } 

github gist


Comments