Аспери был прав, и меня осенило, когда я прочитал Пол Хадсон и понял (единственное) требование Shape
- путь ( в прямоугольнике: CGRect ) - > Путь метод. Параметр rect
сообщает вам все, что вам нужно знать о локальной системе координат, а именно: ее размер.
Мой рабочий код теперь выглядит следующим образом.
Помощники
extension CGPoint {
func scaled(xFactor:CGFloat, yFactor:CGFloat) -> CGPoint {
return CGPoint(x: x * xFactor, y: y * yFactor)
}
typealias SelfMap = (CGPoint) -> CGPoint
static func scale(_ designSize: CGSize, into displaySize: CGSize) -> SelfMap {{
$0.scaled(
xFactor: displaySize.width / designSize.width,
yFactor: displaySize.height / designSize.height
)
}}
typealias Tuple = (x:CGFloat, y:CGFloat)
init(tuple t: Tuple) {
self.init(x: t.x, y: t.y)
}
}
Рисование контура в правильном контексте
// This is just the ad-hoc solution.
// You will want to parameterize the designSize and points.
let designSize = CGSize(width:800, height:800)
let opaqueBorder = [(200, 200), (600, 200), (600, 600), (200, 600)]
// To find boundary of real-life images, see Python code below.
struct Mask : Shape {
func path(in rect: CGRect) -> Path {
let points = opaqueBorder
.map(CGPoint.init(tuple:))
// *** Here we use the context *** (rect.size)
.map(CGPoint.scale(designSize, into:rect.size))
var result = Path()
result.move(to: points.first!)
result.addLines(points)
result.closeSubpath()
return result
}
}
Использование маски
struct ContentView: View {
var body: some View {
ZStack{
Image("gray") // Just so we record all touches.
.resizable()
.frame(
maxWidth : .infinity,
maxHeight: .infinity
)
.onTapGesture {
print("background")
}
// Adding mask here left as exercise.
Image("square_green")
.resizable()
.scaledToFit()
.border(Color.green, width: 4)
.offset(x: 64, y:10) // So the two Images don't overlap completely.
.onTapGesture {
print("green")
}
Image("square_orange")
.resizable()
.scaledToFit()
.border(Color.orange, width: 4)
// Sanity check shows the Mask outline.
.overlay(Mask().stroke(Color.purple))
// *** Actual working Mask ***
.contentShape(Mask())
.offset(x: -64, y:-10)
.onTapGesture {
print("orange")
}
}
}
}
Получение плана
#!/usr/bin/python3
# From https://www.reddit.com/r/Python/comments/f2kv1/question_on_tracing_an_image_in_python_with_pil
import sys
import os
os.environ['PYGAME_HIDE_SUPPORT_PROMPT'] = "hide"
import pygame
# Find simple border.
fname = sys.argv[1]
image = pygame.image.load(fname)
bitmask = pygame.mask.from_surface(image)
comp = bitmask.connected_component()
outline = comp.outline(48)
print("name: ", fname)
print("size: ", image.get_rect().size)
print("points:", outline)
# Sanity check.
# From https://www.geeksforgeeks.org/python-pil-imagedraw-draw-polygon-method
from PIL import Image, ImageDraw, ImagePath
import math
box = ImagePath.Path(outline).getbbox()
bsize = list(map(int, map(math.ceil, box[2:])))
im = Image.new("RGB", bsize, "white")
draw = ImageDraw.Draw(im)
draw.polygon(outline, fill="#e0c0ff", outline="purple")
im.show() # That this works is amazing.