Fitting UIBezierPath/CGPath to circle

限于喜欢 提交于 2019-12-24 07:27:38

问题


I need to aspectFit arbitrarily shaped paths to the interior of a circle.

I can fit rectangular paths to the circle by calculating the size, building a dest rect, then scaling the path to fit the dest rect.

let targetSize = circleDiameter / sqrt(2)
let destRect = CGRect(x: 0, y: 0, width: targetSize, height: targetSize)
// ... Scale the path to the rect, using boundingBox of path.

But the scaling process isn't the issue; it's really how to calculate the required scale.

A bounding-box based approach provides a poor fit for non-rectangular shapes, and a very poor fit for a shape such as a circle.

Can anyone suggest a way to fit an arbitrary path to the interior of a circle? Thanks.

[edit - lots of research later]

The problem can be seen in this image:

  • The blue rectangle represents the native image, trimmed to the path.

  • The red circle is the minimum bounding circle.

  • The green rectangle represents the bounding box of the bounding circle.

I need to calculate the red circle, so I can use it to scale the path.

Turns out this is known as the Minimum Enclosing Circle Problem (AKA Smallest Enclosing Circle).

Alas, I cannot find a Swift or Objective-C implementation of any of the known algorithms, and will probably have to tackle some Java/JS/C++ code. If someone can save me the time, please do! Warm fuzzies inbound.

[another edit, time flies]

Turns out to be quite an "interesting" problem, in the Chinese sense.

Current approach is:

  • get all points in the path(s). Since CGPath/UIBEzier don't have this functionality, the paths have to be walked using the (disgraceful) apply(:) method. This includes control points, so it's not the same thing as the end result, but hopefully it's close enough.

  • combine all points in to one array. Hooray for the wondrous flatMap()!

  • create some json from the points, load some javascript *, feed the json to a javascript method via JavaScriptCore. Fun, fun times, spend a few hours in console.log() ... Finally, get a circle in return.

  • get the bounds of the circle; use it bounds as a framing device for the path when transforming.

  • then, if it works, port the MEC JS to in Swift and hope like hell I never have to use an UnsafeMutablePointer in the process.

  • Thanks to: https://www.nayuki.io/page/smallest-enclosing-circle

[another edit, after a week's hard work]

OK, this is one tough problem. Some shapes lend themselves to centering using rects, others centering using a bounding circle. Detecting which is which is quite the trick.

It may be because of the quality of the path points I was able to get from UIBezierPath/CGPath, but the computed boundingCircle for a small percentage of shapes was too inaccurate to use. Whereas for most other shapes it was very accurate. But that discrepancy means I can't use this as a general solution; some offsets (fixups) still have to be applied to certain shapes to make them look centered. Argh.

来源:https://stackoverflow.com/questions/44239513/fitting-uibezierpath-cgpath-to-circle

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!