@slavenski
Android developer

Как отрисовать трек движения?

Добрый день, люди!
Пишу сейчас программу для оптимизации движения техники по полю.
Собственно не могу справится с такой задачей:
В программу задается полигон (он же является полем), в нем задается направление трека движения техники (пусть в данном случае будет комбайн), и нужно отрисовать трек движения весь в поле, чтобы он заполнил его, т.е. выглядит примерно так (рис. 1):
5a69d32ea7c54098882695.png
здесь на первом этапе задается полигон, а потом отрисовывается трек. Проблема в том, что когда трек прямолинейный - все хорошо прорисовывается, а если он будет направлен диагонально, возникает ошибка.
Уже неделю бьюсь головой о стол, не могу решить =(

Логика у меня такая:
1)сначала я получаю максимальные координаты поля по Х и Y
2)отрисовываю трек
3)чтобы трек не заходил за пределы поля, я написал функцию определения пересечения двух отрезков, и нахожу точки пересечения отрезка трека с отрезком полигона, собственно вот код функции:
Код функции пересечения
private float [] CrossLine(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4)
        {
            float Ua, Ub, numerator_a, numerator_b, denominator, x, y;
            float [] pointCross = new float[2];
            denominator = (y4 - y3) * (x1 - x2) - (x4 - x3) * (y1 - y2);
            numerator_a = (x4 - x2) * (y4 - y3) - (x4 - x3) * (y4 - y2);
            numerator_b = (x1 - x2) * (y4 - y2) - (x4 - x2) * (y1 - y2);
            Ua = numerator_a / denominator;
            Ub = numerator_b / denominator;
            //cout << (Ua >=0 && Ua <=1 && Ub >=0 && Ub <=1 ? "peresek" : "ne peresek") << endl;
            if (Ua >= 0 && Ua <= 1 && Ub >= 0 && Ub <= 1)
            {
                float dx1 = x2 - x1;
                float dy1 = y2 - y1;
                float dx2 = x4 - x3;
                float dy2 = y4 - y3;
                x = dy1 * dx2 - dy2 * dx1;
                y = x3 * y4 - y3 * x4;
                x = ((x1 * y2 - y1 * x2) * dx2 - y * dx1) / x;
                y = (dy2 * x - y) / dx2;
                pointCross[0] = x;
                pointCross[1] = y;
            }
            else
            {
                pointCross[0] = 0;
                pointCross[1] = 0;
            }
            //cout << pointCross[0] << "\t" << pointCross[1] << endl;
            return pointCross;
        }


В ней, если нашлось пересечение - мы возвращаем координаты точки пересечения, если пересечения нету - возвращаем нуль.
4)Повторяем эти итерации пока не покроем все поле

Собственно вот весь код функции отрисовки трека:
Код функции отрисовки
private void buttonTrackView_Click_1(object sender, EventArgs e)
        {
            if (textBoxTrack.Text != "")
            {
                Graphics g = pictureBoxField.CreateGraphics();
                g.TranslateTransform((float)pictureBoxField.Width, 0);
                Pen myPen = new Pen(Color.Blue);   //Кисть синего цвета для отрисовки трека
                StreamReader fcoords = new StreamReader(textBoxTrack.Text);

                string temp = "";   //хранит текущее число в файле до точки с запятой

                while (true)    //Считывание с файла
                {
                    string text = fcoords.ReadLine();
                    if (text == null) break;
                    temp += text;
                }

                String[] temps = temp.Split(';');   //создаем массив для списка координат
                float coordsBeginTrackX = 1;
                float coordsBeginTrackY = 1;

                /*for (int i = 0; i < temps.Length-1; i++)  //записываем в массив координаты
                {
                    if(i % 2 == 0)
                    {
                        //coordsBeginTrackX = Convert.ToSingle(temps[i]);
                        coordsBeginTrackX = 1;
                    }
                    else
                    {
                        //coordsBeginTrackY = Convert.ToSingle(temps[i]);
                        coordsBeginTrackY = 1;
                    }
                }*/
                //MessageBox.Show(coordsBeginTrackX.ToString() + ";" + coordsBeginTrackY.ToString());

                float maxFieldX = Convert.ToSingle(coordsX.Max());  //Максимальная координата поля по Х
                float maxFieldY = Convert.ToSingle(coordsY.Max());  //Максимальная координата поля по У
                float coordsEndTrackX = coordsBeginTrackX;
                float coordsEndTrackY = coordsBeginTrackY;
                //MessageBox.Show(maxFieldY.ToString() + ";" + maxFieldX.ToString());

                /*for (int i = 0; coordsEndTrackY < maxFieldY; i++) //увеличиваем отрезок трека до границы поля
                {
                    coordsEndTrackX = i * 0.5f;
                    coordsEndTrackY = coordsEndTrackX;
                }*/

                while(coordsEndTrackY < maxFieldY)  //отрисовка первого отрезка
                {
                    //coordsEndTrackX++;
                    coordsEndTrackY++;
                }
                //g.DrawLine(myPen, coordsBeginTrackX*(-0.1f), coordsBeginTrackY * (0.1f), coordsEndTrackX * (-0.1f), coordsEndTrackY * (0.1f));

                List<double> crossPointX = new List<double>();  //координаты поля по x
                List<double> crossPointY = new List<double>();  //ккординаты поля по у

                //MessageBox.Show(coordsEndTrackX.ToString() + ";" + coordsEndTrackY.ToString());

                
                float[] crossPoints = new float[2];
                float[] points = new float[4];
                
                
                while (coordsBeginTrackX < maxFieldX)
                {
                    for (int i = 0; i < coordsX.Count - 1; i++)
                    {
                        crossPoints = CrossLine(coordsBeginTrackX, coordsBeginTrackY, coordsEndTrackX, coordsEndTrackY,
                            Convert.ToSingle(coordsX[i]), Convert.ToSingle(coordsY[i]), 
                            Convert.ToSingle(coordsX[i + 1]), Convert.ToSingle(coordsY[i + 1]));

                        if (crossPoints[0] != 0 && crossPoints[1] != 0)
                        {
                            crossPointX.Add(crossPoints[0]);
                            crossPointY.Add(crossPoints[1]);
                            //MessageBox.Show(crossPoints[0].ToString() + "; " + crossPoints[1].ToString());
                        }
                    }
                    coordsBeginTrackX += 100;
                    coordsEndTrackX += 100;
                    //g.DrawLine(myPen, coordsBeginTrackX * (-0.1f), coordsBeginTrackY * (0.1f), coordsEndTrackX * (-0.1f), coordsEndTrackY * (0.1f));
                    
                }
                

                for (int i = 0; i < crossPointX.Count - 1; i++)
                {
                    if (i % 2 == 0)
                    {
                        /*при небольшом сдвиге дает ошибку перевыполнения*/
                        g.DrawLine(myPen, Convert.ToSingle(crossPointX[i]) * (-0.1f), Convert.ToSingle(crossPointY[i]) * (0.1f),
                             Convert.ToSingle(crossPointX[i + 1]) * (-0.1f), Convert.ToSingle(crossPointY[i + 1]) * (0.1f));
                        //MessageBox.Show(crossPointX[i].ToString() + "; " + crossPointY[i].ToString() + "; итерация: " + i.ToString());
                        //g.DrawLine(myPen, coordsBeginTrackX * (-0.1f), coordsBeginTrackY * (0.1f), coordsEndTrackX * (-0.1f), coordsEndTrackY * (0.1f));
                    }
                    //MessageBox.Show(crossPointX[i].ToString() + "; " + crossPointY[i].ToString() + "; итерация: " + i.ToString());
                    //MessageBox.Show(i.ToString());
                }
                //MessageBox.Show(crossPointX[0].ToString() + "; " + crossPointY[0].ToString());
                //g.DrawLine(myPen, Convert.ToSingle(crossPointX[0]) * (-0.1f), Convert.ToSingle(crossPointY[0]) * (0.1f),
                                //Convert.ToSingle(crossPointX[1]) * (-0.1f), Convert.ToSingle(crossPointY[1]) * (0.1f));
            }
            else
            {
                MessageBox.Show("Файл с координатами направления трека не выбран!");
            }
        }


В общем, изложил как мог детально, не судите строго мои костыли, буду благодарен любой помощи)
  • Вопрос задан
  • 226 просмотров
Пригласить эксперта
Ответы на вопрос 2
tsarevfs
@tsarevfs
C++ developer
Рекомендация 1. Найдите библиотеку для геометрии, если ваша задача не в том чтобы научиться решать задачи вычислительной геометрии. Проверка находится ли точка внутри полигона реализована в каждой первой такой библиотеке.

Рекомендация 2. Разбейте вашу задачу на отдельные части. Считывание данных - функция или метод, вычисления -- еще одна, отрисовка -- третья.

Рекомендация 3. Если все же пишете геометрию сами, заведите классы для Point, Segment, Polyline, Polygon. Вместо передачи отдельных координат используйте их.

Рекомендация 4. Функция определения пересечений содержит много делений, и если какой-то из делителей будет близок к нулю, то все будет плохо. Рекомендую посмотреть реализацию http://e-maxx.ru/algo/segments_intersection, где есть проверки на маленький делитель.
Ответ написан
@slavenski Автор вопроса
Android developer
Да я, наверное, плохо объяснил, программа не должна определять направление, направление уже задано, просто его нужно отобразить, у меня не выходит, а так да, все ваши замечания уместны, спасибо =)
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы