在画布上绘制大像素

发布于 2024-12-10 21:41:15 字数 1000 浏览 1 评论 0原文

我正在编写一个应用程序,允许用户在触摸屏显示器上绘图。我目前正在使用下面的方法,效果非常好。该方法产生“高分辨率图像”,因为几乎每个像素都绘制一条线(例如100、100→102、103)。

这是我的问题。我希望用户绘制一个“低分辨率图片”(大像素板),您可以在其中故意看到 50×50 的像素(例如 100, 100 -> 150, 150)。有人知道如何实现这一目标吗?我正在使用 Windows Phone 版 Silverlight。我正在考虑构建一个 50×50 像素的大网格,但可能控件太多。

void FingerMove(object sender, MouseEventArgs e)
{
    if (this.IsDrawing)
    {
        this.DestinationPoint = e.GetPosition(paint);
        Line line = new Line
        {
            Stroke = this.Color,
            X1 = this.DestinationPoint.X,
            Y1 = this.DestinationPoint.Y,
            X2 = this.OriginPoint.X,
            Y2 = this.OriginPoint.Y,
            StrokeStartLineCap = PenLineCap.Round,
            StrokeEndLineCap = PenLineCap.Round,
            StrokeThickness = 15,
            Opacity = 1,
        };

        Debug.WriteLine(string.Join(",", line.X1, line.Y1, line.X2, line.Y2));

        paint.Children.Add(line);
    }

    this.OriginPoint = this.DestinationPoint;
}

I am writing an app which allow user to draw on a touch screen display. I am currently using the method below and it work very well. This method is producing a “high resolution image” since for almost every single pixel a line is drawn (e.g. 100, 100 -> 102, 103).

Here is my question. I’d like user to draw a “low resolution picture” (big pixels board) where you can intentionally see pixels of 50×50 (e.g. 100, 100 -> 150, 150). Does anybody have an idea on how to accomplish that? I am using Silverlight for Windows Phone. I was thinking about building a big grid of 50×50 pixels, but there might be too many controls.

void FingerMove(object sender, MouseEventArgs e)
{
    if (this.IsDrawing)
    {
        this.DestinationPoint = e.GetPosition(paint);
        Line line = new Line
        {
            Stroke = this.Color,
            X1 = this.DestinationPoint.X,
            Y1 = this.DestinationPoint.Y,
            X2 = this.OriginPoint.X,
            Y2 = this.OriginPoint.Y,
            StrokeStartLineCap = PenLineCap.Round,
            StrokeEndLineCap = PenLineCap.Round,
            StrokeThickness = 15,
            Opacity = 1,
        };

        Debug.WriteLine(string.Join(",", line.X1, line.Y1, line.X2, line.Y2));

        paint.Children.Add(line);
    }

    this.OriginPoint = this.DestinationPoint;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

往昔成烟 2024-12-17 21:41:15

@Amr 有正确的想法。我将向您提供这段代码,但需要注意的是我根本没有测试过它。我从此处获取线段相交算法。

首先,您需要设置一个矩形列表并将它们添加到作为“像素”的画布中:

    private List<Rectangle> _rects = new List<Rectangle>();

    private void GenerateRects()
    {
        int width = 300; // or whatever dimensions...
        int height = 300;
        int gridSize = 50;

        for (int x = 0; x < width; x += gridSize)
        {
            for (int y = 0; y < height; y += gridSize)
            {
                var rect = new Rectangle
                {
                    Opacity = 0,
                    Width = Math.Min(gridSize, width - x),
                    Height = Math.Min(gridSize, height - y),
                };

                Canvas.SetLeft(rect, x);
                Canvas.SetTop(rect, y);

                _rects.Add(rect);
                this.paint.Children.Add(rect);
            }
        }
    }

我们需要这些辅助方法:

    class LineSegment
    {
        public double X1 { get; set; }
        public double X2 { get; set; }
        public double Y1 { get; set; }
        public double Y2 { get; set; }
    }

    private static bool SegmentsIntersect(LineSegment A, LineSegment B)
    {
        double x1 = A.X1, x2 = A.X2, x3 = B.X1, x4 = B.X2;
        double y1 = A.Y1, y2 = A.Y2, y3 = B.Y1, y4 = B.Y2;

        double denominator = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1);

        if (denominator == 0)
            return false;

        double ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / denominator;
        double ub = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / denominator;

        return (ua > 0 && ua < 1 && ub > 0 && ub < 1);
    }

    private static bool RectIntersectsLine(Rect A, LineSegment B)
    {
        return (SegmentsIntersect(B, new LineSegment { X1 = A.X, Y1 = A.Y, X2 = A.X, Y2 = A.Y + A.Height }) ||
            SegmentsIntersect(B, new LineSegment { X1 = A.X, Y1 = A.Y + A.Height, X2 = A.X + A.Width, Y2 = A.Y + A.Height }) ||
            SegmentsIntersect(B, new LineSegment { X1 = A.X + A.Width, Y1 = A.Y + A.Height, X2 = A.X + A.Width, Y2 = A.Y }) ||
            SegmentsIntersect(B, new LineSegment { X1 = A.X + A.Width, Y1 = A.Y, X2 = A.X, Y2 = A.Y }) ||
            RectContainsPoint(A, new Point(B.X1, B.Y1)) ||
            RectContainsPoint(A, new Point(B.X2, B.Y2)));
    }

    private static bool RectContainsPoint(Rect A, Point B)
    {
        return (B.X > A.X && B.X < A.X + A.Width && B.Y > A.Y && B.Y < A.Y + A.Height);
    }

然后,在 FingerMove 函数中,我们循环遍历每个矩形以查看它是否相交。如果是,我们改变它的颜色:

    void FingerMove(object sender, MouseEventArgs e)
    {
        if (this.IsDrawing)
        {
            this.DestinationPoint = e.GetPosition(paint);
            LineSegment line = new LineSegment
            {
                X1 = this.DestinationPoint.X,
                Y1 = this.DestinationPoint.Y,
                X2 = this.OriginPoint.X,
                Y2 = this.OriginPoint.Y
            };

            foreach (var rect in _rects)
            {
                var x = Canvas.GetLeft(rect);
                var y = Canvas.GetTop(rect);

                if (RectIntersectsLine(new Rect(x, y, rect.Width, rect.Height) , line))
                {
                    rect.Opacity = 1;
                    rect.Fill = Color;
                }
            }
        }

        this.OriginPoint = this.DestinationPoint;
    }

@Amr has the right idea. I'll give you this code with the caveat that I haven't tested it at all. I took the line segment intersection algorithm from here.

First, you need to set up a list of Rectangles and add them to the canvas that are your "pixels":

    private List<Rectangle> _rects = new List<Rectangle>();

    private void GenerateRects()
    {
        int width = 300; // or whatever dimensions...
        int height = 300;
        int gridSize = 50;

        for (int x = 0; x < width; x += gridSize)
        {
            for (int y = 0; y < height; y += gridSize)
            {
                var rect = new Rectangle
                {
                    Opacity = 0,
                    Width = Math.Min(gridSize, width - x),
                    Height = Math.Min(gridSize, height - y),
                };

                Canvas.SetLeft(rect, x);
                Canvas.SetTop(rect, y);

                _rects.Add(rect);
                this.paint.Children.Add(rect);
            }
        }
    }

We'll need these helper methods:

    class LineSegment
    {
        public double X1 { get; set; }
        public double X2 { get; set; }
        public double Y1 { get; set; }
        public double Y2 { get; set; }
    }

    private static bool SegmentsIntersect(LineSegment A, LineSegment B)
    {
        double x1 = A.X1, x2 = A.X2, x3 = B.X1, x4 = B.X2;
        double y1 = A.Y1, y2 = A.Y2, y3 = B.Y1, y4 = B.Y2;

        double denominator = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1);

        if (denominator == 0)
            return false;

        double ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / denominator;
        double ub = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / denominator;

        return (ua > 0 && ua < 1 && ub > 0 && ub < 1);
    }

    private static bool RectIntersectsLine(Rect A, LineSegment B)
    {
        return (SegmentsIntersect(B, new LineSegment { X1 = A.X, Y1 = A.Y, X2 = A.X, Y2 = A.Y + A.Height }) ||
            SegmentsIntersect(B, new LineSegment { X1 = A.X, Y1 = A.Y + A.Height, X2 = A.X + A.Width, Y2 = A.Y + A.Height }) ||
            SegmentsIntersect(B, new LineSegment { X1 = A.X + A.Width, Y1 = A.Y + A.Height, X2 = A.X + A.Width, Y2 = A.Y }) ||
            SegmentsIntersect(B, new LineSegment { X1 = A.X + A.Width, Y1 = A.Y, X2 = A.X, Y2 = A.Y }) ||
            RectContainsPoint(A, new Point(B.X1, B.Y1)) ||
            RectContainsPoint(A, new Point(B.X2, B.Y2)));
    }

    private static bool RectContainsPoint(Rect A, Point B)
    {
        return (B.X > A.X && B.X < A.X + A.Width && B.Y > A.Y && B.Y < A.Y + A.Height);
    }

Then, in the FingerMove function, we loop through each Rectangle to see if it intersects. If it does, we change its color:

    void FingerMove(object sender, MouseEventArgs e)
    {
        if (this.IsDrawing)
        {
            this.DestinationPoint = e.GetPosition(paint);
            LineSegment line = new LineSegment
            {
                X1 = this.DestinationPoint.X,
                Y1 = this.DestinationPoint.Y,
                X2 = this.OriginPoint.X,
                Y2 = this.OriginPoint.Y
            };

            foreach (var rect in _rects)
            {
                var x = Canvas.GetLeft(rect);
                var y = Canvas.GetTop(rect);

                if (RectIntersectsLine(new Rect(x, y, rect.Width, rect.Height) , line))
                {
                    rect.Opacity = 1;
                    rect.Fill = Color;
                }
            }
        }

        this.OriginPoint = this.DestinationPoint;
    }
绮筵 2024-12-17 21:41:15

如果您只是想让线条变粗,只需尝试 StrokeThickness 的可能值,直到获得所需的效果。

如果您想通过填充屏幕的大面积(例如(50x50)矩形)来手动绘制线条,您可以执行以下操作:

  1. 将屏幕划分为 50x50 矩形
  2. 检查哪些矩形与用户绘制的线条相交
  3. 仅绘制矩形从步骤 2 开始,

这将为您提供所需的“对齐网格”线。

If you simply want to make the line thicker, just experiment with possible values of StrokeThickness untill you get the desired effect.

If you want to manually draw the line by filling up large areas of the screen say (50x50) rectangles, you could do the follwing:

  1. divide the screen into 50x50 rectangles
  2. check which rectangles are intersected by the line drawn by the user
  3. only draw the rectangles from step 2

This would give you the 'snap to grid' line that you want.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文