墨迹擦除示例

此应用程序通过演示墨迹笔划删除,基于墨迹 集合示例 示例构建。 该示例为用户提供了一个菜单,该菜单有四种模式可供选择:启用墨迹、在尖处擦除、在交叉处擦除和擦除笔划。

在启用墨迹的模式下, InkCollector 对象收集墨迹,如 墨迹收集示例中所示。

在擦除模式下,用户用光标触摸的现有墨迹笔划段将被清除。 此外,提示或交集可能标有一个红色圆圈。

此示例最有趣的部分位于 InkErase 窗体的 OnPaint 事件处理程序和从窗体的事件处理程序调用的 OnMouseMove 擦除函数中。

盘旋提示和交叉点

表单的 OnPaint 事件处理程序首先绘制笔划,根据应用程序模式,可能会查找并标记所有带红色小圆圈的提示或交集。 一个尖点标记笔划突然改变方向的点。 交集标记一个点,其中一个笔划与自身或另一个笔划相交。

每当重绘控件时, 将发生 Paint 事件。

注意

该示例强制在擦除笔划或应用程序模式更改时,使用窗体的 Refresh 方法重绘窗体本身。

 

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;
    }
}

PaintCusps中,代码循环访问每个笔划中的每个尖点,并在其周围绘制一个红色圆圈。 笔划的 PolylineCusps 属性返回与提示对应的点内的点的索引。 另请注意 Renderer 对象的 InkSpaceToPixel 方法,该方法将点转换为与 DrawEllipse 方法相关的坐标。

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);
        }
    }
}

PaintIntersections中,代码循环访问每个笔划,以查找其与整个笔划集的交集。 请注意,笔划的 FindIntersections 方法向 Strokes 集合传递,并返回表示交集的浮点索引值数组。 然后,代码计算每个交集的 X-Y 坐标,并在其周围绘制一个红色圆圈。

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

处理具有两端的笔

CursorDownNewPacketsStroke 事件的 InkCollector 对象定义了三个事件处理程序。 每个事件处理程序检查 Cursor 对象的 Inverted 属性,以查看正在使用笔的哪一端。 当笔反转时:

  • 方法 myInkCollector_CursorDown 使笔划透明。
  • 方法 myInkCollector_NewPackets 擦除笔划。
  • 方法 myInkCollector_Stroke 取消事件。 NewPackets 事件在 Stroke 事件之前生成。

跟踪游标

无论用户使用的是笔还是鼠标, 都会生成 MouseMove 事件。 MouseMove 事件处理程序首先检查以确定当前模式是否为擦除模式以及是否按下了任何鼠标按钮,并在这些状态不存在时忽略该事件。 然后,事件处理程序使用 Renderer 对象的 PixelToInkSpace 方法将光标的像素坐标转换为墨迹空间坐标,并根据当前擦除模式调用代码的擦除方法之一。

擦除笔划

方法 EraseStrokes 采用光标在墨迹空间中的位置,并生成单位内的 HitTestRadius 笔划集合。 参数 currentStroke 指定不应删除的 Stroke 对象。 然后,从收集器中删除笔划集合,并重绘窗体。

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();
    }
}

在交叉口处擦除

方法 EraseAtIntersections 循环访问位于测试半径内的每个笔划,并生成该笔划与集合中所有其他笔划之间的交集数组。 如果未找到交集,则删除整个笔划;否则,将找到距离测试点的笔划上最近的点,并从中定位点两侧的交集,描述要移除的段。

Stroke 对象的 Split 方法用于将段与笔划的其余部分分开,然后删除段,使笔划的其余部分保持不变。 与 在 中 EraseStrokes一样,窗体在方法返回之前重新绘制。

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]);
        ...
    }
    ...
}

在 Cusps 处擦除

对于测试半径范围内的每个笔划, EraseAtCusps 方法从 Stroke 对象的 PolylineCusps 方法中检索 cusps 数组。 笔划的每一端也是一个尖点,因此,如果笔划只有两个尖点,则删除整个笔画:否则,将找到距离测试点的笔划上最近的点,并从中定位点两侧的交集,描述要移除的段。

Stroke 对象的 Split 方法用于将段与笔划的其余部分分开,然后删除段,使笔划的其余部分保持不变。 与 在 中 EraseStrokes一样,窗体在方法返回之前重新绘制。

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);
        ...
    }
    ...
}

关闭窗体

窗体的 Dispose 方法释放 InkCollector 对象 myInkCollector