Перетворення 2D кривої в точки для зберігання даних


12

Я створив алгоритм, який перетворює будь-яку криву, тобто шлях, у мінімальну кількість точок, щоб я міг зберегти її у файл чи базу даних.

Метод простий: він переміщує три точки рівними кроками і вимірює кут між лініями, які ці точки утворюють. Якщо кут більший за допуск, то він створює нову кубічну криву до цієї точки. Потім він рухає лінії вперед і знову вимірює кут ...

Для тих, хто знає клас Android Path - зауважте, що dstPath - це власний клас, який записує точки в масив, щоб я міг зберегти очки пізніше, тоді як srcPath є результатом об'єднання регіонів і тому не має для мене ключових моментів зберегти.

Проблема полягає в тому, що коло не виглядає гладким, як ви бачите на цьому зображенні, отриманому за кодом нижче, де вихідний шлях складається з ідеального кола та прямокутника. Я намагався змінити кут толерантності та довжину кроків, але нічого не допомагає. Цікаво, чи можете ви запропонувати будь-яке вдосконалення цього алгоритму чи інший підхід.

EDIT: Зараз я опублікував увесь код для тих, хто використовує Android java, тому вони можуть легко спробувати і експериментувати.

введіть тут опис зображення

public class CurveSavePointsActivity extends Activity{

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(new CurveView(this));
    }

    class CurveView extends View{

        Path srcPath, dstPath;
        Paint srcPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        Paint dstPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

        public CurveView(Context context) {
            super(context);

            srcPaint.setColor(Color.BLACK);
            srcPaint.setStyle(Style.STROKE);
            srcPaint.setStrokeWidth(2);
            srcPaint.setTextSize(20);

            dstPaint.setColor(Color.BLUE);
            dstPaint.setStyle(Style.STROKE);
            dstPaint.setStrokeWidth(2);
            dstPaint.setTextSize(20);

            srcPath = new Path();
            dstPath = new Path();

        }

        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);

            //make a circle path
            srcPath.addCircle(w/4, h/2, w/6 - 30, Direction.CW);

            //make a rectangle path
            Path rectPath = new Path();
            rectPath.addRect(new RectF(w/4, h/2 - w/16, w*0.5f, h/2 + w/16), Direction.CW);


            //create a path union of circle and rectangle paths
            RectF bounds = new RectF();
            srcPath.computeBounds(bounds, true);
            Region destReg = new Region();
            Region clip = new Region();
            clip.set(new Rect(0,0, w, h));
            destReg.setPath(srcPath, clip);
            Region srcReg = new Region();
            srcReg.setPath(rectPath, clip); 
            Region resultReg = new Region();
            resultReg.op(destReg, srcReg, Region.Op.UNION);
            if(!resultReg.isEmpty()){
                srcPath.reset();
                srcPath.addPath(resultReg.getBoundaryPath());
            }

            //extract a new path from the region boundary path
            extractOutlinePath();

            //shift the resulting path bottom left, so they can be compared
            Matrix matrix = new Matrix();
            matrix.postTranslate(10, 30);
            dstPath.transform(matrix);

        }

         @Override 
            public void onDraw(Canvas canvas) { 
                super.onDraw(canvas);    
                canvas.drawColor(Color.WHITE);
                canvas.drawPath(srcPath, srcPaint);
                canvas.drawPath(dstPath, dstPaint);

                canvas.drawText("Source path", 40, 50, srcPaint);
                canvas.drawText("Destination path", 40, 100, dstPaint);
         }


         public void extractOutlinePath() {

             PathMeasure pm = new PathMeasure(srcPath, false); //get access to curve points

             float p0[] = {0f, 0f}; //current position of the new polygon
             float p1[] = {0f, 0f}; //beginning of the first line
             float p2[] = {0f, 0f}; //end of the first & the beginning of the second line
             float p3[] = {0f, 0f}; //end of the second line

             float pxStep = 5; //sampling step for extracting points
             float pxPlace  = 0; //current place on the curve for taking x,y coordinates
             float angleT = 5; //angle of tolerance

             double a1 = 0; //angle of the first line
             double a2 = 0; //angle of the second line

             pm.getPosTan(0, p0, null); //get the beginning x,y of the original curve into p0
             dstPath.moveTo(p0[0], p0[1]); //start new path from the beginning of the curve
             p1 = p0.clone(); //set start of the first line

             pm.getPosTan(pxStep, p2, null); //set end of the first line & the beginning of the second

             pxPlace = pxStep * 2;
             pm.getPosTan(pxPlace, p3, null); //set end of the second line


             while(pxPlace < pm.getLength()){
             a1 = 180 - Math.toDegrees(Math.atan2(p1[1] - p2[1], p1[0] - p2[0])); //angle of the first line
             a2 = 180 - Math.toDegrees(Math.atan2(p2[1] - p3[1], p2[0] - p3[0])); //angle of the second line

             //check the angle between the lines
             if (Math.abs(a1-a2) > angleT){

               //draw a straight line to the first point if the current p0 is not already there
               if(p0[0] != p1[0] && p0[1] != p1[1]) dstPath.quadTo((p0[0] + p1[0])/2, (p0[1] + p1[1])/2, p1[0], p1[1]);

               dstPath.quadTo(p2[0] , p2[1], p3[0], p3[1]); //create a curve to the third point through the second

               //shift the three points by two steps forward
               p0 = p3.clone();
               p1 = p3.clone();
               pxPlace += pxStep;
               pm.getPosTan(pxPlace, p2, null); 
               pxPlace += pxStep;
               pm.getPosTan(pxPlace, p3, null);
               if (pxPlace > pm.getLength()) break;
             }else{
               //shift three points by one step towards the end of the curve
               p1 = p2.clone(); 
               p2 = p3.clone();
               pxPlace += pxStep;
               pm.getPosTan(pxPlace, p3, null); 
             }
             }
             dstPath.close();
         }
    }

}

Ось порівняння оригіналу з тим, що виробляє мій алгоритм:

порівняння між стежками;  помітно більш гладкі кути на похідній


чому б не використовувати b-сплайни?
GriffinHeart

4
якщо ви знаєте, що річ - це коло та прямокутник, чому б не зберігати коло та прямокутник? І в узагальненому вигляді - будь-який вхід, який створила ваша річ, мабуть, розумний формат для її зберігання. Якщо ви шукаєте схему стиснення, яка здається іншим питанням (або, принаймні, нам знадобиться набагато більше інформації про вихідні дані бути корисним).
Джефф Гейтс

Це може бути будь-яка непередбачувана форма, як я вже говорив у першому реченні - коло та переказ тут є лише тестовим прикладом.
Луміс

@Lumis, ви дійсно повинні вивчити b-сплайни, це те, для чого вони потрібні. Будь-яка причина спробувати реалізувати власне рішення?
GriffinHeart

1
Клас добре доріжки побудує ці криві зі сплайнами, тому ви вже використовуєте її. У мене є ще одна пропозиція, менш орієнтована на математику: замість збереження очок збережіть введення користувача (шаблон команди) та повторіть його, щоб створити той самий "образ".
GriffinHeart

Відповіді:


6

Я думаю, у вас є дві проблеми:

Несиметричні контрольні точки

Спочатку ви починаєте з рівних відстаней між p0 до p1 і p1 до p2. Якщо кут допуску між відрізками рядків не дотриманий, ви рухаєте p1 і p2 вперед, але тримаєте p0 там, де він був. Це збільшує відстань між p0 до p1, зберігаючи відстань між p1 до p2 однаковою. Коли ви створюєте криву, використовуючи p1 в якості контрольних точок, вона може бути сильно зміщена в бік p2 залежно від того, скільки ітерацій пройшло з моменту останньої кривої. Якщо ви перемістите p2 вдвічі більше, ніж p1, ви отримаєте рівні відстані між точками.

Квадратичні криві

Як вже згадувалося в інших відповідях, квадратична крива не дуже добре для цього випадку. Суміжні криві, які ви створюєте, повинні розділяти контрольну точку та дотичну . Якщо ваші вхідні дані є лише пунктами, Catmull-Rom Spline - хороший вибір для цієї мети. Це кубічна крива Герміта, де дотичні для контрольних точок обчислюються від попередньої та наступної точок.

API Path в Android підтримує криві Безьє, які трохи відрізняються від кривих Hermite щодо параметрів. На щастя, криві Герміта можна перетворити на криві Безьє. Ось перший приклад коду, який я знайшов під час гуглінгу. Ця відповідь Stackoverflow також здається формулою.

Ви також згадали про проблему гострих країв. За наявними вхідними даними неможливо визначити, чи існує фактичний гострий кут чи просто дуже крута крива. Якщо це стає проблемою, ви можете зробити ітерацію більш адаптивною, збільшивши / зменшивши крок на ходу в міру необхідності.

Редагувати: Після подальшого продумування квадратичні криві можна було використати. Замість того, щоб малювати квадратичну криву від p0 до p2, використовуючи p1 як контрольну точку, намалюйте її від p0 до p1, використовуючи нову точку p0_1 як контрольну точку. Дивіться малюнок нижче. Нові контрольні пункти

Якщо p0_1 знаходиться в перетині дотичних в p0 і p1, результат повинен бути рівним. Ще краще, оскільки PathMeasure.getPosTan()повернення також є дотичною як третій параметр, ви можете використовувати фактичні точні дотичні замість наближень від сусідніх точок. При такому підході вам потрібно менше змін до існуючого рішення.

Виходячи з цієї відповіді , точку перетину можна обчислити за такою формулою:

getPosTan(pxPlace0, p0, t0); // Also get the tangent
getPosTan(pxPlace1, p1, t1);
t1 = -t1; // Reverse direction of second tangent
vec2 d = p1 - p0;
float det = t1.x * t0.y - t1.y * t0.x;
float u = (d.y * t1.x - d.x * t1.y) / det;
float v = (d.y * t0.x - d.x * t0.y) / det; // Not needed ... yet
p0_1 = p0 + u * t0;

Однак це рішення працює лише в тому випадку, якщо і u, і v є негативними. Дивіться другу картинку: Промені не перетинаються

Тут промені не перетинаються, хоча лінії будуть, оскільки u від'ємний. У цьому випадку неможливо намалювати квадратичну криву, яка б плавно з'єднувалася з попередньою. Тут потрібні криві Безьє. Ви можете обчислити контрольні точки для цього або методом, наведеним раніше у цій відповіді, або отримати їх безпосередньо від дотичних. Проеціювання p0 на дотичний промінь p0 + u * t0 і навпаки для іншого променя дає обидві контрольні точки c0 і c1. Ви також можете регулювати криву, використовуючи будь-яку точку між p0 і c0 замість c0, поки вона лежить на дотичному промені.

Edit2: Якщо ваше місце малювання знаходиться в p1, ви можете обчислити контрольні точки безьє до p2 за допомогою наступного псевдокоду:

vec2 p0, p1, p2, p3; // These are calculated with PathMeasure
vec2 cp1 = p1 + (p2 - p0) / 6;
vec2 cp2 = p2 - (p3 - p1) / 6;

За допомогою них ви можете додати шлях від p1 до p2:

path.cubicTo(cp1.x, cp1.y, cp2.x, cp2.y, p2.x, p2.y);

Замініть векторні операції на операції з компонентами на float [ 2 ] масивах, щоб вони відповідали вашому коду. Ви починаєте з ініціалізації, p1 = start;а наступні пункти - p2 та p3. p0 спочатку не визначений. Для першого сегмента, де у вас ще немає p0, ви можете використовувати квадратичну криву від p1 до p2 з cp2 як контрольну точку. Те ж саме для кінця шляху, де у вас немає p3, ви можете намалювати квадратичну криву від p1 до p2 з cp1 в якості контрольної точки. Крім того, ви можете ініціалізувати p0 = p1 для першого сегмента і p3 = p2 для останнього сегмента. Після кожного сегменту ви зміщуєте значення p0 = p1; p1 = p2; and p2 = p3;при русі вперед.

Зберігаючи шлях, ви просто зберігаєте всі точки p0 ... pN. Не потрібно зберігати контрольні точки cp1 та cp2, оскільки їх можна обчислити за потребою.

Edit3: Оскільки, здається, важко отримати хороші вхідні значення для генерації кривих, я пропоную інший підхід: Використовуйте серіалізацію. Android Path, здається, не підтримує це, але, на щастя, клас регіону. Дивіться цю відповідь щодо коду. Це має дати точний результат. Це може зайняти деякий простір у серіалізованому вигляді, якщо воно не оптимізоване, але в цьому випадку він повинен дуже добре стискатися. У Android Java стиснення легко за допомогою GZIPOutputStream .


Це звучить багатообіцяюче. Однак використовуються не p0, а p1, p2, p3, p0 - це лише для зберігання нових визначених точок, коли вони обчислюються, і заради прямих ліній, щоб вони не відбирали вибірку кожного кроку. Чи можете ви допомогти мені, як обчислити х, у для нових контрольних точок?
Луміс

Я міг би зробити це пізніше, але поки перевірити stackoverflow.com/questions/2931573 / ... . За допомогою u і v ви можете отримати точку перетину.
msell

Дякую за допомогу, я хотів би спробувати це, але це потрібно записати на Java для Android. Немає vector2, а t1 і p1 тощо - це плаваючі масиви, тому я не можу робити жодних прямих операцій над ними, як t1 = -t1, або u * t0. Я припускаю, що t1 = -t1 означає t1.x = -t1x; t1.y = -t1.y тощо, правда?
Луміс

Так, це був просто псевдокод, щоб зробити його більш компактним і читабельним.
msell

Ну, сюжет потовщується. Оскільки перетин регіону з двох контурів в Android повертає шлях, який НЕ є антисексуальним, дотичні перебувають над місцем. Таким чином, правильним рішенням буде спочатку провести деяку плавну криву через задані точки, а потім пробити її вибірку. Ваш код прекрасно працює на антисексуальному шляху, він створює належні контрольні точки.
Луміс

13

Що б W3C зробив?

В Інтернеті була така проблема. World Wide Web Consortium помітив. Він має рекомендоване стандартне рішення з 1999 року: масштабована векторна графіка (SVG) . Це формат файлів на основі XML, розроблений спеціально для зберігання 2D фігур.

" Масштабованість - що? "

Масштабована векторна графіка !

  • Масштабованість : призначена для плавного масштабування до будь-якого розміру.
  • Вектор : Це засноване на математичному понятті векторів .
  • Графіка . Це призначено робити фотографії.

Ось технічна специфікація для SVG версії 1.1.
(Не лякайтеся імені; насправді це приємно читати.)

Вони точно записали, як потрібно зберігати основні фігури, такі як кола або прямокутники . Наприклад, прямокутники мають такі властивості: x, y, width, height, rx, ry. (The rxі ryможе бути використана для заокруглених кутів.)

Ось їх приклад прямокутник у SVG: (Ну, два справді - один для контуру полотна.)

<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" 
  "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="12cm" height="4cm" viewBox="0 0 1200 400"
     xmlns="http://www.w3.org/2000/svg" version="1.1">
  <desc>Example rect01 - rectangle with sharp corners</desc>

  <!-- Show outline of canvas using 'rect' element -->
  <rect x="1" y="1" width="1198" height="398"
        fill="none" stroke="blue" stroke-width="2"/>

  <rect x="400" y="100" width="400" height="200"
        fill="yellow" stroke="navy" stroke-width="10"  />
</svg>

Ось що це таке:

жовтий прямокутник із синім контуром

Як говорить специфікація, ви можете залишити деякі властивості, якщо вони вам не потрібні. (Наприклад, rxі ryатрибути не були використані тут.) Так, є тонни непотребу у верхній частині про DOCTYPEяку не потрібно буде тільки для вашої гри. Вони також необов’язкові.

Шляхи

Шляхи SVG - це "стежки" в тому сенсі, що якщо ви покладете олівець на папір, перемістіть його і, врешті-решт, знову підніміть його , у вас є шлях. Вони не повинні бути закриті , але вони могли б бути.

Кожен шлях має dатрибут (мені подобається вважати, що він означає «малювати»), що містить дані про шлях , послідовність команд для того, щоб в основному просто класти ручку на папір і переміщувати її .

Вони наводять приклад трикутника:

<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" 
  "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="4cm" height="4cm" viewBox="0 0 400 400"
     xmlns="http://www.w3.org/2000/svg" version="1.1">
  <title>Example triangle01- simple example of a 'path'</title>
  <desc>A path that draws a triangle</desc>
  <rect x="1" y="1" width="398" height="398"
        fill="none" stroke="blue" />
  <path d="M 100 100 L 300 100 L 200 300 z"
        fill="red" stroke="blue" stroke-width="3" />
</svg>

червоний трикутник

Дивіться dатрибут у path?

d="M 100 100 L 300 100 L 200 300 z"

MЄ командою для Перемістити в ( а потім за координатами), то Ls є для лінії до (з координатами) і zє командою , щоб закрити шлях (тобто намалювати лінію назад до першої локації, то не потрібна координати).

Прямі лінії нудні? Використовуйте кубічні чи квадратичні команди Безьє!

деякі кубічні Безьє

Теорія кривих Безьє висвітлена в інших місцях (наприклад, у Вікіпедії ), але ось резюме: Безьє має початкову та кінцеву точку, можливо, багато контрольних точок, які впливають на те, де крива знаходиться між ними.

простеження квадратичного Безьє

Специфікація також дає вказівки щодо перетворення більшості основних фігур у шляхи, якщо ви хочете.

Чому і коли використовувати SVG

Вирішіть уважно, чи хочете ви піти цим шляхом (каламбур призначений), оскільки представляти будь-яку довільну 2D форму в тексті дуже складно! Ви можете зробити своє життя набагато простішим, якщо, наприклад, обмежитеся лише дорогами, зробленими (можливо, справді багато) прямими.

Але якщо ви все-таки вирішите, що хочете довільних форм, SVG - це шлях: він має чудову підтримку інструментів: Ви можете знайти багато бібліотек для розбору XML на низькому рівні та інструментів редактора SVG на високому рівні.

Незважаючи на те, стандарт SVG є хорошим прикладом.


Питання полягає в перетворенні кривої в точки, а не в збереженні її. Але дякую за це посилання, добре знати про стандарт SVG.
Луміс

@Lumis Назва та зміст пропонують інакше. Поміркуйте над перефразуванням питання. (Або тепер, коли це цілком встановлено, просять іншого.)
Анко

4

Ваш код містить оманливий коментар:

dstPath.quadTo(p2[0] , p2[1], p3[0], p3[1]); //create a curve to the third point through the second

Квадратична крива Безьє не проходить через другу точку. Якщо ви хочете пройти через другу точку, вам потрібен інший тип кривої, наприклад крива герміта . Можливо, ви зможете перетворити криві гермітів у безьє, щоб ви могли використовувати клас Path.

Ще одна пропозиція: замість вибірки балів, використовуйте середнє значення балів, які ви пропускаєте.

Інша пропозиція замість того, щоб використовувати кут як поріг, використовувати різницю між фактичною кривою та приблизною кривою. Кути не є справжньою проблемою; реальна проблема полягає в тому, що набір точок не відповідає кривій Безьє.

Ще одна пропозиція - використовувати кубічні безири, при цьому дотична одна відповідає дотичній наступної. Інакше (з квадратикою) я думаю, що ваші криві не зрівняються.


Ви маєте рацію, друга точка лише «тягне» криву до неї. КубічнийТо вимагає двох контрольних точок замість однієї як quadTo. Проблема, звичайно, у тому, як отримати правильні контрольні точки. Зауважте, що я не хочу втрачати гострі кути, оскільки вихідний Шлях може бути комбінацією будь-якої форми прямою або круглою - в основному я роблю інструмент вибору зображення, де можу зберегти вибраний шлях.
Луміс

4

Щоб отримати більш плавне перехрестя двох шляхів, ви можете їх масштабувати до перехрестя і масштабувати їх після.

Я не знаю, чи це гарне рішення, але це спрацювало добре для мене. Це також швидко. У своєму прикладі я перетинаю округлий шлях із створеним нами малюнком (смугами). Це добре виглядає навіть у масштабах.

Ось мій код:

    Path mypath=new Path(<desiredpath to fill with a pattern>);
    String sPatternType=cpath.getsPattern();

    Path pathtempforbounds=new Path(cpath.getPath());
    RectF rectF = new RectF();
     if (sPatternType.equals("1")){
         turnPath(pathtempforbounds, -45);
     }
     pathtempforbounds.computeBounds(rectF, true);

     float ftop=rectF.top;
     float fbottom=rectF.bottom;
     float fleft=rectF.left;
     float fright=rectF.right;
     float xlength=fright-fleft;

     Path pathpattern=new Path();

     float ypos=ftop;
     float xpos=fleft;

     float fStreifenbreite=4f;

     while(ypos<fbottom){
         pathpattern.moveTo(xpos,ypos);
         xpos=xpos+xlength;
         pathpattern.lineTo(xpos, ypos);
         ypos=ypos+fStreifenbreite;
         pathpattern.lineTo(xpos, ypos);
         xpos=xpos-xlength;
         pathpattern.lineTo(xpos, ypos);
         ypos=ypos-fStreifenbreite;
         pathpattern.lineTo(xpos, ypos);
         pathpattern.close();
         ypos=ypos+2*fStreifenbreite;

     }

     // Original vergrössern

     scalepath(pathpattern,10);
     scalepath(mypath,10);

     if (sPatternType.equals("1")){
         Matrix mdrehen=new Matrix();
         RectF bounds=new RectF();
         pathpattern.computeBounds(bounds, true);
         mdrehen.postRotate(45, (bounds.right + bounds.left)/2,(bounds.bottom + bounds.top)/2);
         pathpattern.transform(mdrehen);
     }

     RectF rectF2 = new RectF();
     mypath.computeBounds(rectF2, true);

     Region clip = new Region();
     clip.set((int)(rectF2.left-100f),(int)(rectF2.top -100f), (int)(rectF2.right+100f),(int)( rectF2.bottom+100f));
     Region region1 = new Region();
     region1.setPath(pathpattern, clip);

     Region region2 = new Region();
     region2.setPath(mypath, clip);

     region1.op(region2, Region.Op.INTERSECT);


     Path pnew=region1.getBoundaryPath();


     scalepath(pnew, 0.1f);
     cpath.setPathpattern(pnew);




public void turnPath(Path p,int idegree){
     Matrix mdrehen=new Matrix();
     RectF bounds=new RectF();
     p.computeBounds(bounds, true);
     mdrehen.postRotate(idegree, (bounds.right + bounds.left)/2,(bounds.bottom + bounds.top)/2);
     p.transform(mdrehen);
}

public void scalepath(Path p,float fscale){
     Matrix mverkleinern=new Matrix();
     mverkleinern.preScale(fscale,fscale);
     p.transform(mverkleinern);
}

введіть тут опис зображення

Виглядає все ще гладко під час масштабування за допомогою canvas.scale (): введіть тут опис зображення


Завдяки тому, хто коли-небудь витратив мені 10 репутації, щоб додати зображення :-)
user1344545

1
Дивовижно, що цей простий трюк вирішує дві проблеми: по-перше, це робить отриманий шлях перетину або об'єднання гладким, а по-друге, мій код у питанні, коли вибірка цього ж масштабованого шляху дає ідеально рівний результат. Яке несподіване та просте рішення, дякую!
Луміс

@user Редагування безкоштовне. Для користувачів <2k-rep це фактично +2.
Анко

@Lumis Я трохи розгублений - я думав, ти запитав, як зберігати доріжки?
Анко

1
На жаль, після більшого тестування я виявив, що оскільки регіон використовує пікселі, до яких траплятиметься шлях, коли малюється, додаток легко втрачає пам'ять, якщо масштабування Шляху велике і робиться повторно. Тож це рішення обмежене та ризикове, але добре пам’ятати.
Луміс

3

Подивіться на інтерполяцію полігона ( http://en.wikipedia.org/wiki/Polynomial_interpolation )

В основному, ви берете n нерозподілених вузлів (оптимальна інтерполяція не є рівномірною, але для вашого випадку вона повинна бути достатньо хорошою і простою у здійсненні)

Ви закінчуєте багатокутник порядку n, який зменшує помилку між кривою, якщо (<- велика, якщо) ваша лінія є досить гладкою .

У вашому випадку ви робите лінійну інтерполяцію (порядок 1).

Іншим випадком (як рекомендував GriffinHeart ) було використання Splines ( http://en.wikipedia.org/wiki/Spline_interpolation )

У будь-якому випадку дасть вам деяку форму полиномиальной підгонки для кривої.


2

Якщо точка перетворення призначена лише для зберігання, і коли ви повертаєте її назад на екрані, вам потрібно, щоб вона була гладкою, то зберігання з найвищою надійністю, яке ви можете отримати, при цьому все ж мінімізуючи загальний обсяг пам’яті, необхідний для збереження заданої кривої, може бути фактично зберігати атрибути кола (або дуги, швидше) та повторно намалювати його на вимогу.

Походження. Радіус. Кути початку / зупинки для малювання дуги.

Якщо вам потрібно все-таки перетворити коло / дугу в точки для рендерингу, то, можливо, ви можете це зробити, завантаживши його зі сховища, зберігаючи лише атрибути.


Шлях / крива джерела може мати будь-яку форму, включаючи малюнок вільної лінії. Я розглядав рішення, яке повинно було би зберегти кожен компонент окремо, а потім об'єднати їх при завантаженні, але це потребує великої роботи, і це уповільнить маніпулювання таким складним об'єктом, оскільки кожна трансформація повинна бути застосована до кожного з його компоненти, щоб можна було зберегти його знову.
Луміс

2

Чи є причина кривих кривих на відміну від прямих? З прямими лініями простіше працювати і їх можна ефективно рендерувати за допомогою апаратних засобів.

Інший підхід, який варто розглянути, - це зберігати пару біт на піксель, вказуючи, чи є вона всередині, зовні або на контуру фігури. Це має добре стискатися та може бути ефективнішим, ніж лінії для складних виділень.

Ці статті також можуть вам бути цікавими / корисними:


1

Погляньте на інтерполяцію кривої - є кілька різних типів, які ви можете реалізувати, що допоможе згладити криву. Чим більше балів ви можете отримати на цьому колі, тим краще. Зберігання досить дешеве - тому якщо витягнути 360 близьких вузлів досить дешево (навіть у 8 байт для позиції; 360 вузлів навряд чи дорого зберігати).

Ви можете розмістити тут кілька інтерполяційних зразків, які мають лише чотири точки; і результати досить непогані (мій фаворит - Безьє для цього випадку, хоча інші можуть наголошувати на інших ефективних рішеннях).

Ви можете пограти і тут .

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.