Почему CGPath содержит test, иногда возвращающий true в точке вне пути? - PullRequest
0 голосов
/ 14 октября 2018

У меня есть подкласс UIView, содержащий набор графики, нарисованных с использованием CGPAth s.Мне нужно знать, когда касание попадает на один из этих путей и какой.

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    for t in touches {
        for p in myDrawnPaths {
           if(p.contains(t.location(in: self)) {
              doStuffWith(p)
           }
        }
    }
}

Иногда этот код приводит к выполнению doStuffWith() по нескольким путям, включая те, которые находятся далеко от места попадания,Я провел некоторый осмотр и обнаружил что-то действительно странное в путях, которые не должны быть затронуты:

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    for t in touches {
        for p in myDrawnPaths {
           print(p.contains(t.location(in: self))) //true
           print(p.boundingBox.contains(t.location(in: self))) //false!
        }
    }
}

А?Разве ограничивающая рамка не должна содержать весь путь, а это означает, что точка внутри пути гарантированно находится внутри ограничительной рамки?

Проблема возникает только в таких тестах - когда я использую CoreGraphics API для рисования ианимируйте с теми же CGPath с, все изображения отображаются правильно.

ОБНОВЛЕНИЕ

Я пытался воспроизвести проблему с простыми экспериментальными путями в игровых площадках, ноне смог, поэтому мне пришлось взять это из моего фактического приложения.

(75.0, 264.0) is in 
Path 0x600003643960:
  moveto (252.02, 287.067)
    lineto (259.489, 286.324)
    lineto (259.567, 286.754)
    lineto (263.438, 286.167)
    lineto (268.131, 285.503)
    lineto (268.17, 285.268)
    lineto (268.248, 284.681)
    lineto (269.069, 283.235)
    lineto (269.851, 282.57)
    lineto (269.773, 280.576)
    lineto (270.399, 279.95)
    lineto (270.829, 279.833)
    lineto (270.907, 278.425)
    lineto (271.494, 277.252)
    lineto (271.924, 277.486)
    lineto (272.002, 277.721)
    lineto (272.315, 277.799)
    lineto (273.058, 277.408)
    lineto (272.901, 273.85)
    lineto (271.65, 270.643)
    lineto (270.751, 267.085)
    lineto (269.812, 265.834)
    lineto (268.796, 265.13)
    lineto (268.17, 265.56)
    lineto (266.645, 266.264)
    lineto (265.902, 268.219)
    lineto (264.846, 269.666)
    lineto (264.416, 269.9)
    lineto (263.83, 269.666)
    curveto (263.83, 269.666) (262.813, 269.079) (262.891, 268.845)
    curveto (262.969, 268.61) (263.087, 266.889) (263.087, 266.889)
    lineto (264.416, 266.381)
    lineto (264.729, 265.052)
    lineto (264.964, 264.035)
    lineto (265.902, 263.409)
    lineto (265.785, 259.499)
    lineto (265.159, 258.6)
    lineto (264.651, 258.287)
    lineto (264.338, 257.466)
    lineto (264.651, 257.153)
    lineto (265.276, 257.27)
    lineto (265.355, 256.644)
    lineto (264.338, 255.784)
    lineto (263.83, 254.767)
    lineto (262.813, 254.767)
    lineto (261.053, 254.181)
    lineto (258.903, 252.851)
    lineto (257.847, 252.851)
    lineto (257.612, 253.086)
    lineto (257.221, 252.891)
    lineto (256.009, 251.991)
    lineto (254.875, 252.695)
    lineto (253.741, 253.594)
    lineto (253.858, 255.002)
    lineto (254.249, 255.119)
    lineto (255.07, 255.315)
    lineto (255.266, 255.628)
    lineto (254.249, 255.941)
    lineto (253.233, 256.058)
    lineto (252.646, 256.762)
    lineto (252.529, 257.583)
    lineto (252.646, 258.209)
    lineto (252.763, 260.359)
    lineto (251.356, 261.18)
    lineto (251.121, 261.102)
    lineto (251.121, 259.46)
    lineto (251.629, 258.521)
    lineto (251.864, 257.583)
    lineto (251.551, 257.27)
    lineto (250.808, 257.583)
    lineto (250.417, 259.225)
    lineto (249.361, 259.655)
    lineto (248.657, 260.398)
    lineto (248.579, 260.789)
    lineto (248.814, 261.102)
    lineto (248.579, 262.119)
    lineto (247.68, 262.314)
    lineto (247.68, 262.745)
    lineto (247.993, 263.683)
    lineto (247.563, 266.068)
    lineto (246.937, 267.632)
    lineto (247.172, 269.47)
    lineto (247.367, 269.9)
    lineto (247.054, 270.839)
    lineto (246.937, 271.152)
    lineto (246.82, 272.208)
    lineto (248.227, 274.554)
    lineto (249.361, 277.095)
    lineto (249.948, 278.972)
    lineto (249.635, 280.81)
    lineto (249.244, 283.156)
    lineto (248.306, 285.19)
    lineto (248.188, 286.246)
    lineto (246.937, 287.458)
    closepath
  moveto (233.916, 259.147)
  moveto (233.407, 258.717)
  moveto (232.703, 254.65)
  moveto (231.257, 254.142)
  moveto (230.592, 253.242)
  moveto (225.665, 252.148)
  moveto (224.57, 251.717)
  moveto (221.403, 250.857)
  moveto (218.352, 250.466)
  moveto (216.827, 248.394)
  moveto (217.101, 248.198)
  moveto (218.157, 247.885)
  moveto (219.565, 246.986)
    lineto (219.565, 246.595)
    lineto (219.799, 246.36)
    lineto (222.145, 245.969)
    lineto (223.084, 245.226)
    lineto (224.804, 244.405)
    lineto (224.883, 243.897)
    lineto (225.626, 242.763)
    lineto (226.33, 242.45)
    lineto (226.838, 241.746)
    lineto (227.737, 240.847)
    lineto (229.458, 239.908)
    lineto (231.296, 239.713)
    lineto (231.726, 240.143)
    lineto (231.608, 240.534)
    lineto (230.162, 240.925)
    lineto (229.575, 242.137)
    lineto (228.676, 242.45)
    lineto (228.48, 243.388)
    lineto (227.542, 244.64)
    lineto (227.424, 245.656)
    lineto (227.737, 245.852)
    lineto (228.128, 245.422)
    lineto (229.536, 244.288)
    lineto (230.044, 244.796)
    lineto (230.944, 244.796)
    lineto (232.195, 245.187)
    lineto (232.782, 245.617)
    lineto (233.368, 246.83)
    lineto (234.424, 247.885)
    lineto (235.949, 247.807)
    lineto (236.535, 247.416)
    lineto (237.161, 247.924)
    lineto (237.787, 248.12)
    lineto (238.295, 247.807)
    lineto (238.725, 247.807)
    lineto (239.351, 247.416)
    lineto (240.915, 246.008)
    lineto (242.245, 245.578)
    lineto (244.825, 245.461)
    lineto (246.585, 244.718)
    lineto (247.602, 244.21)
    lineto (248.188, 244.288)
    lineto (248.188, 246.517)
    lineto (248.384, 246.634)
    lineto (249.518, 246.947)
    lineto (250.261, 246.751)
    lineto (252.646, 246.126)
    lineto (253.076, 245.696)
    lineto (253.663, 245.891)
    lineto (253.663, 248.628)
    lineto (254.914, 249.84)
    lineto (255.422, 250.075)
    lineto (255.931, 250.466)
    lineto (255.422, 250.583)
    lineto (255.109, 250.466)
    lineto (253.663, 250.271)
    lineto (252.842, 250.505)
    lineto (251.942, 250.427)
    lineto (250.691, 251.014)
    lineto (249.987, 251.014)
    lineto (247.719, 250.505)
    lineto (245.686, 250.583)
    lineto (244.943, 251.6)
    lineto (242.205, 251.835)
    lineto (241.267, 252.148)
    lineto (240.837, 253.36)
    lineto (240.328, 253.79)
    lineto (240.133, 253.712)
    lineto (239.546, 253.086)
    lineto (237.787, 254.025)
    lineto (237.552, 254.025)
    lineto (237.122, 253.399)
    lineto (236.809, 253.477)
    lineto (236.066, 255.198)
    lineto (235.675, 256.762)
    lineto (234.424, 259.46)
    closepath
  moveto (222.849, 237.367)
  moveto (223.553, 236.545)
  moveto (224.413, 236.233)
  moveto (226.525, 234.708)
  moveto (227.424, 234.473)
  moveto (227.62, 234.668)
  moveto (225.626, 236.663)
  moveto (224.335, 237.406)
  moveto (223.514, 237.758)
    closepath
  moveto (257.221, 250.31)
  moveto (257.456, 251.287)
  moveto (258.707, 251.365)
  moveto (259.215, 250.896)
    curveto (259.215, 250.896) (259.176, 250.31) (259.059, 250.271)
    curveto (258.942, 250.192) (258.433, 249.528) (258.433, 249.528)
    lineto (257.573, 249.606)
    lineto (256.947, 249.684)
    lineto (256.83, 250.114)

Как мы видим, все точки пути имеют X-координату в 200 с, но точку с X-координатойиз 75 вычисляется, чтобы быть в пределах этого пути.

Проблема исчезнет, ​​если я добавлю команду close к пути, но это поднимает дополнительные вопросы:

  1. Как CGPath contains работает на открытых путях, и где, если где-либо, это задокументировано?

  2. Почему это влияет на результаты метода contains, а не на все, что яувидеть на экране?Например, если я скажу, что соответствующий CALayer должен быть заполнен определенным цветом, цвет не выливается за пределы, где он должен быть.

1 Ответ

0 голосов
/ 16 октября 2018

Поэкспериментировав с этим на игровой площадке, я обнаружил, что метод CGRect.contains(:) возвращает false для точек, у которых значение x больше или равно значение maxX или значение yy, котороебольше или равно значение maxY.

Оказывается, что документация для CGRect говорит нечто подобное:

Точка считается внутри прямоугольника, если ее координаты лежат внутри прямоугольника или на минимальном X или минимальном ребре Y.

Я бы сказал, что документация сформулирована плохо, поскольку пересечение максимального X иребра min Y не включены одинаково для пересечения ребер min X и max Y

Вот код, демонстрирующий это:

let rect = CGRect(x: 0, y: 0, width: 200, height: 200)
rect.contains(CGPoint(x: 0, y: 0))                 // true
rect.contains(CGPoint(x: rect.midX, y: 0))         // true
rect.contains(CGPoint(x: 0, y: rect.midY))         // true
rect.contains(CGPoint(x: 200, y: 200))             // false
rect.contains(CGPoint(x: 200, y: 0))               // false
rect.contains(CGPoint(x: 0, y: 200))               // false
rect.contains(CGPoint(x: 0, y: rect.maxY))         // false
rect.contains(CGPoint(x: rect.maxX, y: 0))         // false
rect.contains(CGPoint(x: rect.maxX, y: rect.maxY)) // false
rect.maxX                                          // 200
rect.maxY                                          // 200

Кроме того, документация утверждает, что boundingBox содержит все точки для пути, включая контрольные точки для кривых Безье и квадратичных кривых.Вы можете использовать boundingBoxOfPath вместо.

...