如何启发式地制作平行贝塞尔曲线

2023-12-12

我只发现这个博客有相关答案http://seant23.wordpress.com/2010/11/12/offset-bezier-curves/,但不幸的是我不懂这种语言,也无法理解其背后的数学。我需要的是知道如何使贝塞尔曲线与我现有的曲线平行。

我有一个点、段和路径类,但我不明白如何将路径分成段。 Point 类具有 CGPoint 位置公共变量, Segment 类具有 4 个点属性,Point *control1、*control2、*point2 和 *point1; Path 类包含一个 NSMutableArray 段和一个 Point startPoint。

我是 Objective C 的新手,如果不是为了我的特定类构建,至少对于更通用的方法,我真的很感激一些帮助。


我不知道您正在解决的具体问题,但一个可爱(而且非常简单)的解决方案是仅渲染贝塞尔曲线的轮廓,例如:

outline of bezier curve

使用 Core Graphics 可以轻松完成此操作(在本例中,drawRect of a UIView子类):

- (void)drawRect:(CGRect)rect {
    CGPathRef path = [self newBezierPath];
    CGPathRef outlinePath = CGPathCreateCopyByStrokingPath(path, NULL, 10, kCGLineCapButt, kCGLineJoinBevel, 0);

    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetLineWidth(context, 3.0);
    CGContextAddPath(context, outlinePath);
    CGContextSetStrokeColorWithColor(context, [[UIColor redColor] CGColor]);
    CGContextDrawPath(context, kCGPathStroke);

    CGPathRelease(path);
    CGPathRelease(outlinePath);
}

- (CGPathRef)newBezierPath {
    CGPoint point1 = CGPointMake(10.0, 50.0);
    CGPoint point2 = CGPointMake(self.bounds.size.width - 10.0, point1.y + 150.0);

    CGPoint controlPoint1 = CGPointMake(point1.x + 400.0, point1.y);
    CGPoint controlPoint2 = CGPointMake(point2.x - 400.0, point2.y);

    CGMutablePathRef path = CGPathCreateMutable();
    CGPathMoveToPoint(path, NULL, point1.x, point1.y);
    CGPathAddCurveToPoint(path, NULL, controlPoint1.x, controlPoint1.y, controlPoint2.x, controlPoint2.y, point2.x, point2.y);

    return path;
}

或者在 Swift 3 中:

override func draw(_ rect: CGRect) {
    let path = bezierPath().cgPath
    let outlinePath = path.copy(strokingWithWidth: 10, lineCap: .butt, lineJoin: .bevel, miterLimit: 0)

    let context = UIGraphicsGetCurrentContext()!
    context.setLineWidth(3)
    context.addPath(outlinePath)
    context.setStrokeColor(UIColor.red.cgColor)
    context.strokePath()
}

private func bezierPath() -> UIBezierPath {
    let point1 = CGPoint(x: 10.0, y: 50.0)
    let point2 = CGPoint(x: bounds.size.width - 10.0, y: point1.y + 150.0)

    let controlPoint1 = CGPoint(x: point1.x + 400.0, y: point1.y)
    let controlPoint2 = CGPoint(x: point2.x - 400.0, y: point2.y)

    let path = UIBezierPath()
    path.move(to: point1)
    path.addCurve(to: point2, controlPoint1: controlPoint1, controlPoint2: controlPoint2)

    return path
}

如果你真的想画一条平行路径,那就更复杂了。但是您可以渲染类似的东西(原始贝塞尔路径为红色,“平行”线为蓝色)。

enter image description here

我不完全确定您所确定的算法,但我将其呈现为

  • 自己计算贝塞尔点(红色路径),将其分解得足够精细以使效果平滑;
  • 计算每个点与下一个点之间的角度;
  • 通过获取贝塞尔路径上的点来计算偏移路径(蓝色的)的坐标,并计算垂直于我刚刚确定的角度的新点;和
  • 使用这些偏移点坐标,绘制一系列新的线段以渲染与贝塞尔曲线的平行线。

因此,在 Objective-C 中,这可能看起来像:

- (void)drawRect:(CGRect)rect {
    CGPoint point1 = CGPointMake(10.0, 50.0);
    CGPoint point2 = CGPointMake(self.bounds.size.width - 10.0, point1.y + 150.0);

    CGPoint controlPoint1 = CGPointMake(point1.x + 400.0, point1.y);
    CGPoint controlPoint2 = CGPointMake(point2.x - 400.0, point2.y);

    // draw original bezier path in red

    [[UIColor redColor] setStroke];

    [[self bezierPathFromPoint1:point1
                         point2:point2
                  controlPoint1:controlPoint1
                  controlPoint2:controlPoint2] stroke];

    // calculate and draw offset bezier curve in blue

    [[UIColor blueColor] setStroke];

    [[self offsetBezierPathBy:10.0
                       point1:point1
                       point2:point2
                controlPoint1:controlPoint1
                controlPoint2:controlPoint2] stroke];
}

- (UIBezierPath *)bezierPathFromPoint1:(CGPoint)point1
                                point2:(CGPoint)point2
                         controlPoint1:(CGPoint)controlPoint1
                         controlPoint2:(CGPoint)controlPoint2 {
    UIBezierPath *path = [UIBezierPath bezierPath];

    [path moveToPoint:point1];
    [path addCurveToPoint:point2 controlPoint1:controlPoint1 controlPoint2:controlPoint2];

    return path;
}

- (UIBezierPath *)offsetBezierPathBy:(CGFloat)offset
                              point1:(CGPoint)point1
                              point2:(CGPoint)point2
                       controlPoint1:(CGPoint)controlPoint1
                       controlPoint2:(CGPoint)controlPoint2 {
    UIBezierPath *path = [UIBezierPath bezierPath];
    static NSInteger numberOfPoints = 100;

    CGPoint previousPoint = [self cubicBezierAtTime:0.0
                                             point1:point1
                                             point2:point2
                                      controlPoint1:controlPoint1
                                      controlPoint2:controlPoint2];
    CGPoint point;
    double angle;

    for (NSInteger i = 1; i <= numberOfPoints; i++) {
        double t = (double) i / numberOfPoints;

        point = [self cubicBezierAtTime:t
                                 point1:point1
                                 point2:point2
                          controlPoint1:controlPoint1
                          controlPoint2:controlPoint2];

        // calculate the angle to the offset point
        // this is the angle between the two points, plus 90 degrees (pi / 2.0)

        angle = atan2(point.y - previousPoint.y, point.x - previousPoint.x) + M_PI_2;


        if (i == 1)
            [path moveToPoint:[self offsetPoint:previousPoint by:offset angle:angle]];

        previousPoint = point;
        [path addLineToPoint:[self offsetPoint:previousPoint by:offset angle:angle]];
    }

    return path;
}

// return point offset by particular distance and particular angle

- (CGPoint)offsetPoint:(CGPoint)point by:(CGFloat)offset angle:(double)angle {
    return CGPointMake(point.x + cos(angle) * offset, point.y + sin(angle) * offset);
}

// Manually calculate cubic bezier curve
//
// B(t) = (1-t)^3 * point1 + 3 * (1-t)^2 * t controlPoint1 + 3 * (1-t) * t^2 * pointPoint2 + t^3 * point2

- (CGPoint)cubicBezierAtTime:(double)t
                      point1:(CGPoint)point1
                      point2:(CGPoint)point2
               controlPoint1:(CGPoint)controlPoint1
               controlPoint2:(CGPoint)controlPoint2 {
    double oneMinusT = 1.0 - t;
    double oneMinusTSquared = oneMinusT * oneMinusT;
    double oneMinusTCubed = oneMinusTSquared * oneMinusT;

    double tSquared = t * t;
    double tCubed = tSquared * t;

    CGFloat x = point1.x * oneMinusTCubed +
    3.0 * oneMinusTSquared * t * controlPoint1.x +
    3.0 * oneMinusT * tSquared * controlPoint2.x +
    tCubed * point2.x;
    CGFloat y = point1.y * oneMinusTCubed +
    3.0 * oneMinusTSquared * t * controlPoint1.y +
    3.0 * oneMinusT * tSquared * controlPoint2.y +
    tCubed * point2.y;

    return CGPointMake(x, y);
}

或者,在 Swift 3 中:

override func draw(_ rect: CGRect) {
    let point1 = CGPoint(x: 10.0, y: 50.0)
    let point2 = CGPoint(x: bounds.size.width - 10.0, y: point1.y + 150.0)

    let controlPoint1 = CGPoint(x: point1.x + 400.0, y: point1.y)
    let controlPoint2 = CGPoint(x: point2.x - 400.0, y: point2.y)

    UIColor.red.setStroke()
    bezierPath(from: point1, to: point2, withControl: controlPoint1, and: controlPoint2).stroke()

    UIColor.blue.setStroke()
    offSetBezierPath(by: 5, from: point1, to: point2, withControl: controlPoint1, and: controlPoint2).stroke()
}

private func bezierPath(from point1: CGPoint, to point2: CGPoint, withControl controlPoint1: CGPoint, and controlPoint2:CGPoint) -> UIBezierPath {
    let path = UIBezierPath()

    path.move(to: point1)
    path.addCurve(to: point2, controlPoint1: controlPoint1, controlPoint2: controlPoint2)

    return path
}

private func offSetBezierPath(by offset: CGFloat, from point1: CGPoint, to point2: CGPoint, withControl controlPoint1: CGPoint, and controlPoint2:CGPoint) -> UIBezierPath {
    let path = UIBezierPath()

    let numberOfPoints = 100

    var previousPoint = cubicBezier(at: 0, point1: point1, point2: point2, controlPoint1: controlPoint1, controlPoint2: controlPoint2)

    for i in 1 ... numberOfPoints {
        let time = CGFloat(i) / CGFloat(numberOfPoints)
        let point = cubicBezier(at: time, point1: point1, point2: point2, controlPoint1: controlPoint1, controlPoint2: controlPoint2)

        // calculate the angle to the offset point
        // this is the angle between the two points, plus 90 degrees (pi / 2.0)

        let angle = atan2(point.y - previousPoint.y, point.x - previousPoint.x) + .pi / 2;

        if i == 1 {
            path.move(to: calculateOffset(of: previousPoint, by: offset, angle: angle))
        }

        previousPoint = point
        path.addLine(to: calculateOffset(of: previousPoint, by: offset, angle: angle))
    }

    return path
}

/// Return point offset by particular distance and particular angle
///
/// - Parameters:
///   - point: Point to offset.
///   - offset: How much to offset by.
///   - angle: At what angle.
///
/// - Returns: New `CGPoint`.

private func calculateOffset(of point: CGPoint, by offset: CGFloat, angle: CGFloat) -> CGPoint {
    return CGPoint(x: point.x + cos(angle) * offset, y: point.y + sin(angle) * offset)
}

/// Manually calculate cubic bezier curve
///
/// B(t) = (1-t)^3 * point1 + 3 * (1-t)^2 * t controlPoint1 + 3 * (1-t) * t^2 * pointPoint2 + t^3 * point2
///
/// - Parameters:
///   - time:          Time, a value between zero and one.
///   - point1:        Starting point.
///   - point2:        Ending point.
///   - controlPoint1: First control point.
///   - controlPoint2: Second control point.
///
/// - Returns: Point on bezier curve.

private func cubicBezier(at time: CGFloat, point1: CGPoint, point2: CGPoint, controlPoint1: CGPoint, controlPoint2: CGPoint) -> CGPoint {
    let oneMinusT = 1.0 - time
    let oneMinusTSquared = oneMinusT * oneMinusT
    let oneMinusTCubed = oneMinusTSquared * oneMinusT

    let tSquared = time * time
    let tCubed = tSquared * time

    var x = point1.x * oneMinusTCubed
    x += 3.0 * oneMinusTSquared * time * controlPoint1.x
    x += 3.0 * oneMinusT * tSquared * controlPoint2.x
    x += tCubed * point2.x

    var y = point1.y * oneMinusTCubed
    y += 3.0 * oneMinusTSquared * time * controlPoint1.y
    y += 3.0 * oneMinusT * tSquared * controlPoint2.y
    y += tCubed * point2.y

    return CGPoint(x: x, y: y)
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何启发式地制作平行贝塞尔曲线 的相关文章

  • Firebase 连接管理器应仅返回一个结果

    我正在关注位于以下位置的文档 https www firebase com docs ios guide offline capability html section connection state https www firebase
  • 使用 Push Transition 效果更改 RootViewcontroller

    在我的iOS应用程序中 我需要更改应用程序之间窗口的rootviewController 因此 当我动态更改我的rootviewcontroller时 它会在更改之前轻拂视图 但我想要的是在更改rootviewcontroller时提供平滑
  • 如何在 RestKit 中为同一类提供两条发布路线

    由于我无法弄清楚如何为同一个类设置两个不同的 POST 资源路径 因此我尝试手动创建 RKObjectLoader 请求 但它似乎不断发送 GET 请求而不是 POST 即使我已将方法设置为邮政 这是我的代码 User user User
  • 使用 UIImagePickerController 的应用程序在拍照后选择“使用照片”时冻结

    我现在正在开发一个简单的照片和视频捕获应用程序 该应用程序成功地允许用户按下按钮即可拍摄照片或视频 但是 一旦您完成拍摄照片或视频 它就会提供 2 个选项 重新拍摄 和 使用照片 或 使用视频 具体取决于您使用的选项 如果用户点击 重新拍摄
  • 如何在我的 iOS 项目中添加和执行 .sql 文件?

    我找到了很多关于在 iOS 中使用 SQLite 数据库的教程 但没有找到任何直接引用 sql 文件的内容 谁能告诉我如何将现有的 SQL 数据库链接到我的应用程序 编辑 这是一个 MySQL 转储 我们有一个基于浏览器的抽认卡程序 现在我
  • 对 UIImage 进行方形裁剪,导致图像拉伸

    当尝试执行 UIImage 的中心裁剪时 我得到以下结果 左侧是原始图像640 1136 右边是适合正方形的裁剪图像UIImageView at 320 320 turns to 我对比率元素进行了相当多的修改 以便它可以正确检测要修剪的量
  • iOS:从非图像数据生成图像(Godus,如风景)

    所以看到图像后Godus http www kickstarter com projects 22cans project godus我想知道如何生成简单的 非交互式的 2D 图像 with 不同高度或层数的颜色不同就像下面的图片一样 我只
  • 在 Interface Builder 中的资产目录上使用图像

    是否可以直接在界面生成器上使用添加到资产目录中的图像 这是怎么做到的 在 UIImageView 属性上 我看不到任何引用资产目录上任何图像的选项 Import the images into the xcassets folder 单击右
  • 切换到工作区并在 Xcode 中添加 CocoaPods 后提交 git 吗?

    我刚刚在 Xcode 5 中将 CocoaPods 添加到我当前的项目中 当然 CocoaPods 创建了一个工作区 并且我已在 Xcode 中启动了该工作区 我在工作区中看到了我的项目和 Pods 项目 我的项目从第一天起就处于源代码控制
  • 更改 UITextField 辅助功能描述

    有没有办法将 UITextField 的辅助功能标签设置为 文本字段 之外的其他内容 因此 我不想将其称为 文本字段 而是将其命名为 代码验证字段 我的建议是不要试图在内置语音输出上智取系统 对于盲人用户来说 文本字段正在编辑 相当于 该项
  • iPhone 标签栏上的未读计数

    在 Cocoa Touch 上实现 TabBar 上图标的未读计数的最佳方法是什么 我想模仿 SMS 或邮件应用程序的行为 向我的应用程序的用户显示未读消息计数 并使用包含数字的红点 您正在寻找的属性称为徽章 您可以通过执行以下操作来设置它
  • 在WKWebview中设置useragent

    如何在 WKWebView 中设置自定义用户代理字符串 我正在尝试嵌入我的应用程序的版本 以便我的服务器端可以看到可用的功能 我找到了以下方法 let userAgent MyApp 1 33 7 request setValue user
  • 在 Swift 中检查一个数组是否包含另一个数组的所有元素

    我想为数组编写一个扩展来检查一个数组是否包含另一个数组的所有元素 在我的用例中它是字符串对象 但我一直得到 Cannot convert value of type T Generator Element to expected argum
  • 通过 iPhone 连接到 AT&T 无线网络的谷歌静态地图无法工作 [关闭]

    Closed 这个问题是无关 help closed questions 目前不接受答案 最近 当我的 iPhone 连接到 AT T 无线网络时 我在检索静态地图时遇到了问题 但是 当我连接到本地 WiFi 时工作正常 上周日停止工作了几
  • 具有多种自定义单元格类型的 RxSwift 表视图

    我想知道是否有任何代码示例RxSwift当我可以在一个表视图中使用多个自定义单元格时 例如 我有两个部分 第一部分有 10 个单元格 类型为CellWithImage标识符和第二部分有 10 个带有类型的单元格CellWithVideo标识
  • 增加 NSData 的长度

    基本上 我有一个 46 个字符的 NSString 我将其转换为 NSData 我需要将字符串填充到 48 个字符 仅在 NSString 末尾添加 是行不通的 所以 我只是使用以下方法增加了 NSData 的长度 NSString str
  • Swift 中 UIImages 的淡入淡出动画

    我有一组图像 我希望它们在登录屏幕的背景中淡出 我无法在 swift 中找到任何可以做到这一点的东西 有什么办法我可以做到吗 这是我当前的代码 override func viewDidLoad super viewDidLoad star
  • 播放(非库)Apple Music 内容 - 请求失败

    我正在尝试使用以下代码播放专辑 let predicate MPMediaPropertyPredicate value 1459938538 forProperty MPMediaItemPropertyAlbumPersistentID
  • 如何在内存中存储分子?

    我想将分子存储在内存中 这些可以是简单的分子 Methane CH4 C H bond length 108 7 pm H H angle 109 degrees But also more complex molecules like p
  • 当设置 setVisibleXRangeMaximum 时,iOS-Charts X 轴值无限重复

    我正在尝试绘制一个图表 其中 x 轴是TimeIntervalY 轴是power consumption 由于每天都会有数据 因此将有太多数据无法显示 因此 我想一次显示 5 个值 我通过设置实现了这一点self chart setVisi

随机推荐

  • 从 Makefile 运行 go 时权限被拒绝

    我遇到了一些奇怪的权限被拒绝的错误 我不知道这些错误可能来自哪里 go run Hello from go make run go run make go Permission denied make Makefile 2 run Erro
  • 迁移持久存储崩溃

    我有一个开关 可以为 iOS 7 应用程序打开或关闭 iCloud iCloud 同步工作正常 当 iCloud 打开并且我将其关闭时 我调用以下代码 void migrateiCloudStoreToLocalStore NSError
  • 未知属性 Android:Android STUDIO 3.2.1 上出现错误

    我为解决这个问题所做的事情 清洁构建和重建 已删除 idea 文件 无效的缓存 重新启动 选项 打开省电模式 SDK 是最新的 同步项目与 Gradle 文件已完成 重新安装Android Studio完成 重新创建新项目完成 更改了应用程
  • 禁用表中的控件 - JQuery/Javascript

    我有一个复杂的 UI 其中包含多个嵌套表 一个中继器控件和几个其他 HTML 控件 这些控件的禁用属性基于 JQuery 中的业务逻辑进行设置 我需要一种方法来禁用整个表格 包括嵌套控件 UI 元素等 并在用户切换按钮时重新启用它们 我可以
  • 使用 IComparer 进行排序

    我正在尝试使用IComparer对点列表进行排序 这是 IComparer 类 public class CoordinatesBasedComparer IComparer public int Compare Object q Obje
  • Eloquent 中的 IN 语句

    我正在尝试在 Eloquent 查询中执行 IN 语句 我试过这个 id urldecode id news News whereHas newsCategories function q use id q gt where categor
  • Groovy 中的属性注释内省

    有没有一种方便的方法来迭代对象的属性并检查每个属性的注释 你可以这样做 First declare your annotation import java lang annotation Retention RetentionPolicy
  • IIS 反向代理到 node.js

    我正在尝试配置反向代理 以便从 IIS 中托管的网站向 node js 应用程序发出的 HTTP 调用可以正常工作 我会更好地解释它 我租了一个VPS 在此 VPS 中 我有一个 IIS 网站 简单的 HTMl Javascript 和一个
  • 从特定字符开始截断 NSString 的一部分

    我有一根绳子 NSString myOldString This is a string and this part is between brackets 现在 我想以这样的方式截断字符串 基本上括号之间的所有内容 包括括号 都被截断 更
  • Spring Boot:将另一个端口上的请求发送到自定义 Servlet

    我希望我的 spring boot 应用程序侦听第二个端口 其中 第一个 端口是用于 spring webmvc 端点的 server port 并将进入第二个端口上的 的所有流量定向到 Servlet 的实现我已经写了 这些请求将是 js
  • 当选择 p/h:selectOneMenu 中的空 f:selectItem 时,有条件地跳过验证

    父母
  • My.Settings 引用库使用的 app.config 文件的位置?

    如果我有一个类库app config文件 我知道这并不理想 请耐心等待一下 其中包含使用项目 设置 选项卡创建的设置值 并按如下方式访问 Public Shared Function GetMySetting key As String A
  • 我可以使用计时器每 x 毫秒更新一次标签吗

    这是我的代码 Stopwatch timer new Stopwatch timer Start while timer ElapsedMilliseconds lt 3000 label1 Text Convert ToString ti
  • 如何在 conda 环境中安装已弃用/不受支持的 Python 3.4?

    自 Python 3 4 弃用以来 conda 已将其从包列表中删除 但是 有什么方法可以安装它吗 我需要它才能使用用旧版本编写的软件 编辑 我的问题与建议重复一份 因为我指的是已弃用和不受支持的版本 我已经知道如何使用特定的 python
  • 同时交换两个片段

    在我的应用程序中 我有一个活动 并且在运行时添加两个片段 我需要同时交换这两个片段 片段 1 包含一个按钮 我希望当我单击该按钮时 片段 1 移动到屏幕的右侧 而其他片段移动到活动的左侧 在按钮的 onClick 方法中我尝试了这样的方法
  • 使用 ggplot2 绘制彩色直方图

    我有一个包含值及其相关权重的数据框 我想制作一个直方图 使得每个条形的高度对应于该箱中的值的数量 并且条形的颜色对应于它们的总重量 我怎么做 Example D lt data frame x c 0 39 0 12 0 94 1 67 1
  • ImportError:libnvidia-fatbinaryloader.so.375.39:无法打开共享对象文件:没有这样的文件或目录

    我使用的是 Ubuntu 16 04 Cuda 8 0 和 cudann v5 1 我卸载了 Tensorflow CPU 版本并重新安装了启用的 Tensorflow GPU 按照此处给出的说明进行操作 https alliseesolu
  • iOS从通知中心删除通知[重复]

    这个问题在这里已经有答案了 可能的重复 iOS应用程序 如何清除通知 当我单击它并启动我的应用程序时 有没有办法从通知中心 iOS 5 中删除通知 多谢 Set the applicationIconBadgeNumber你的财产UIApp
  • 使用 typeid 检查模板类型

    我想知道执行以下操作是否安全 template
  • 如何启发式地制作平行贝塞尔曲线

    我只发现这个博客有相关答案http seant23 wordpress com 2010 11 12 offset bezier curves 但不幸的是我不懂这种语言 也无法理解其背后的数学 我需要的是知道如何使贝塞尔曲线与我现有的曲线平