Algorytm rysowania polilinii.

0

Hej,

Potrzebuję się Was poradzić. Piszę dodatek standalone, który wrzuca do Excela pewne dane.
Dodatkową funkcją jest rysowanie polilinii na podstawie wygenerowanego wcześniej łańcucha znaków.

Łańcuch jest w postaci (przykład):
95.0,16.0,195.0,59.0<-30.4

Rozbijając po znaku przecinka "," otrzymujemy długości poszczególnych boków. Przecinek oznajmia nam także, iż kolejny bok do poprzedniego jest rysowany pod kątem prostym. Jeśli tak nie jest (tzn. mamy kąt inny niż prosty), to kąt ten określamy znakiem "<".
Pomierzony kąt jest kątem między wektorami, które odczytałem wcześniej.

Z racji tego, iż dodatek musi działać z poprzednimi "wersjami" łańcuchów, nie mam możliwości modyfikacji zapisywania "schematu" kształtu.

Tutaj są zaprezentowane dwa schematy:
http://gpasko.website.pl/excel.jpg

Żółta strzałka określa kierunek "czytania" łańcucha.

I teraz mając taki łańcuch, potrzebuję narysować w Excelu kształt.
Ograniczają mnie rozmiary komórki, przez co potrzebuję skalować boki, jak również ogranicza mnie "margines" okna excela.
Korzystam z EPPlus do generowania plików, ale to tutaj nie ma znaczenia.

Chodzi mi głównie o algorytm, na razie napisałem sobie coś takiego:

` `` `
private class RodTypeArray
        {
            public double x;
            public double y;
            public double width;
            public double angle;
        }

private void DrawRodShape(string type, int currentRecord, int currentPage, ExcelWorksheet specSheet, int currentRod)
        {

            int currentRow = 6 + currentRecord + 32 * (currentPage - 1);
            int cellMaxWidth = 170;
            int cellMaxHeight = 30;
            int offsetTop = 0;
            int offsetLeft = 0;

            List<RodTypeArray> rta = new List<RodTypeArray>();
            rodLine rod = new rodLine(type);

                for (int i = 0; i < rod.ElementsCount; i++)
                {
                    double angle = rod.ElementAt(i).Angle;
                    RodTypeArray r = new RodTypeArray();
                    rodElement e = rod.ElementAt(i);
                    angle = angle * Math.PI / 180.0;
                    r.width = e.Length;
                    r.angle = angle;
                    rta.Add(r);
                }
                rta[0].angle = 0;

                double _prevAngle = 0.0;
                double width = 0.0;
                double height = 0.0;
                double maxWidth = 0.0;
                double maxHeight = 0.0;

                for (int i = 0; i < rod.ElementsCount; i++)
                {

                    _prevAngle += rta[i].angle;
                    rta[i].x = rta[i].width * Math.Cos(_prevAngle);
                    rta[i].y = rta[i].width * Math.Sin(_prevAngle);
                    //}
                    width += rta[i].x;
                    height += rta[i].y;
                    maxWidth = Math.Max(Math.Abs(width), maxWidth);
                    maxHeight = Math.Max(Math.Abs(height), maxHeight);
                }

                double scaleCorrectionX = 1.0;
                double scaleCorrectionY = 1.0;

                if (maxWidth > cellMaxWidth)
                    scaleCorrectionX = cellMaxWidth / maxWidth;
                if (maxHeight > cellMaxHeight)
                    scaleCorrectionY = cellMaxHeight / maxHeight;

                int startX = 0;
                int startY = 0;
                string _test2 = string.Empty;

                offsetTop = 1443 * (currentPage - 1) + 223 + (currentRecord - 1) * 51 + 10;
                offsetLeft = 117 + Math.Abs(Convert.ToInt32((cellMaxWidth - maxWidth * scaleCorrectionX) / 2));

                for (int i = 0; i < rta.Count; i++)
                {
                    startX = (int)(rta[i].x * scaleCorrectionX);
                    startY = (int)(rta[i].y * scaleCorrectionY);

                    bool goBack = false;
                    if (startY < 0)
                    {
                        goBack = true;
                        offsetTop += startY;
                    }
                    if (startX < 0)
                    {
                        goBack = true;
                        offsetLeft += startX;
                    }

                    startX = Math.Abs(startX);
                    startY = Math.Abs(startY);

                    AddLineShape(string.Format("shape_p{0}_{1}_{2}", currentPage, currentRecord, i), offsetTop, offsetLeft, startX, startY, specSheet);

                    if (!goBack)
                    {
                        offsetLeft += startX;
                        offsetTop += startY;
                    }
                }
        }

rodLine to obiekt łańcucha, rodElement to obiekt konkretnego "boku" kształtu.

Niestety nie wszystkie kształty udaje mi się narysować prawidłowo:
http://gpasko.website.pl/excel2.jpg

Chciałbym się poradzić, jak powinienem fachowo podejść do tematu, bo mój kod jest na razie ... no bardzo prymitywny chyba.

Może ktoś mi będzie mógł chociaż trochę pomóc, podpowiedzieć. A może gdzieś w internecie jest jakiś dobry kod do rysowania polilinii na podstawie długości boków i kątów. To by mi mogło pomóc :)

Dzięki!

1

Ja bym działał trochę inaczej. Wpierw wyliczam współrzędne wierzchołków (nie wiem gdzie ma być pierwszy):

dlugosc = ...
kat = ...
nastepny.x = poprzedni.x + dlugosc*cos(kat);
nastepny.y = poprzedni.y + dlugosc.sin(kat);

W pętli po obliczonych wierzchołkach wyznaczam skrajne wartości współrzędnych. Znając rozmiar komórki w pikselach, skaluję wszystkie wierzchołki i rysuję.

0

Trochę naprowadziłeś mnie na temat (jak zwykle zakopałem się w czymś, co jest prostsze).
Niestety mój algorytm rysowania nadal jest niedoskonały albo coś pominąłem:

        private void DrawRodShape(string type, int currentRecord, int currentPage, ExcelWorksheet specSheet, int currentRod)
        {
            int offsetTop = 0;
            int offsetLeft = 0;
            int startX = 0;
            int startY = 0;
            int currentRow = 6 + currentRecord + 32 * (currentPage - 1);

            List<RodTypeArray> rta = new List<RodTypeArray>();
            rodLine rod = new rodLine(type);

            double _prevAngle = 0.0;
            double _prevOffsetX = 0.0;
            double _prevOffsetY = 0.0;
            string _test = string.Empty;

            for (int i = 0; i < rod.ElementsCount; i++)
            {
                RodTypeArray r = new RodTypeArray();
                rodElement e = rod.ElementAt(i);
                
                double angle = e.Angle;
                
                r.width = e.Length;

                //pierwszy bok zawsze poziomy
                if (i == 0)
                    angle = 0.0;
                else
                        angle = angle * Math.PI / 180.0;


                angle += _prevAngle;
                r.angle = angle;

                startX =  (int)(r.width * Math.Cos(angle));
                startY = (int)(r.width * Math.Sin(angle));
                
                r.x = Math.Abs(startX);
                r.y = Math.Abs(startY);
                if (startX < 0)
                    r.offsetX += _prevOffsetX + startX;
                else
                    r.offsetX += _prevOffsetX;

                if (startY < 0)
                    r.offsetY += _prevOffsetY + startY;
                else
                    r.offsetY += _prevOffsetY;

                rta.Add(r);
                _prevAngle = angle;
                _prevOffsetX += startX;
                _prevOffsetY += startY;

                _test += string.Format("X:{0}    Y:{1}    oX:{2}    oY:{3}    A:{4}\n", r.x, r.y, r.offsetX, r.offsetY, Math.Round(_prevAngle * 180.0 / Math.PI, 2));

            }

            specSheet.Cells[currentRow, 4].Value = _test;

            for (int i = 0; i < rta.Count; i++)
            {
                startX = (int)(rta[i].x);
                startY = (int)(rta[i].y);
                offsetTop = 1443 * (currentPage - 1) + 223 + (currentRecord - 1) * 51 + 10 + (int)(rta[i].offsetY);
                offsetLeft = 217 + (int)(rta[i].offsetX);
                AddLineShape(string.Format("shape_p{0}_{1}_{2}", currentPage, currentRecord, i), offsetTop, offsetLeft, startX, startY, specSheet);
            }

                specSheet.Cells[currentRow, 4].Value = _test;
        }


private void AddLineShape(string name, int offsetTop, int offsetLeft, int sizeX, int sizeY, ExcelWorksheet specSheet)
        {
            var shape = specSheet.Drawings.AddShape(name, eShapeStyle.Line);
            shape.SetPosition(offsetTop, offsetLeft);
            shape.SetSize(sizeX, sizeY);
            //...
        }

Mechanizm rysowania jest dosyć banalny, ale utrudniony z tego względu, iż nie mogę podać ujemnych wartości sizeX, sizeY. Z tego powodu w przypadku ujemnych wartości startX i/lub startY "modyfikuję" odpowiednie offsety.

Tutaj screenshoty z wynikami i danymi:
http://gpasko.website.pl/excel3.jpg
http://gpasko.website.pl/excel4.jpg

1 użytkowników online, w tym zalogowanych: 0, gości: 1