मैं फ्लोटेन-पॉइंट एंडपॉइंट्स के लिए ब्रेसेनहैम की लाइन एल्गोरिदम को कैसे सामान्य करूं?


12

मैं दो चीजों को मिलाने की कोशिश कर रहा हूं। मैं एक खेल लिख रहा हूं और मुझे फ़्लोटिंग-पॉइंट एंडपॉइंट्स के साथ लाइन पर पड़े ग्रिड वर्गों को निर्धारित करने की आवश्यकता है।

लाइन गर्त ग्रिड

इसके अलावा मुझे इसकी जरूरत है कि इसे छूने वाले सभी ग्रिड वर्गों को शामिल किया जाए (यानी सिर्फ ब्रेसेनहैम की रेखा नहीं बल्कि नीले रंग की)

ब्रेसेनहैम बनाम पूर्ण स्वीप

क्या कोई मुझे यह करने के लिए कोई अंतर्दृष्टि प्रदान कर सकता है? स्पष्ट समाधान भोली रेखा एल्गोरिथ्म का उपयोग करना है, लेकिन क्या कुछ और अधिक अनुकूलित (तेज) है?


यदि लिंक ऑफ़लाइन हो जाता है, तो "पुनरावृत्ति के लिए एक तेज़ स्वर ट्रैवर्सल एल्गोरिथ्म" के लिए बस Google
Gustavo Maciel

जवाबों:


9

आप एक ग्रिड ट्रैवर्सल एल्गोरिथ्म की तलाश कर रहे हैं। यह पेपर एक अच्छा कार्यान्वयन देता है;

यहाँ 2 डी में बुनियादी कार्यान्वयन कागज पर पाया गया है:

loop {
    if(tMaxX < tMaxY) {
        tMaxX= tMaxX + tDeltaX;
        X= X + stepX;
    } else {
        tMaxY= tMaxY + tDeltaY;
        Y= Y + stepY;
    }
    NextVoxel(X,Y);
}

कागज पर एक 3D रे-कास्टिंग संस्करण भी है।

यदि लिंक रॉट के मामले में है , तो आप इसके नाम के साथ कई दर्पण पा सकते हैं: रेराट्रिंग के लिए एक तेज़ स्वर ट्रैवर्सल एल्गोरिथम


खैर, अजीब है। मुझे लगता है कि, मैं आपको जवाब दूंगा और अप-वोट ljjax। क्योंकि मैंने उस पेपर के आपके लिंक के आधार पर हल किया।
स्मार्टके 8

5

ब्लू का विचार अच्छा है, लेकिन कार्यान्वयन थोड़ा अनाड़ी है। वास्तव में, आप इसे sqrt के बिना आसानी से कर सकते हैं। आइए उस क्षण के लिए मान लें कि आप पतित मामलों को छोड़ देते हैं ( BeginX==EndX || BeginY==EndY) और पहले चतुर्थांश में केवल लाइन दिशाओं पर ध्यान केंद्रित करते हैं, इसलिए BeginX < EndX && BeginY < EndY। आपको कम से कम एक अन्य क्वाड्रंट के लिए भी एक संस्करण लागू करना होगा, लेकिन यह पहले क्वाड्रंट के संस्करण के समान है - आप केवल दो किनारों की जांच करते हैं। C'ish छद्म कोड में:

int cx = floor(BeginX); // Begin/current cell coords
int cy = floor(BeginY);
int ex = floor(EndX); // End cell coords
int ey = floor(EndY);

// Delta or direction
double dx = EndX-BeginX;
double dy = EndY-BeginY;

while (cx < ex && cy < ey)
{
  // find intersection "time" in x dir
  float t0 = (ceil(BeginX)-BeginX)/dx;
  float t1 = (ceil(BeginY)-BeginY)/dy;

  visit_cell(cx, cy);

  if (t0 < t1) // cross x boundary first=?
  {
    ++cx;
    BeginX += t0*dx;
    BeginY += t0*dy;
  }
  else
  {
    ++cy;
    BeginX += t1*dx;
    BeginY += t1*dy;
  }
}

अब अन्य क्वैडेंट्स के लिए, आप बस ++cxया ++cyलूप की स्थिति को बदलते हैं। यदि आप टकराव के लिए इसका उपयोग करते हैं, तो आपको संभवतः सभी 4 संस्करणों को लागू करना होगा, अन्यथा आप दो के साथ उचित रूप से आरंभ और समाप्ति बिंदुओं की अदला-बदली कर सकते हैं।


एल्गोरिथ्म Gustavo Maciel प्रदान की एक बिट अधिक कुशल है। यह केवल पहले टीएस को निर्धारित करता है और फिर सेल के आकार के अनुसार 1 या ऊर्ध्वाधर या क्षैतिज और शिफ्ट टीएस को जोड़ता है। लेकिन जैसा कि उन्होंने इसे एक उत्तर में परिवर्तित नहीं किया था, मैं इसे निकटतम उत्तर के रूप में स्वीकार करूंगा।
स्मार्टके 8

3

आपकी धारणा आवश्यक नहीं है कि वे कोशिकाओं को खोजें लेकिन रेखाएँ इस ग्रिड को पार करती हैं।

उदाहरण के लिए आपकी छवि लेने पर हम कोशिकाओं को नहीं, बल्कि इसे पार करने वाली ग्रिड की रेखाओं को उजागर कर सकते हैं:

लाल रेखाएं

यह तब दिखाता है कि यदि यह एक ग्रिड रेखा को पार करता है कि इस रेखा के दोनों ओर कोशिकाएं भरी हुई हैं।

आप यह पता लगाने के लिए कि आपकी फ्लोटिंग पॉइंट लाइन पिक्सल्स पर आपके पॉइंट्स को स्केल करके पार करेगी या नहीं, आप एक इंटरसेक्शन एल्गोरिथम का उपयोग कर सकते हैं। यदि आपके पास फ्लोटिंग निर्देशांक का 1.0: 1 अनुपात है: तो आप सॉर्ट किए जाते हैं और आप इसे सीधे अनुवाद कर सकते हैं। यदि आप अपनी निचली बाईं रेखा (1,7) (2,7) को अपनी रेखा (1.3,6.2) (6.51,2.9) के साथ काटते हैं, तो लाइन सेगमेंट चौराहे एल्गोरिथ्म का उपयोग करके आप देख सकते हैं। http://alienryderflex.com/intersect/

C से C # के कुछ अनुवाद की आवश्यकता होगी लेकिन आप उस पेपर से विचार प्राप्त कर सकते हैं। लिंक टूटने की स्थिति में मैं नीचे कोड डालूँगा।

//  public domain function by Darel Rex Finley, 2006

//  Determines the intersection point of the line defined by points A and B with the
//  line defined by points C and D.
//
//  Returns YES if the intersection point was found, and stores that point in X,Y.
//  Returns NO if there is no determinable intersection point, in which case X,Y will
//  be unmodified.

bool lineIntersection(
double Ax, double Ay,
double Bx, double By,
double Cx, double Cy,
double Dx, double Dy,
double *X, double *Y) {

  double  distAB, theCos, theSin, newX, ABpos ;

  //  Fail if either line is undefined.
  if (Ax==Bx && Ay==By || Cx==Dx && Cy==Dy) return NO;

  //  (1) Translate the system so that point A is on the origin.
  Bx-=Ax; By-=Ay;
  Cx-=Ax; Cy-=Ay;
  Dx-=Ax; Dy-=Ay;

  //  Discover the length of segment A-B.
  distAB=sqrt(Bx*Bx+By*By);

  //  (2) Rotate the system so that point B is on the positive X axis.
  theCos=Bx/distAB;
  theSin=By/distAB;
  newX=Cx*theCos+Cy*theSin;
  Cy  =Cy*theCos-Cx*theSin; Cx=newX;
  newX=Dx*theCos+Dy*theSin;
  Dy  =Dy*theCos-Dx*theSin; Dx=newX;

  //  Fail if the lines are parallel.
  if (Cy==Dy) return NO;

  //  (3) Discover the position of the intersection point along line A-B.
  ABpos=Dx+(Cx-Dx)*Dy/(Dy-Cy);

  //  (4) Apply the discovered position to line A-B in the original coordinate system.
  *X=Ax+ABpos*theCos;
  *Y=Ay+ABpos*theSin;

  //  Success.
  return YES; }

यदि आपको यह पता लगाना है कि लाइन सेगमेंट्स को कब (और कहाँ) करना है, तो आप फ़ंक्शन को निम्नानुसार संशोधित कर सकते हैं:

//  public domain function by Darel Rex Finley, 2006  

//  Determines the intersection point of the line segment defined by points A and B
//  with the line segment defined by points C and D.
//
//  Returns YES if the intersection point was found, and stores that point in X,Y.
//  Returns NO if there is no determinable intersection point, in which case X,Y will
//  be unmodified.

bool lineSegmentIntersection(
double Ax, double Ay,
double Bx, double By,
double Cx, double Cy,
double Dx, double Dy,
double *X, double *Y) {

  double  distAB, theCos, theSin, newX, ABpos ;

  //  Fail if either line segment is zero-length.
  if (Ax==Bx && Ay==By || Cx==Dx && Cy==Dy) return NO;

  //  Fail if the segments share an end-point.
  if (Ax==Cx && Ay==Cy || Bx==Cx && By==Cy
  ||  Ax==Dx && Ay==Dy || Bx==Dx && By==Dy) {
    return NO; }

  //  (1) Translate the system so that point A is on the origin.
  Bx-=Ax; By-=Ay;
  Cx-=Ax; Cy-=Ay;
  Dx-=Ax; Dy-=Ay;

  //  Discover the length of segment A-B.
  distAB=sqrt(Bx*Bx+By*By);

  //  (2) Rotate the system so that point B is on the positive X axis.
  theCos=Bx/distAB;
  theSin=By/distAB;
  newX=Cx*theCos+Cy*theSin;
  Cy  =Cy*theCos-Cx*theSin; Cx=newX;
  newX=Dx*theCos+Dy*theSin;
  Dy  =Dy*theCos-Dx*theSin; Dx=newX;

  //  Fail if segment C-D doesn't cross line A-B.
  if (Cy<0. && Dy<0. || Cy>=0. && Dy>=0.) return NO;

  //  (3) Discover the position of the intersection point along line A-B.
  ABpos=Dx+(Cx-Dx)*Dy/(Dy-Cy);

  //  Fail if segment C-D crosses line A-B outside of segment A-B.
  if (ABpos<0. || ABpos>distAB) return NO;

  //  (4) Apply the discovered position to line A-B in the original coordinate system.
  *X=Ax+ABpos*theCos;
  *Y=Ay+ABpos*theSin;

  //  Success.
  return YES; }

नमस्ते, ग्रिड ट्रैवर्सल बिल्कुल ग्रिड के हजारों लाइन चौराहों के अनुकूलन के उद्देश्य से है। यह हजारों लाइन चौराहों द्वारा हल नहीं किया जा सकता है। मेरे पास ग्राउंड लाइनों के साथ एक गेम है जिसमें खिलाड़ी पार नहीं कर सकता है। इनमें से हजारों हो सकते हैं। मुझे यह निर्धारित करने की आवश्यकता है कि किसके लिए महंगी चौराहे की गणना करना है। ये निर्धारित करने के लिए मैं केवल खिलाड़ी आंदोलन (या प्रकाश स्रोत से प्रकाश) की कतार में उन लोगों के चौराहों की गणना करना चाहता हूं। आपके मामले में मुझे प्रत्येक दौर में ~ 256x256x2 लाइन सेगमेंट वाले चौराहों को निर्धारित करने की आवश्यकता होगी। यह बिल्कुल भी अनुकूलित नहीं होगा।
स्मार्टके 8

लेकिन फिर भी आपको जवाब देने के लिए धन्यवाद। तकनीकी रूप से यह काम करता है और सही है। लेकिन सिर्फ मेरे लिए संभव नहीं है।
20K में स्मार्टके 8

3
float difX = end.x - start.x;
float difY = end.y - start.y;
float dist = abs(difX) + abs(difY);

float dx = difX / dist;
float dy = difY / dist;

for (int i = 0, int x, int y; i <= ceil(dist); i++) {
    x = floor(start.x + dx * i);
    y = floor(start.y + dy * i);
    draw(x,y);
}
return true;

जेएस डेमो:

Imgur


1
फ़्लोटिंग पॉइंट संख्यात्मक त्रुटियों के कारण यह मेरे लिए विफल रहा (लूप अगले पूर्णांक पर सबसे छोटे अंश के लिए एक अतिरिक्त पुनरावृत्ति करेगा, जो 'एंड' लोकेशन से परे लाइन एंड-पॉइंट को धक्का देगा)। पहली जगह में एक छत के रूप में डिस्टिल की गणना करने के लिए सरल फिक्स है ताकि dx, डाई लूप के पुनरावृत्तियों की पूर्णांक संख्या से विभाजित हो (इसका मतलब है कि आप लूप के लिए छत (डिस्ट) को खो सकते हैं)।
पेटेबी

0

मैं आज उसी समस्या में भाग गया और मोल पहाड़ी के बाहर स्पेगेटी का एक बहुत बड़ा पहाड़ बना दिया, लेकिन कुछ इस तरह से काम करता है: https://github.com/SnpM/Pan-Line-Al एल्गोरिदम

ReadMe से:

इस एल्गोरिथ्म की मुख्य अवधारणा ब्रेसेनहैम के समान है कि यह एक धुरी पर 1 इकाई द्वारा वृद्धि करता है और अन्य अक्ष पर वृद्धि का परीक्षण करता है। भिन्नों में वृद्धि काफी कठिन होती है, हालाँकि, और बहुत सारे पिज़ा को जोड़ना पड़ता था। उदाहरण के लिए, 5 की ढलान के साथ X = .21 से X = 1.21 तक की वृद्धि एक जटिल समस्या के लिए बनाता है (उन गंदी संख्याओं के बीच समन्वय पैटर्न। भविष्यवाणी करना कठिन है) लेकिन 5 की ढलान के साथ 1 से 2 तक बढ़ाना एक आसान समस्या है। पूर्णांकों के बीच समन्वयित पैटर्न को हल करना बहुत आसान है (केवल एक रेखा सीधा होने वाली धुरी के लिए लंबवत)। आसान समस्या प्राप्त करने के लिए, वेतन वृद्धि एक पूर्णांक संख्या की भरपाई होती है, जिसमें सभी अंश भिन्नात्मक भाग के लिए अलग-अलग किए जाते हैं। इसलिए वेतन वृद्धि शुरू करने के बजाय ।21।

ReadMe कोड की तुलना में समाधान को बेहतर तरीके से समझाता है। मैं इसे कम सिरदर्द-उत्प्रेरण के रूप में संशोधित करने की योजना बना रहा हूं।

मुझे पता है कि मुझे इस सवाल के बारे में एक साल देर हो चुकी है, लेकिन मुझे उम्मीद है कि यह उन लोगों को भी मिलेगा जो इस समस्या का हल ढूंढ रहे हैं।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.