WWDC Quick Look 💓 By SwiftGGTeam
Inspect, modify, and construct PencilKit drawings

Inspect, modify, and construct PencilKit drawings

元の動画を見る

ハイライト

iOS 14 合格PKStrokePKStrokePathPKStrokePointそしてPKInk開けるPKDrawing内部データを使用すると、開発者はストロークごとに PencilKit 描画を検査、補間、識別、トリミングし、プログラム的に構築することができます。

主要内容

iOS 14 より前は、多くのアプリが非常に簡単な方法で PencilKit に接続していました。PKCanvasView、システムに Apple Pencil の低遅延描画を処理させてから、PKDrawing保存してください。このプロセスは、ホワイトボード、注釈、および製図に適しています。問題は次のステップにあります。アプリがユーザーが何を描いたかを知りたいとき、ストロークごとに再生したいとき、手書きの一部を文字に分割したいとき、認識結果に基づいて描画を変更したいとき、PKDrawing構造が十分に透明ではありません。

このセッションでは、書道の練習アプリを使用して、新しい能力の価値を説明します。このアプリはまず小文字のグループを単一文字の PencilKit 描画に分割し、次にユーザーの入力に従ってカリグラフィーの練習テンプレートを合成します。練習中は、赤い点が次のストロークの軌道に沿って移動します。ユーザーがテンプレートの近くに書き込みをすると、テンプレートが緑色に変わり、書き込みがオフになると、再び開始されます。このプロセスでは、プログラムによる描画の構築、時間に沿ったストローク アニメーション、ユーザー ストロークとテンプレート間の類似性のマッチングという 3 つのことを同時に使用します。

iOS14PKDrawingストローク、インク、パス、ポイントに解凍されます。各ストロークはユーザーが描いた線を表し、その順序はユーザーの書き込みプロセスを保持します。ストロークのパスは、一連の立方体均一 B スプライン制御点です。ポイントは、位置、サイズ、角度、透明度、圧力、傾斜、時間オフセットを記録します。マスクは、ピクセル消しゴムが切り取られた後の可視範囲を表します。

これにより、PencilKit がキャンバス コントロールからデータ レイヤーに取り込まれます。開発者は、次を使用して、図面からストロークを削除し、距離または時間によってパスをサンプリングし、消去後に表示されているフラグメントを処理できます。PKDrawingレンダリング機能によりイメージが生成され、既存のストロークから新しい描画を結合することもできます。

詳細

からPKDrawing.strokes新しい図面を取り出します

(03:37) スピーチでは最初に花の絵が示されました。分解したらそれぞれPKStrokeそれらはすべてユーザーが描いた線に対応しており、その順序が描画順序になります。習字練習デモではこれを利用し、小文字を含む絵を複数の文字に切り出し、それらの文字を使ってテンプレートを合成します。

import PencilKit

func letterDrawing(from alphabetDrawing: PKDrawing,
                   strokes range: Range<Int>) -> PKDrawing {
    let selectedStrokes = alphabetDrawing.strokes[range]
    return PKDrawing(strokes: selectedStrokes)
}

キーポイント:

  • alphabetDrawing.strokes図面で構成されていますPKStroke配列。 -Range<Int>素材図面内の特定の文字が占めるストローク間隔に対応します。 -PKDrawing(strokes:)グループを受け取るPKStrokeを使用すると、新しい図面を直接作成できます。
  • これは、トランスクリプトの「最初にストロークを取得し、次に文字ごとにスライスし、最後に文字ごとに新しい描画を作成する」というプロセスと一致しています。

ストロークはパス、インク、トランスフォーム、マスクで構成されます

04:57PKStrokeその核となるのはパスです。パスは形状を決定し、インクは色とブラシの種類を決定し、変換は位置と方向を決定し、マスクはピクセル消しゴムによって切り取られる領域を表します。renderBoundsパス、インク、トランスフォーム、マスクの効果が考慮されるため、ヒット検出や部分リフレッシュに適しています。

import UIKit
import PencilKit

func strokeSummary(_ stroke: PKStroke) -> (ink: PKInk,
                                           bounds: CGRect,
                                           hasMask: Bool) {
    return (
        ink: stroke.ink,
        bounds: stroke.renderBounds,
        hasMask: stroke.mask != nil
    )
}

キーポイント:

  • stroke.inkこの行で使用されるインクを提供します。 -stroke.renderBoundsストロークの幅、変換、マスクの効果を含む、レンダリングされた外接する四角形です。 -stroke.maskストロークがマスクによってクリップされた場合にのみ存在します。
  • パスの制御点自体は実際の曲線上にないため、ここでは読み込まれません。

補間点を使用して実際の曲線を読み取る

06:11PKStrokePathは 3 次均一 B スプラインです。パスにはコントロール ポイントが格納されます。制御点を直接移動すると、ストローク カーブ上にない一連の点が生成されます。曲線上の点を取得するには、次を使用する必要があります。interpolatedPoints(in:by:)、ストライドは距離、時間、またはパラメーターのステップ サイズによって設定できます。

import UIKit
import PencilKit

func sampleLocations(from stroke: PKStroke,
                     every distance: CGFloat = 50) -> [CGPoint] {
    return stroke.path
        .interpolatedPoints(by: .distance(distance))
        .map(\.location)
}

キーポイント:

  • stroke.pathはいPKStrokePath、ストロークの B スプライン パスを記述します。 -.distance(distance)図面座標系の点の数に応じたサンプリングを示します。 -interpolatedPoints返されるものは曲線上にありますPKStrokePoint
  • locationこれは、後続の描画、識別、またはデバッグで一般的に使用される位置データです。

パスアニメーションを時間ごとに進める

(08:54) パラメーター値は浮動小数点数であるため、2.4 と 4.8 などの制御ポイントの間の位置を取ることができます。このスピーチでは、この機能を使用して、書道アプリの赤い点のアニメーションを説明しています。各フレームは、前のフレームから現在のフレームまでの時間差を取得し、同じ時間だけパスに沿って現在のパラメータ値を進めます。すると、赤い点は元の筆記速度で移動します。

import UIKit
import PencilKit

func nextMarkerPoint(on stroke: PKStroke,
                     currentValue: CGFloat,
                     deltaTime: TimeInterval) -> (value: CGFloat, location: CGPoint) {
    let nextValue = stroke.path.parametricValue(currentValue,
                                                offsetBy: .time(deltaTime))
    let point = stroke.path.interpolatedPoint(at: nextValue)

    return (value: nextValue, location: point.location)
}

キーポイント:

  • parametricValue(_:offsetBy:)時間、距離、またはパラメーター ステップに基づいてパス上の位置を進めます。 -.time(deltaTime)アニメーション ループに適した実際のフレーム間隔を使用します。 -interpolatedPoint(at:)新しいパラメータ値を使用して、PKStrokePoint
  • 新しいパラメータ値を返し、ここから次のフレームが進み続けます。

消しゴムで切り取られたストロークを処理する

(11:54) ピクセル消しゴムはストロークをマスクします。マスクは穴を掘ったり、ストロークを複数のセグメントに分割したりする場合があります。パス自体にはマスク情報がないため、パス全体をサンプリングすると消去された部分も含まれます。このタイプの脳卒中に対処するときは、次のようにする必要があります。maskedPathRanges見える範囲を制限します。

import UIKit
import PencilKit

func visibleLocationsInMaskedStroke(_ stroke: PKStroke,
                                    every distance: CGFloat = 12) -> [CGPoint] {
    return stroke.maskedPathRanges.flatMap { range in
        stroke.path
            .interpolatedPoints(in: range, by: .distance(distance))
            .map(\.location)
    }
}

キーポイント:

  • maskedPathRangesはいClosedRange<CGFloat>パスのパラメータ値を使用した配列、範囲。
  • 各範囲は、マスクのクリッピング後も表示されるパスのセグメントを表します。
  • 範囲は 0 になる可能性があり、残りの可視フラグメントがパス スプラインと交差しないことを示します。
  • 認識アルゴリズムは、消去された手書き文字が結果にカウントされることを避けるために、これらの目に見える間隔のサンプリングに基づいている必要があります。

識別とレンダリングには 2 つの入り口があります。

(14:09) この音声により、認識が 2 つの方向に分かれました。スプラインベースの認識が使用可能maskedPathRanges、補間点、時間、圧力、その他の情報。画像ベースの認識の用途PKDrawingレンダリング API は画像を生成します。

import UIKit
import PencilKit

func renderedImage(from drawing: PKDrawing, scale: CGFloat) -> UIImage {
    return drawing.image(from: drawing.bounds, scale: scale)
}

キーポイント:

  • drawing.boundsは、描画の内容を含む四角形です。 -image(from:scale:)指定した領域がレンダリングされます。UIImage
  • このパスは画像認識やサムネイル生成に適しています。
  • ストロークの形状を比較したい場合は、時間、圧力、マスクの情報が保持されているため、ストローク パスとポイント データを優先する必要があります。

重要ポイント

1. 書道評価アプリ

機能: 標準の文字または漢字を PencilKit 描画として保存し、ユーザーが書いているときにリアルタイムでストローク パスを比較します。

実行する価値がある理由: セッション デモでは、テンプレート合成、赤いドット アニメーション、類似性スコアリングの完全なリンクが示されています。

開始方法: 最初に使用しますPKDrawing.strokesマテリアルグリフを文字に切り取って使用しますPKDrawing(strokes:)テンプレートを組み立てます。ユーザーが書き込んだ後、使用しますinterpolatedPoints(in:by:)テンプレートとユーザー ストロークをサンプリングし、パスの距離と時間の情報を比較します。

2. ノートの再生と指導の注釈

何をすべきか: 数学的導出、スケッチの説明、またはスコアの注釈の各ストロークを、ユーザーの元の筆記速度で再生します。

実行する価値がある理由:PKStrokePoint.timeOffsetそしてparametricValue(_:offsetBy:)書き込みリズムを維持することができ、再生中に各パスの速度を推測する必要はありません。

開始方法: 各フレームで使用される現在のストロークのパラメータ値を保存します.time(deltaTime)前に押して、もう一度使用しますinterpolatedPoint(at:)カーソルまたはハイライト位置を更新します。

3. スマート消しゴム後の認識

内容: ユーザーがメモの一部を消去した後、認識アルゴリズムは残りの目に見える手書き文字のみを処理します。

実行する価値がある理由: セッションでは、ピクセル消しゴムがマスクを生成し、完全なパスを直接読み取ると消去されたパスが含まれることが明確に述べられています。

開始方法: マスクを使用してストロークをトラバースしますmaskedPathRanges、これらの範囲内の補間値のみをサンプリングします。範囲が空の場合は、このストロークをスキップします。

4. 手書きコンテンツのサムネイルとインデックス

機能: 手書きページのサムネイルを生成し、ストロークのサンプリング結果を独自の認識インデックスまたは検索インデックスにフィードします。

実行する価値がある理由: セッションには 2 つの認識入口があります: 画像認識の使用PKDrawingレンダリング結果、ストローク、点、時間、圧力を使用したスプライン認識。

開始方法: を使用しますdrawing.image(from:scale:)ページのプレビューを生成します。より詳細な検索が必要な場合は、ストロークごとにサンプリングしますlocationforceそしてtimeOffset、結果を認識プロセスに渡します。

関連セッション

  • PencilKit の新機能 - iOS 14 PencilKit は入口を更新し、PKCanvasView、PKToolPicker、drawingPolicy、およびストローク アクセス入口をカバーします。
  • iPad 用 Scribble の紹介 — Scribble が Apple Pencil の手書き入力を標準およびカスタムのテキスト コントロールに統合する方法について説明します。
  • PencilKit の紹介 — WWDC19 での PencilKit の紹介セッション。PKCanvasView、PKToolPicker を紹介し、低遅延の Apple Pencil 描画をアプリに追加します。

コメント

GitHub Issues · utterances