Beispiel für Freihandlöschung

Diese Anwendung baut auf dem Beispiel für die Freihandsammlung auf, indem das Löschen von Freihandstrichen veranschaulicht wird. Das Beispiel bietet dem Benutzer ein Menü, das vier Modi zur Auswahl hat: Freihand aktiviert, Löschvorgänge an Cusp, Löschen an Schnittpunkten und Löschen von Strichen.

Im Freihandmodus sammelt das InkCollector-Objekt Freihand, wie im Beispiel für die Freihandsammlung gezeigt.

In einem Löschmodus werden Segmente vorhandener Freihandstriche, die der Benutzer mit dem Cursor berührt, gelöscht. Außerdem können die Cusps oder Schnittpunkte mit einem roten Kreis markiert werden.

Die interessantesten Teile dieses Beispiels liegen im InkErase Ereignishandler des Formulars OnPaint und in den Löschfunktionen, die aus dem Ereignishandler des Formulars OnMouseMove aufgerufen werden.

Kreisen der Cusps und Schnittpunkte

Der Ereignishandler des OnPaint Formulars zeichnet zuerst die Striche und kann je nach Anwendungsmodus alle Cusps oder Schnittpunkte finden und mit einem kleinen roten Kreis markieren. Ein Cusp markiert den Punkt, an dem ein Strich abrupt die Richtung ändert. Eine Schnittmenge markiert einen Punkt, an dem sich ein Strich mit sich selbst oder einem anderen Strich überschneidet.

Das Paint-Ereignis tritt immer dann auf, wenn ein Steuerelement neu gezeichnet wird.

Hinweis

Das Beispiel zwingt das Formular, sich mit der Refresh-Methode des Formulars neu zu zeichnen, wenn ein Strich gelöscht wird oder sich der Anwendungsmodus ändert.

 

private void InkErase_OnPaint(object sender, PaintEventArgs e)
{
    Strokes strokesToPaint = myInkCollector.Ink.Strokes;

    myInkCollector.Renderer.Draw(e.Graphics, strokesToPaint);

    switch (mode)
    {
        case ApplicationMode.CuspErase:
            PaintCusps(e.Graphics, strokesToPaint);
            break;
        case ApplicationMode.IntersectErase:
            PaintIntersections(e.Graphics, strokesToPaint);
            break;
    }
}

In PaintCuspsdurchläuft der Code jeden Cusp in jedem Strich und zeichnet einen roten Kreis um ihn herum. Die PolylineCusps-Eigenschaft des Strichs gibt die Indizes der Punkte in einem Stoke zurück, die cusps entsprechen. Beachten Sie auch die InkSpaceToPixel-Methode des Renderer-Objekts, die den Punkt in Koordinaten konvertiert, die für die DrawEllipse-Methode relevant sind.

private void PaintCusps(Graphics g, Strokes strokesToPaint)
{
    foreach (Stroke currentStroke in strokesToPaint)
    {
        int[] cusps = currentStroke.PolylineCusps;

        foreach (int i in cusps)
        {
            Point pt = currentStroke.GetPoint(i);

            // Convert the X, Y position to Window based pixel coordinates
            myInkCollector.Renderer.InkSpaceToPixel(g, ref pt);

            // Draw a red circle as the cusp position
            g.DrawEllipse(Pens.Red, pt.X-3, pt.Y-3, 6, 6);
        }
    }
}

In PaintIntersectionsdurchläuft der Code jeden Strich, um seine Schnittpunkte mit dem gesamten Satz von Strichen zu finden. Beachten Sie, dass die FindIntersections-Methode der Striche eine Strokes-Auflistung übergeben wird und ein Array von Gleitkommaindexwerten zurückgibt, die die Schnittpunkte darstellen. Der Code berechnet dann eine X-Y-Koordinate für jede Schnittmenge und zeichnet einen roten Kreis um sie herum.

private void PaintIntersections(Graphics g, Strokes strokesToPaint)
{
    foreach (Stroke currentStroke in strokesToPaint)
    {
        float[] intersections =            currentStroke.FindIntersections(strokesToPaint);
    }
}

Behandeln eines Stifts mit zwei Enden

Drei Ereignishandler sind für das InkCollector-Objekt für die Ereignisse CursorDown, NewPackets und Stroke definiert. Jeder Ereignishandler überprüft die Inverted-Eigenschaft des Cursor-Objekts, um festzustellen, welches Ende des Stifts verwendet wird. Wenn der Stift invertiert ist:

  • Die myInkCollector_CursorDown -Methode macht den Strich transparent.
  • Die myInkCollector_NewPackets -Methode löscht Striche.
  • Die myInkCollector_Stroke -Methode bricht das Ereignis ab. NewPackets-Ereignisse werden vor dem Stroke-Ereignis generiert.

Verfolgen des Cursors

Unabhängig davon, ob der Benutzer einen Stift oder eine Maus verwendet, werden MouseMove-Ereignisse generiert. Der MouseMove-Ereignishandler überprüft zunächst, ob der aktuelle Modus ein Löschmodus ist und ob eine Maustaste gedrückt wird, und ignoriert das Ereignis, wenn diese Zustände nicht vorhanden sind. Anschließend konvertiert der Ereignishandler die Pixelkoordinaten für den Cursor mithilfe der PixelToInkSpace-Methode des Renderer-Objekts in Freihandraumkoordinaten und ruft je nach aktuellem Löschmodus eine der Löschmethoden des Codes auf.

Striche löschen

Die EraseStrokes -Methode übernimmt die Position des Cursors im Freihandraum und generiert eine Auflistung von Strichen, die sich innerhalb HitTestRadius von Einheiten befinden. Der currentStroke Parameter gibt ein Stroke-Objekt an, das nicht gelöscht werden soll. Anschließend wird die Strichauflistung aus dem Collector gelöscht, und das Formular wird neu gezeichnet.

private void EraseStrokes(Point pt, Stroke currentStroke)
{
    Strokes strokesHit = myInkCollector.Ink.HitTest(pt, HitTestRadius);

    if (null!=currentStroke && strokesHit.Contains(currentStroke))
    {
        strokesHit.Remove(currentStroke);
    }

    myInkCollector.Ink.DeleteStrokes(strokesHit);

    if (strokesHit.Count > 0)
    {
        this.Refresh();
    }
}

Löschen an Kreuzungen

Die EraseAtIntersections Methode durchläuft jeden Strich, der in den Testradius fällt, und generiert ein Array von Schnittpunkten zwischen diesem Strich und allen anderen Strichen in der Auflistung. Wenn keine Schnittpunkte gefunden werden, wird der gesamte Strich gelöscht. andernfalls befindet sich der nächstgelegene Punkt auf dem Strich zum Prüfpunkt, und daraus werden die Schnittpunkte auf beiden Seiten des Punkts lokalisiert, die das zu entfernende Segment beschreiben.

Die Split-Methode des Stroke-Objekts wird verwendet, um das Segment vom Rest des Strichs zu trennen, und dann wird das Segment gelöscht, sodass der Rest des Strichs intakt bleibt. Wie in EraseStrokeswird das Formular neu gezeichnet, bevor die Methode zurückgibt.

private void EraseAtIntersections(Point pt)
{
    Strokes strokesHit = myInkCollector.Ink.HitTest(pt, HitTestRadius);

    foreach (Stroke currentStroke in strokesHit)
    {
        float[] intersections = currentStroke.FindIntersections(myInkCollector.Ink.Strokes);
        ...
        float findex = currentStroke.NearestPoint(pt);
        ...
        strokeToDelete = currentStroke.Split(intersections[i]);
        ...
    }
    ...
}

Löschen bei Cusps

Für jeden Strich, der in den Testradius fällt, ruft die EraseAtCusps Methode das Array von Cusps aus der PolylineCusps-Methode des Stroke-Objekts ab. Jedes Ende des Strichs ist auch ein Cusp. Wenn der Strich also nur zwei Cusps hat, wird der gesamte Strich gelöscht; andernfalls befindet sich der nächstgelegene Punkt auf dem Strich zum Prüfpunkt, und daraus werden die Schnittpunkte auf beiden Seiten des Punkts lokalisiert, die das zu entfernende Segment beschreiben.

Die Split-Methode des Stroke-Objekts wird verwendet, um das Segment vom Rest des Strichs zu trennen, und dann wird das Segment gelöscht, sodass der Rest des Strichs intakt bleibt. Wie in EraseStrokeswird das Formular neu gezeichnet, bevor die Methode zurückgibt.

private void EraseAtCusps(Point pt)
{
    ...
    strokesHit = myInkCollector.Ink.HitTest(pt, HitTestRadius);
    
    foreach (Stroke currentStroke in strokesHit)
    {
        int[] cusps = currentStroke.PolylineCusps;
        ...
        float findex = currentStroke.NearestPoint(pt);
        ...
        strokeToDelete = currentStroke.Split(cusps[i]); 
        myInkCollector.Ink.DeleteStroke(strokeToDelete);
        ...
    }
    ...
}

Schließen des Formulars

Die Dispose-Methode des Formulars veräußert das InkCollector-Objekt. myInkCollector