墨迹擦除示例

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

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

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

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

环绕提示和十字路口

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

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

注意

该示例使用窗体的 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 对象的 倒排 属性,以查看正在使用哪个笔的末尾。 反转笔时:

  • 该方法 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 中擦除

对于属于测试半径中的每个笔划,该方法 EraseAtCuspsStroke 对象的 PolylineCusps 方法中检索提示数组。 笔划的每个末尾也是一个提示,因此,如果笔划只有两个提示,则删除整个笔划:否则,笔划到测试点的最接近点位于该点的两侧的交集,用于描述要移除的段。

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