CAShape图层.这条线是否通过该点? [英] CAShapeLayer. Does the line pass through the point?

查看:26
本文介绍了CAShape图层.这条线是否通过该点?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用 CAShapeLayer 来在屏幕上画一条线.在方法 touchesEnded 中,我想检查线是否通过该点?".在我的代码中,当我按下屏幕的任何部分时,contains 方法总是返回 true.也许,我在 line.frame = (view?.bounds)! 中有问题.我该如何解决?抱歉我的英语不好.

override func touchesBegan(_ touches: Set, with event: UIEvent?) {让触摸 = touches.first让 firstPosition = touch?.location(in: self)如果 atPoint(firstPosition!) == lvl1 {让 firstPositionX = firstPosition?.x让 firstPositionY = frame.size.height - (firstPosition?.y)!查看?.layer.addSublayer(line)线.线宽 = 8让颜色 = #colorLiteral(红色:0.8078431487,绿色:0.02745098062,蓝色:0.3333333433,alpha:1).cgColorline.strokeColor = 颜色line.fillColor = nilline.frame = (view?.bounds)!path.move(to: CGPoint(x: firstPositionX!, y: firstPositionY))}}覆盖 func touchesMoved(_ touches: Set, with event: UIEvent?) {让触摸 = touches.first让 firstPosition = touch?.location(in: self)如果 atPoint(firstPosition!) == lvl1 {让 firstPositionX = firstPosition?.x让 firstPositionY = frame.size.height - (firstPosition?.y)!path.addLine(to: CGPoint(x: firstPositionX!, y: firstPositionY))line.path = path.cgPath}}覆盖 func touchesEnded(_ touches: Set, with event: UIEvent?) {如果 line.contains(screenCenterPoint) {打印(确定")}}

解决方案

问题

方法 func contains(_ p: CGPoint) ->CAShapeLayer 的 Bool 返回 true 如果图层的 bounds 包含该点.(

性能注意事项

由于您仅在触及结束时检查遏制,因此我认为创建路径轮廓不会增加太多开销.

当您想实时检查包含情况时,例如在touchesMoved函数内部,计算轮廓可能会产生一些开销,因为该方法被调用了很多次每秒.此外,路径越长,计算轮廓所需的时间就越长.

因此,最好实时生成最后绘制的线段的轮廓,然后检查该轮廓是否包含您的点.

如果你想认真地减少开销,你可以自己写一个包含函数.在直线上包含一个点相当简单,可以简化为以下公式:

<块引用>

给定从 startend 的一行,带有 width 和一个点 p

计算:

  • dx = start.x - end.x
  • dy = start.y - end.y
  • a = dy * p.x - dx * p.y + end.x * start.y - end.y * start.x
  • b = hypot(dy, dx)

该行包含点 p 如果:

abs(a/b) and p 在线条的边界框内.

I use CAShapeLayer in order to draw a line on the screen. In the method touchesEnded I want to check " Does the line pass through the point?". In my code when I press on the any part of the screen the method contains returns always true. Perhaps, I have problem in line.frame = (view?.bounds)!. How can I fix it? Sorry for my bad English.

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {

    let touch = touches.first
    let firstPosition = touch?.location(in: self)

    if atPoint(firstPosition!) == lvl1 {

        let firstPositionX = firstPosition?.x
        let firstPositionY = frame.size.height - (firstPosition?.y)!
        view?.layer.addSublayer(line)
        line.lineWidth = 8
        let color = #colorLiteral(red: 0.8078431487, green: 0.02745098062, blue: 0.3333333433, alpha: 1).cgColor
        line.strokeColor = color
        line.fillColor = nil
        line.frame = (view?.bounds)!
        path.move(to: CGPoint(x: firstPositionX!, y: firstPositionY))

    }

}

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {

    let touch = touches.first
    let firstPosition = touch?.location(in: self)

    if atPoint(firstPosition!) == lvl1 {

        let firstPositionX = firstPosition?.x
        let firstPositionY = frame.size.height - (firstPosition?.y)!
        path.addLine(to: CGPoint(x: firstPositionX!, y: firstPositionY))
        line.path = path.cgPath

    }
}


override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {

    if line.contains(screenCenterPoint) {
        print("ok")
    }

}

解决方案

Problem

The method func contains(_ p: CGPoint) -> Bool of CAShapeLayer returns true if the bounds of the layer contains the point. (see documentation)

So you cannot use this to check if the line contains a point.

There is however, another method with the same name in the class CGPath that returns whether the specified point is interior to the path. But since you only stroke your path and you don't fill the interior, this method will not give the desired result either.

Solution

The trick is to create an outline of your path using:

let outline = path.cgPath.copy(strokingWithWidth: line.lineWidth, lineCap: .butt, lineJoin: .round, miterLimit: 0)

And then check if the interior of the outline contains your screenCenterPoint

if outline.contains(screenCenterPoint) {
    print("ok")
}

Performance considerations

Since you are checking the containment only when touches end, I think that creating an outline of the path does not add too much overhead.

When you want to check the containment in realtime, for example inside the touchesMoved function, calculating an outline may produce some overhead because this method is called a lot of times per second. Also the longer the path becomes, the longer it will take to calculate the outline.

So in realtime it is better to generate only the outline of the last drawn segment and then check if that outline contains your point.

If you want to reduce overhead seriously, you can write your own containment function. Containment of a point in a straight line is fairly simple and can be reduced to the following formula:

Given a line from start to end with width and a point p

Calculate:

  • dx = start.x - end.x
  • dy = start.y - end.y
  • a = dy * p.x - dx * p.y + end.x * start.y - end.y * start.x
  • b = hypot(dy, dx)

The line contains point p if:

abs(a/b) < width/2 and p is in the bounding box of the line.

这篇关于CAShape图层.这条线是否通过该点?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆