【算法实现】压路机压路原理,又名色彩重叠显示算法

 问题描述:

最近需要实现当重复画图时候,色彩根据重叠次数显示指定颜色。来达到模拟现实压路机实况。

使用winform实现。

解决思路:

1.首先在winform上实现,最简单的配置就是利用Graphics 进行画图

2.将所有的图画都相当于是坐标点的处理-Point。

然后线就等于两个点的连线组成直线。图形就相当于线给于一定的宽度。

3.这样一条笔直的直线,可以拆分成许多数量的短线段来处理。短线段又可以当做矩形处理,即时是斜着的,或者不规则的矩形,我们可以进行两段处理,第一段利用矩形的特征,去过滤掉不重叠的线段,减少无效处理,筛选完成后,在进行精确处理,利用Region的Intersect获得相交区域。

解决方案:

1.前置准备工作。

准备颜色表。

          
         List<Color> colorList = new List<Color>();
             colorList.Add(Color.Red);
            colorList.Add(Color.Green);
            colorList.Add(Color.Blue);
            colorList.Add(Color.Black);
            colorList.Add(Color.Orange);
            colorList.Add(Color.Yellow);
            colorList.Add(Color.White);

画线的代码

        Pen pen = new Pen(color, 40);

        pe.Graphics.DrawLine(pen, lastPoint,_point); 
         //pen是画笔,定义颜色和线宽。lastPoint和_point是两个点,不用在意起名。就是对应的线的起点和终点。

初始化的,每个0.5秒将点变化,然后将新的点存放进列表list里面,自己有数据来源的,可以把这部分替换成自己的变化数据源。

            System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
            timer.Interval = 500; // 1000毫秒(1秒)
            timer.Tick += Timer_Tick;
            timer.Start();
        private void Timer_Tick(object sender, EventArgs e)
        {
            if (_isRunning)
            {
                Point startPoint = new Point(_point.X, _point.Y);
                _point.X += 10; // 每秒更新点的X坐标
                _point.Y += 10; // 每秒更新点的Y坐标
                //lastPoint = _point;
                segments.Add(new LineSegment(startPoint, new Point(_point.X, _point.Y), 40));
                // 触发重绘事件
                this.Invalidate();
            }
        }

添加图片作为模型图标的代码。png图片旋转角度,我这里设置的是45度,图片的宽高尺寸设的是110和110.

 pe.Graphics.DrawImage(image1, _point.X - 20, _point.Y - 20, 110, 110);
            Image image = Image.FromFile(path);
            image1 = RotateImage(image, 45);



//图标旋转的方法
        public static Image RotateImage(Image img, float angle)
        {
            // 计算旋转后的长方形内接矩形大小
            double sin = Math.Abs(Math.Sin(angle * Math.PI / 180.0));
            double cos = Math.Abs(Math.Cos(angle * Math.PI / 180.0));
            int newWidth = (int)(img.Width * cos + img.Height * sin);
            int newHeight = (int)(img.Width * sin + img.Height * cos);

            Bitmap b = new Bitmap(newWidth, newHeight);
            Graphics g = Graphics.FromImage(b);
            g.TranslateTransform((float)b.Width / 2, (float)b.Height / 2);
            g.RotateTransform(angle);
            g.TranslateTransform(-(float)img.Width / 2, -(float)img.Height / 2);
            g.InterpolationMode = InterpolationMode.HighQualityBicubic;
            g.DrawImage(img, new Point(0, 0));
            g.Dispose();
            return b;
        }

2.我们所有的算法都在form的OnPaint里面写,这样可以不断的刷新页面,同步显示信息。

直接复制进去就行,不用和页面做操作,override 就是重写。

        protected override void OnPaint(PaintEventArgs pe)
        {
            base.OnPaint(pe);
           //算法在这里添加
         
        }

3.主要算法

 

为了便利,创建LineSegment类进行管理,嫌麻烦的也可以不要,这个LineSegment主要是存放起点,终点,和线宽,以及判断方法,放一起好管理,也可以拆开,看个人喜好。

其中的GetBounds方法就是用来判断两个线是否重叠,这个方法适用不同角度的线,当然也不够精确,会把一定范围内的线也算进来的,但是这步只是筛选,将数据大幅度减少。

后面的GetOverlapRegion方法,才是精确处理,重叠区域,如果不重叠返回null就可以。

  public class LineSegment
    {
        public Point StartPoint { get; set; }
        public Point EndPoint { get; set; }
        public int Width { get; set; }

        public LineSegment(Point startPoint, Point endPoint, int width)
        {
            StartPoint = startPoint;
            EndPoint = endPoint;
            Width = width;
        }

        public RectangleF GetBounds()
        {
            //Line line1 = new Line();
            //line1.Width = Width;
            //line1.X1 = StartPoint.X;
            //line1.Y1 = StartPoint.Y;
            //line1.X2 = EndPoint.X;
            //line1.Y2 = EndPoint.Y;
            float left = Math.Min(StartPoint.X, EndPoint.X) - Width / 2;
            float top = Math.Min(StartPoint.Y, EndPoint.Y) - Width / 2;
            float width = Math.Abs(StartPoint.X - EndPoint.X) + Width;
            float height = Math.Abs(StartPoint.Y - EndPoint.Y) + Width;

            return new RectangleF(left, top, width, height);
        }

        public Region GetOverlapRegion(LineSegment otherSegment)
        {
            RectangleF bounds = GetBounds();
            RectangleF otherBounds = otherSegment.GetBounds();

            if (!bounds.IntersectsWith(otherBounds)) return null;

            GraphicsPath path1 = new GraphicsPath();
            path1.AddLine(StartPoint, EndPoint);
            path1.Widen(new Pen(Color.Black, Width));

            GraphicsPath path2 = new GraphicsPath();
            path2.AddLine(otherSegment.StartPoint, otherSegment.EndPoint);
            path2.Widen(new Pen(Color.Black, otherSegment.Width));

            Region overlapRegion = new Region(path1);
            overlapRegion.Intersect(path2);
            if (overlapRegion!=null &&!overlapRegion.IsEmpty(Graphics.FromImage(new Bitmap(1, 1))))
            {
                return overlapRegion;
            }
            else
            {
                return null;
            }

           
        }
    }

使用时,将存放有线的列表,进行两两比较,获得重叠区域,按照字典存放进去,为什么用字典,是利用了字典键的唯一性,后面进行颜色处理比较便利。

 Dictionary<int,List<Region>> overlapRegions = new Dictionary<int, List<Region>>();

            for (int i = 0; i < segments.Count; i++)
            {
                for (int j = i + 1; j < segments.Count; j++)
                {
                    LineSegment segment1 = segments[i];
                    LineSegment segment2 = segments[j];

                    Region overlapRegion = segment1.GetOverlapRegion(segment2);

                    if (overlapRegion != null)
                    {
                        if (overlapRegions.ContainsKey(i))
                        {
                            overlapRegions[i].Add(overlapRegion);
                        }
                        else
                        {
                            List<Region> regions = new List<Region>();
                            regions.Add(overlapRegion);
                            overlapRegions.Add(i, regions);
                        }

                    }
                }
            }

这样得到了,只要重叠了就会存进去,如果同一节线段和多个线段重叠,那么在对应键的下,vale值的列表数量就会变多,可以帮助我们划分颜色层数。

//

下面先将字典按照里面列表数量进行排序,其实不处理也行,习惯了,处理后方面后面数据容错。

然后获得最大重叠层数

然后从第1层颜色开始遍历重叠区域,然后用Graphics.FillRegion填充颜色。

var sortedDictionary = overlapRegions.OrderBy(pair => pair.Value.Count).ToDictionary(pair => pair.Key, pair => pair.Value);


            int maxCount = 0;
            if (overlapRegions.Count > 0)
            {
                maxCount = overlapRegions.Values.Max(list => list.Count);
            }

            for(int i = 0; i < maxCount; i++)
            {
                foreach (var sub in sortedDictionary)
                {
                    List<Region> regions = sub.Value;
                    if (i < regions.Count)
                    {
                        int x = i + 1;
                        if (x < colorList.Count)
                        {

                        }
                        else
                        {
                            x = colorList.Count - 1;
                        }

                        if (x < 1)
                        {
                            x = 1;
                        }

                        pe.Graphics.FillRegion(new SolidBrush(colorList[x]), regions[i]);

                    }          
                }
            }
  

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值