आप एक बिंदु का निर्धारण कैसे कर सकते हैं एक लाइन खंड पर दो अन्य बिंदुओं के बीच?


94

मान लें कि आपके पास 2 बिंदुओं वाला एक दो आयामी विमान है (जिसे a और b कहा जाता है) प्रत्येक बिंदु पर x पूर्णांक और ay पूर्णांक द्वारा दर्शाया गया है।

आप यह निर्धारित कर सकते हैं कि a और b द्वारा परिभाषित रेखा खंड पर कोई अन्य बिंदु c है?

मैं अजगर का सबसे अधिक उपयोग करता हूं, लेकिन किसी भी भाषा में उदाहरण सहायक होंगे।


4
मुझे इन उत्तरों में बहुत सारी लंबाई = sqrt (x) सामान दिखाई दे रहा है; वे काम कर सकते हैं, लेकिन वे तेज नहीं हैं। लंबाई-वर्ग का उपयोग करने पर विचार करें; यदि आप केवल एक-दूसरे की लंबाई के मानों की तुलना कर रहे हैं, तो सटीकता का कोई नुकसान नहीं है, और आप sqrt () को धीमी कॉल बचाते हैं।
ओज्रेक 30'08

1
क्या बिंदु c को 2 पूर्णांकों द्वारा भी दर्शाया गया है? यदि हां, तो क्या आप जानना चाहते हैं कि क्या c और b के बीच वास्तविक स्ट्रेट लाइन के साथ c बिल्कुल है या a और b के बीच स्ट्रेट लाइन के रेखीय सन्निकटन पर स्थित है? यह एक महत्वपूर्ण स्पष्टीकरण है।
रोब्स

इसी तरह का एक सवाल यहां पूछा गया था: stackoverflow.com/q/31346862/1914034 एक समाधान के साथ जब लाइन से एक बफर दूरी की आवश्यकता होती है
राडार


1
भविष्य के पाठकों को चेतावनी: उचित संख्या में उत्तर गलत या अपूर्ण हैं। कुछ किनारे मामले जो अक्सर काम नहीं करते हैं वे क्षैतिज और ऊर्ध्वाधर रेखाएं हैं।
स्टेफनोट

जवाबों:


127

जांचें कि क्या (बा) और (सीए) का क्रॉस उत्पाद है, जैसा कि डेरियस बेकन बताता है, अगर आपको बताते हैं कि अंक ए, बी और सी संरेखित हैं।

लेकिन, जैसा कि आप जानना चाहते हैं कि यदि c और b के बीच में है, तो आपको यह भी जांचना होगा कि (ba) और (ca) का डॉट उत्पाद धनात्मक है और a और b के बीच की दूरी के वर्ग से कम है।

गैर-अनुकूलित छद्मकोड में:

def isBetween(a, b, c):
    crossproduct = (c.y - a.y) * (b.x - a.x) - (c.x - a.x) * (b.y - a.y)

    # compare versus epsilon for floating point values, or != 0 if using integers
    if abs(crossproduct) > epsilon:
        return False

    dotproduct = (c.x - a.x) * (b.x - a.x) + (c.y - a.y)*(b.y - a.y)
    if dotproduct < 0:
        return False

    squaredlengthba = (b.x - a.x)*(b.x - a.x) + (b.y - a.y)*(b.y - a.y)
    if dotproduct > squaredlengthba:
        return False

    return True

5
-epsilon < crossproduct < epsilon and min(a.x, b.x) <= c.x <= max(a.x, b.x) and min(a.y, b.y) <= c.y <= max(a.y, b.y)पर्याप्त है, है ना?
jfs

9
क्रॉसप्रोक्ट का निरपेक्ष मान तीन बिंदुओं के साथ बने त्रिभुज के क्षेत्रफल का दोगुना है (संकेत के साथ-साथ तीसरा बिंदु) ताकि IMHO आपको एक एप्सिलॉन का उपयोग करना चाहिए जो दो समापन बिंदुओं के बीच की दूरी के लिए आनुपातिक है।
बार्ट

2
क्या आप हमें बता सकते हैं कि यह पूर्णांक के साथ क्यों काम नहीं करेगा? मुझे समस्या दिखाई नहीं देती, बशर्ते कि एप्सिलॉन चेक को "! = 0" से बदल दिया जाए।
साइरिल का

2
हां, अतिरिक्त कोष्ठक सिर्फ एक टाइपो है। किसी को कुछ कहने से पहले 4 साल बीत चुके हैं। :)
साइरिल का

4
आपको यह स्पष्ट करने के लिए a, b, c का नाम बदलना चाहिए कि कौन से खंड अंत बिंदु हैं और कौन सा क्वेरी बिंदु है।
क्रेग गिदनी

48

यहाँ है कि मैं यह कैसे करूँगा:

def distance(a,b):
    return sqrt((a.x - b.x)**2 + (a.y - b.y)**2)

def is_between(a,c,b):
    return distance(a,c) + distance(c,b) == distance(a,b)

7
यह एक सुरुचिपूर्ण समाधान है।
पॉल डी। ईडन

6
इसके साथ एकमात्र समस्या संख्यात्मक स्थिरता है - संख्याओं के अंतर को लेना और इसी तरह सटीकता खोना उपयुक्त है।
जोनाथन लेफलर

26
-epsilon < (distance(a, c) + distance(c, b) - distance(a, b)) < epsilon
1

1
@jfs आपका क्या मतलब है? के लिए एप्सिलॉन के साथ जाँच करें?
नियॉन वारगे

3
@ निऑनर्ज: sqrt () एक फ्लोट देता है। ==ज्यादातर मामलों में तैरने के लिए एक गलत बात हैmath.isclose()इसके बजाय इस्तेमाल किया जा सकता है। कोई था math.isclose()2008 में और इसलिए मैं के साथ स्पष्ट असमानता प्रदान की है epsilon( abs_tolमें math.isclose()बात)।
jfs

36

देखें कि क्या के पार उत्पाद b-aऔर c-aहै 0: साधन सभी बिंदुओं समरेख हैं कि। यदि वे हैं, तो जांचें कि क्या cनिर्देशांक a's और s के बीच का है b। जब तक x या y निर्देशांक का उपयोग करें, तब तक aऔर bउस अक्ष पर अलग होते हैं (या वे दोनों पर समान हैं)।

def is_on(a, b, c):
    "Return true iff point c intersects the line segment from a to b."
    # (or the degenerate case that all 3 points are coincident)
    return (collinear(a, b, c)
            and (within(a.x, c.x, b.x) if a.x != b.x else 
                 within(a.y, c.y, b.y)))

def collinear(a, b, c):
    "Return true iff a, b, and c all lie on the same line."
    return (b.x - a.x) * (c.y - a.y) == (c.x - a.x) * (b.y - a.y)

def within(p, q, r):
    "Return true iff q is between p and r (inclusive)."
    return p <= q <= r or r <= q <= p

यह उत्तर तीन अपडेट की गड़बड़ी हुआ करता था। उनसे सार्थक जानकारी: सुंदर कोड में ब्रायन हेस का अध्याय एक कोलीनियरिटी-परीक्षण फ़ंक्शन के लिए डिज़ाइन स्थान को कवर करता है - उपयोगी पृष्ठभूमि। विंसेंट के जवाब ने इसे बेहतर बनाने में मदद की। और यह हेस था जिसने एक्स या वाई निर्देशांक में से केवल एक का परीक्षण करने का सुझाव दिया था; मूल रूप से कोड के स्थान पर था ।andif a.x != b.x else


चूंकि सीमा की जाँच तेजी से होती है इसलिए बेहतर होगा कि पहले चेक की सीमा तय की जाए और उसके बाद बाउंडिंग बॉक्स में कोलियर की जांच की जाए।
ग्रांट एम

1
इस मामले के लिए फ़ंक्शन__ (ए, बी, सी) गलत है, जहां == बी = = सी। ऐसे मामले में यह सच हो जाएगा, भले ही सी एक से बी से लाइन सेगमेंट को इंटरसेप्ट नहीं करता है।
मिको विर्ककिला

@SuperFlux, मैंने बस उसे चलाने की कोशिश की, और गलत मिला।
डेरियस बेकन

2
मुझे लगता है कि वर्तमान स्वीकृत उत्तर से यह उत्तर स्पष्ट रूप से बेहतर है।
रिक

1
Collinear (ए, बी, सी) समानता द्वारा फ्लोटिंग पॉइंट नंबरों का परीक्षण कर रहा है। यह एक epsilon / सहिष्णुता का उपयोग नहीं करना चाहिए?
jwezorek

7

यहाँ एक और दृष्टिकोण है:

  • मान लें कि दो बिंदु A (X1, y1) और B (x2, y2) हैं
  • उन बिंदुओं से गुजरने वाली रेखा का समीकरण है (x-X1) / (y-y1) = (x2-X1) / (y2-y1) .. (बस ढलान को समान बनाते हुए)

बिंदु C (x3, y3) A & B के बीच स्थित होगा यदि:

  • x3, y3 उपरोक्त समीकरण को संतुष्ट करता है।
  • x3 झूठ 1 और x2 और y3 के बीच y1 और y2 के बीच स्थित है (तुच्छ जांच)

यह गोल त्रुटियों (निर्देशांक की अक्षमता) को ध्यान में नहीं रखता है।
बार्ट

यह सही विचार है, मुझे लगता है, लेकिन विस्तार पर संक्षिप्त (हम व्यवहार में उस समीकरण की जांच कैसे करते हैं?) और थोड़ा छोटी गाड़ी: अंतिम y3 को y2 होना चाहिए।
डेरियस बेकन

@ दारा: तय किया कि टाइपो
हार्ले होल्कोम्बे

7

खंड की लंबाई महत्वपूर्ण नहीं है, इस प्रकार एक वर्गमूल का उपयोग करने की आवश्यकता नहीं है और इससे बचा जाना चाहिए क्योंकि हम कुछ सटीक खो सकते हैं।

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

class Segment:
    def __init__(self, a, b):
        self.a = a
        self.b = b

    def is_between(self, c):
        # Check if slope of a to c is the same as a to b ;
        # that is, when moving from a.x to c.x, c.y must be proportionally
        # increased than it takes to get from a.x to b.x .

        # Then, c.x must be between a.x and b.x, and c.y must be between a.y and b.y.
        # => c is after a and before b, or the opposite
        # that is, the absolute value of cmp(a, b) + cmp(b, c) is either 0 ( 1 + -1 )
        #    or 1 ( c == a or c == b)

        a, b = self.a, self.b             

        return ((b.x - a.x) * (c.y - a.y) == (c.x - a.x) * (b.y - a.y) and 
                abs(cmp(a.x, c.x) + cmp(b.x, c.x)) <= 1 and
                abs(cmp(a.y, c.y) + cmp(b.y, c.y)) <= 1)

उपयोग के कुछ यादृच्छिक उदाहरण:

a = Point(0,0)
b = Point(50,100)
c = Point(25,50)
d = Point(0,8)

print Segment(a,b).is_between(c)
print Segment(a,b).is_between(d)

1
Cx या cy नाव तो पहले कर रहे हैं ==में is_between()विफल हो सकता है (btw यह भेस में एक crossproduct है)।
jfs

में जोड़ें is_between():a, b = self.a, self.b
jfs

ऐसा लगता है कि अगर तीनों बिंदु समान हैं (जो कि सभी सही, इहो हैं) तो सही होगा लेकिन यदि दो बिंदु समान हैं - तो झूठ को परिभाषित करने का एक बहुत ही असंगत तरीका है। मैंने अपने उत्तर में एक विकल्प पोस्ट किया।
दारिस बेकन

तय है कि एक और सीएमपी ट्रिक द्वारा, लेकिन इस कोड से बदबू आने लगती है ;-)
vincent

5

C ++ में दिए गए कोड के साथ, इसके बारे में जाने का एक अलग तरीका है। दो बिंदुओं को देखते हुए, l1 और l2 यह उनके बीच के रेखा खंड को व्यक्त करने के लिए मामूली है

l1 + A(l2 - l1)

जहां 0 <= A <= 1. यह एक पंक्ति के वेक्टर प्रतिनिधित्व के रूप में जाना जाता है यदि आप इस समस्या के लिए इसका उपयोग करने से परे किसी भी अधिक रुचि रखते हैं। हम इसके x और y घटकों को विभाजित कर सकते हैं:

x = l1.x + A(l2.x - l1.x)
y = l1.y + A(l2.y - l1.y)

एक बिंदु (एक्स, वाई) लें और ए के लिए हल करने के लिए अपने एक्स और वाई घटकों को इन दो अभिव्यक्तियों में स्थानापन्न करें। बिंदु उस रेखा पर है यदि दोनों अभिव्यक्तियों में ए के लिए समाधान समान हैं और 0 <= ए <= 1. क्योंकि A के लिए हल करने के लिए विभाजन की आवश्यकता होती है, ऐसे विशेष मामले होते हैं जिन्हें लाइन खंड के क्षैतिज या ऊर्ध्वाधर होने पर शून्य से विभाजन को रोकने के लिए हैंडलिंग की आवश्यकता होती है। अंतिम समाधान इस प्रकार है:

// Vec2 is a simple x/y struct - it could very well be named Point for this use

bool isBetween(double a, double b, double c) {
    // return if c is between a and b
    double larger = (a >= b) ? a : b;
    double smaller = (a != larger) ? a : b;

    return c <= larger && c >= smaller;
}

bool pointOnLine(Vec2<double> p, Vec2<double> l1, Vec2<double> l2) {
    if(l2.x - l1.x == 0) return isBetween(l1.y, l2.y, p.y); // vertical line
    if(l2.y - l1.y == 0) return isBetween(l1.x, l2.x, p.x); // horizontal line

    double Ax = (p.x - l1.x) / (l2.x - l1.x);
    double Ay = (p.y - l1.y) / (l2.y - l1.y);

    // We want Ax == Ay, so check if the difference is very small (floating
    // point comparison is fun!)

    return fabs(Ax - Ay) < 0.000001 && Ax >= 0.0 && Ax <= 1.0;
}

4

अधिक ज्यामितीय दृष्टिकोण का उपयोग करते हुए, निम्नलिखित दूरियों की गणना करें:

ab = sqrt((a.x-b.x)**2 + (a.y-b.y)**2)
ac = sqrt((a.x-c.x)**2 + (a.y-c.y)**2)
bc = sqrt((b.x-c.x)**2 + (b.y-c.y)**2)

और परीक्षण है कि क्या एसी + बीसी के बराबर होती है अब :

is_on_segment = abs(ac + bc - ab) < EPSILON

ऐसा इसलिए है क्योंकि तीन संभावनाएँ हैं:

  • 3 बिंदु एक त्रिकोण बनाते हैं => एसी + बीसी> एबी
  • वे कोलिनियर हैं और c , ab सेगमेंट के बाहर है => ac + bc> ab
  • वे समतल होते हैं और c , ab खंड => ac + bc = ab के अंदर होता है

जैसा कि जोनाथन लेफ़लर ने एक अन्य टिप्पणी में उल्लेख किया है, इसमें संख्यात्मक मुद्दे हैं जो अन्य दृष्टिकोण जैसे क्रॉस-प्रोडक्ट से बचते हैं। मेरे उत्तर में जो अध्याय मैंने लिंक किया है, वह बताता है।
डेरियस बेकन

3

ठीक है, रैखिक बीजगणित (वैक्टर के क्रॉस उत्पाद) के बहुत सारे उल्लेख और यह एक वास्तविक (निरंतर या अस्थायी बिंदु) स्थान में काम करता है लेकिन सवाल विशेष रूप से कहा गया है कि दो बिंदु पूर्णांक के रूप में व्यक्त किए गए थे और इस तरह एक क्रॉस उत्पाद सही नहीं है समाधान हालांकि यह एक अनुमानित समाधान दे सकता है।

सही समाधान दो बिंदुओं के बीच में ब्रेसेनहैम की रेखा एल्गोरिथम का उपयोग करना है और यह देखने के लिए कि क्या तीसरा बिंदु रेखा पर बिंदुओं में से एक है। यदि अंक पर्याप्त रूप से दूर हैं कि एल्गोरिथ्म की गणना गैर-निष्पादित है (और यह वास्तव में इसके लिए बहुत बड़ा होना चाहिए) तो मुझे यकीन है कि आप चारों ओर खुदाई कर सकते हैं और अनुकूलन पा सकते हैं।


यह दो मध्यस्थ बिंदुओं के बीच एक द्वि-आयामी पूर्णांक स्थान के माध्यम से एक रेखा खींचने का तरीका हल करता है और इसकी गणितीय रूप से सही है। यदि तीसरा बिंदु उस रेखा के बिंदुओं में से एक है, तो यह उन दो बिंदुओं के बीच की परिभाषा है।
cletus

1
नहीं, ब्रेसेनहैम की रेखा एल्गोरिथ्म हल करती है कि दो-आयामी पूर्णांक अंतरिक्ष में रेखा खंड का एक सन्निकटन कैसे बनाया जाए । मैं मूल पोस्टर के संदेश से नहीं देखता हूं कि यह रैस्टोरेशन के बारे में एक प्रश्न था।
साइरिल का

"मान लें कि आपके पास दो बिंदुओं के साथ 2 अंक (a और b) के साथ एक दो आयामी विमान है जो प्रत्येक बिंदु के लिए x INTEGER और ay INTEGER द्वारा प्रस्तुत किया गया है।" (मेरे द्वारा जोर दिया गया)।
cletus

1
मुझे लगता है कि ब्रेसेनहैम की लाइन एल्गोरिदम एक लाइन को कोठरी पूर्णांक अंक देता है, जो तब रेखा खींचने के लिए उपयोग किया जा सकता है। वे लाइन पर नहीं हो सकते हैं। उदाहरण के लिए यदि (0,0) से (11,13) एल्गोरिथ्म ड्रॉ करने के लिए एक नंबर पिक्सेल देगा, लेकिन अंतिम बिंदुओं को छोड़कर कोई पूर्णांक बिंदु नहीं हैं, क्योंकि 11 और 13 कोप्रेम हैं।
अनुदान

वास्तविक अंतरिक्ष (can × not) के लिए सही होने वाला समाधान पूर्णांक स्थान (, × ℕ∈ℝ) के लिए सही नहीं हो सकता है, जैसा कि is। या क्या आपका मतलब है: "के लिए इष्टतम नहीं है ..." के बजाय 'सही नहीं है?
आइडोग्राम

2

(सीए) और (बा) के बीच का स्केलर उत्पाद उनकी लंबाई के उत्पाद के बराबर होना चाहिए (इसका मतलब है कि वैक्टर (सीए) और (बीए) एक ही दिशा के साथ गठबंधन किए गए हैं)। इसके अलावा, (सीए) की लंबाई (बा) के बराबर या उससे कम होनी चाहिए। स्यूडोकोड:

# epsilon = small constant

def isBetween(a, b, c):
    lengthca2  = (c.x - a.x)*(c.x - a.x) + (c.y - a.y)*(c.y - a.y)
    lengthba2  = (b.x - a.x)*(b.x - a.x) + (b.y - a.y)*(b.y - a.y)
    if lengthca2 > lengthba2: return False
    dotproduct = (c.x - a.x)*(b.x - a.x) + (c.y - a.y)*(b.y - a.y)
    if dotproduct < 0.0: return False
    if abs(dotproduct*dotproduct - lengthca2*lengthba2) > epsilon: return False 
    return True

क्या अंतिम स्थिति अधिक नहीं होनी चाहिए: ABS (उत्पाद - लम्बाई * लंबाई) <एप्सिलॉन?
जोनाथन लेफलर

क्या आपको इसके बजाय चौकोर लंबाई की तुलना नहीं करनी चाहिए? वर्गमूल से बचना है। इसके अलावा, यदि यह अतिप्रवाह के कारण अपरिहार्य है, तो आप math.sqrt के बजाय math.hypot का उपयोग कर सकते हैं (तर्कों के उपयुक्त परिवर्तन के साथ)।
डेरियस बेकन

मुझे आश्चर्य है कि एप्सिलॉन के बारे में भी। क्या आप इसे समझा सकते हैं? बेशक, अगर हमें फ्लोट्स से निपटना चाहिए, तो हमें तुलनाओं के बारे में सावधान रहना चाहिए, लेकिन यह मेरे लिए स्पष्ट नहीं है कि एप्सिलॉन इस विशेष तुलना को अधिक सटीक क्यों बनाता है।
डेरियस बेकन

मैं सहमत हूँ। इस प्रश्न के कई अच्छे उत्तर हैं, और यह एक ठीक है। लेकिन इस कोड को sqrt और अंतिम तुलना तय नहीं करने के लिए संशोधित करने की आवश्यकता है।
साइरिल का

@ जोनाथन: वास्तव में एब्स का उपयोग करके कोड अधिक परिचित और सुरुचिपूर्ण है। धन्यवाद।
फेडरिको ए। रम्पोनी

2

मुझे यह पता लगाने के लिए जावास्क्रिप्ट की आवश्यकता थी कि उपयोगकर्ता कर्सर एक निश्चित रेखा के ऊपर है या नहीं। इसलिए मैंने दारा बेकन द्वारा दिए गए जवाब को ताबूत में बदल दिया:

is_on = (a,b,c) ->
    # "Return true if point c intersects the line segment from a to b."
    # (or the degenerate case that all 3 points are coincident)
    return (collinear(a,b,c) and withincheck(a,b,c))

withincheck = (a,b,c) ->
    if a[0] != b[0]
        within(a[0],c[0],b[0]) 
    else 
        within(a[1],c[1],b[1])

collinear = (a,b,c) ->
    # "Return true if a, b, and c all lie on the same line."
    ((b[0]-a[0])*(c[1]-a[1]) < (c[0]-a[0])*(b[1]-a[1]) + 1000) and ((b[0]-a[0])*(c[1]-a[1]) > (c[0]-a[0])*(b[1]-a[1]) - 1000)

within = (p,q,r) ->
    # "Return true if q is between p and r (inclusive)."
    p <= q <= r or r <= q <= p

2

आप कील और डॉट उत्पाद का उपयोग कर सकते हैं:

def dot(v,w): return v.x*w.x + v.y*w.y
def wedge(v,w): return v.x*w.y - v.y*w.x

def is_between(a,b,c):
   v = a - b
   w = b - c
   return wedge(v,w) == 0 and dot(v,w) > 0

1

यहां मैंने इसे स्कूल में कैसे किया। मैं भूल गया कि यह एक अच्छा विचार क्यों नहीं है।

संपादित करें:

@ डैरिक बेकन: एक "ब्यूटीफुल कोड" किताब का हवाला देते हैं जिसमें एक स्पष्टीकरण है कि नीचे दिया गया कोड अच्छा विचार क्यों नहीं है।

#!/usr/bin/env python
from __future__ import division

epsilon = 1e-6

class Point:
    def __init__(self, x, y):
        self.x, self.y = x, y

class LineSegment:
    """
    >>> ls = LineSegment(Point(0,0), Point(2,4))
    >>> Point(1, 2) in ls
    True
    >>> Point(.5, 1) in ls
    True
    >>> Point(.5, 1.1) in ls
    False
    >>> Point(-1, -2) in ls
    False
    >>> Point(.1, 0.20000001) in ls
    True
    >>> Point(.1, 0.2001) in ls
    False
    >>> ls = LineSegment(Point(1, 1), Point(3, 5))
    >>> Point(2, 3) in ls
    True
    >>> Point(1.5, 2) in ls
    True
    >>> Point(0, -1) in ls
    False
    >>> ls = LineSegment(Point(1, 2), Point(1, 10))
    >>> Point(1, 6) in ls
    True
    >>> Point(1, 1) in ls
    False
    >>> Point(2, 6) in ls 
    False
    >>> ls = LineSegment(Point(-1, 10), Point(5, 10))
    >>> Point(3, 10) in ls
    True
    >>> Point(6, 10) in ls
    False
    >>> Point(5, 10) in ls
    True
    >>> Point(3, 11) in ls
    False
    """
    def __init__(self, a, b):
        if a.x > b.x:
            a, b = b, a
        (self.x0, self.y0, self.x1, self.y1) = (a.x, a.y, b.x, b.y)
        self.slope = (self.y1 - self.y0) / (self.x1 - self.x0) if self.x1 != self.x0 else None

    def __contains__(self, c):
        return (self.x0 <= c.x <= self.x1 and
                min(self.y0, self.y1) <= c.y <= max(self.y0, self.y1) and
                (not self.slope or -epsilon < (c.y - self.y(c.x)) < epsilon))

    def y(self, x):        
        return self.slope * (x - self.x0) + self.y0

if __name__ == '__main__':
    import  doctest
    doctest.testmod()

1

लाइन खंड पर कोई बिंदु ( , बी ) (जहां और बी वैक्टर हैं) को दो वैक्टर और बी के रैखिक संयोजन के रूप में व्यक्त किया जा सकता है :

दूसरे शब्दों में, यदि c रेखाखंड पर स्थित है ( a , b ):

c = ma + (1 - m)b, where 0 <= m <= 1

मीटर के लिए हल , हम प्राप्त करते हैं:

m = (c.x - b.x)/(a.x - b.x) = (c.y - b.y)/(a.y - b.y)

तो, हमारा परीक्षण (पायथन में) हो गया:

def is_on(a, b, c):
    """Is c on the line segment ab?"""

    def _is_zero( val ):
        return -epsilon < val < epsilon

    x1 = a.x - b.x
    x2 = c.x - b.x
    y1 = a.y - b.y
    y2 = c.y - b.y

    if _is_zero(x1) and _is_zero(y1):
        # a and b are the same point:
        # so check that c is the same as a and b
        return _is_zero(x2) and _is_zero(y2)

    if _is_zero(x1):
        # a and b are on same vertical line
        m2 = y2 * 1.0 / y1
        return _is_zero(x2) and 0 <= m2 <= 1
    elif _is_zero(y1):
        # a and b are on same horizontal line
        m1 = x2 * 1.0 / x1
        return _is_zero(y2) and 0 <= m1 <= 1
    else:
        m1 = x2 * 1.0 / x1
        if m1 < 0 or m1 > 1:
            return False
        m2 = y2 * 1.0 / y1
        return _is_zero(m2 - m1)

1

c # से http://www.faqs.org/faqs/graphics/algorithms-faq/ -> विषय 1.02: मैं एक बिंदु से एक पंक्ति की दूरी कैसे पता करूँ?

Boolean Contains(PointF from, PointF to, PointF pt, double epsilon)
        {

            double segmentLengthSqr = (to.X - from.X) * (to.X - from.X) + (to.Y - from.Y) * (to.Y - from.Y);
            double r = ((pt.X - from.X) * (to.X - from.X) + (pt.Y - from.Y) * (to.Y - from.Y)) / segmentLengthSqr;
            if(r<0 || r>1) return false;
            double sl = ((from.Y - pt.Y) * (to.X - from.X) - (from.X - pt.X) * (to.Y - from.Y)) / System.Math.Sqrt(segmentLengthSqr);
            return -epsilon <= sl && sl <= epsilon;
        }

अधिकांश अन्य तरीकों में सटीक समस्याओं से बचने का सही तरीका। इसके अलावा काफी अधिक कुशल है कि सबसे अन्य approraches।
रॉबिन डेविस

1

यहाँ कुछ जावा कोड है जो मेरे लिए काम करते हैं:

boolean liesOnSegment(Coordinate a, Coordinate b, Coordinate  c) {

    double dotProduct = (c.x - a.x) * (c.x - b.x) + (c.y - a.y) * (c.y - b.y);
    if (dotProduct < 0) return true;
    return false;
}

1
dotProduct केवल संरेखण के बारे में बता सकते हैं। आपका कोड अधूरा है !!! एक (0,0), b (4,0), c (1,1) के साथ आपके पास dotproduct = (1-0) * (1-4) + (1-0) * (1-0) = - है 3 + 1 = -3
user43968

0

कैसे के बारे में सिर्फ यह सुनिश्चित करना है कि ढलान एक ही है और बिंदु दूसरों के बीच है?

दिए गए अंक (X1, y1) और (x2, y2) (x2> X1 के साथ) और उम्मीदवार बिंदु (a, b)

if (b-y1) / ((-X1) = (y2-y2) / (x2-X1) और X1 <a x2

तब (ए, बी) लाइन पर होना चाहिए (X1, y1) और (x2, y2)


जब कुछ निर्देशांक करीब या समान होते हैं तो पागल फ्लोटिंग पॉइंट सटीक समस्याओं के बारे में कैसे?
रॉबिन डेविस

कंप्यूटर फ्लोटिंग पॉइंट अच्छी तरह से नहीं करते हैं। एक कंप्यूटर में असीम रूप से लगातार समायोज्य मूल्यों जैसी कोई चीज नहीं होती है। इसलिए यदि आप फ्लोटिंग पॉइंट का उपयोग कर रहे हैं, तो आपको एक निर्धारित के रूप में परिभाषित और कुछ छोटे एप्सिलॉन मान का उपयोग करना होगा, और उस एप्सिलॉन की तुलना में करीब दो बिंदुओं को एक ही बिंदु माना जाना चाहिए। उस बिंदु को निर्धारित करें जो एक ही रेखा पर आईएस और अंत बिंदुओं से समान दूरी पर है। यदि आपका उम्मीदवार बिंदु उस गणना बिंदु के आपके एप्सिलॉन के भीतर है, तो उसे समान कहें।
चार्ल्स ब्रेटाना

मेरा कहना यह था कि यह उत्तर सटीक समस्याओं के कारण अनुपयोगी है जब आप वास्तव में इसे कोड में लागू करते हैं। इसलिए किसी को भी इसका इस्तेमाल नहीं करना चाहिए। गणित की परीक्षा में प्यारा सा उत्तर। लेकिन एक कॉम्प-विज्ञान पाठ्यक्रम में एक प्रतियोगिता विफलता। मैं यहां डॉट-प्रोडक्ट विधि (जो सही है) की तलाश में आया था; इसलिए मैंने सोचा कि मैं इस थ्रेड में कई उत्तरों को फ़्लैग करने के लिए कुछ क्षण लूंगा जो कि गलत हैं इसलिए अन्य जो सही समाधान से परिचित हैं उन्हें उपयोग करने के लिए लुभाया नहीं जाएगा।
रॉबिन डेविस

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

0

एक उत्तर # 2 वेक्टर श्रेणी का उपयोग करके

public static bool IsOnSegment(this Segment2D @this, Point2D c, double tolerance)
{
     var distanceSquared = tolerance*tolerance;
     // Start of segment to test point vector
     var v = new Vector2D( @this.P0, c ).To3D();
     // Segment vector
     var s = new Vector2D( @this.P0, @this.P1 ).To3D();
     // Dot product of s
     var ss = s*s;
     // k is the scalar we multiply s by to get the projection of c onto s
     // where we assume s is an infinte line
     var k = v*s/ss;
     // Convert our tolerance to the units of the scalar quanity k
     var kd = tolerance / Math.Sqrt( ss );
     // Check that the projection is within the bounds
     if (k <= -kd || k >= (1+kd))
     {
        return false;
     }
     // Find the projection point
     var p = k*s;
     // Find the vector between test point and it's projection
     var vp = (v - p);
     // Check the distance is within tolerance.
     return vp * vp < distanceSquared;
}

ध्यान दें कि

s * s

सी # में ऑपरेटर ओवरलोडिंग के माध्यम से सेगमेंट वेक्टर का डॉट उत्पाद है

कुंजी अनंत रेखा पर बिंदु के प्रक्षेपण का लाभ उठा रही है और देख रही है कि प्रक्षेपण की स्केलर मात्रा हमें तुच्छ रूप से बताती है कि प्रक्षेपण खंड पर है या नहीं। हम फजी सहिष्णुता का उपयोग करने के लिए अदिश मात्रा की सीमा को समायोजित कर सकते हैं।

यदि प्रक्षेपण सीमा के भीतर है तो हम परीक्षण करते हैं यदि बिंदु से प्रक्षेपण तक की दूरी सीमा के भीतर है।

क्रॉस उत्पाद दृष्टिकोण पर लाभ यह है कि सहिष्णुता का एक सार्थक मूल्य है।


0

यहाँ एकता में C # के साथ मेरा समाधान है।

private bool _isPointOnLine( Vector2 ptLineStart, Vector2 ptLineEnd, Vector2 ptPoint )
{
    bool bRes = false;
    if((Mathf.Approximately(ptPoint.x, ptLineStart.x) || Mathf.Approximately(ptPoint.x, ptLineEnd.x)))
    {
        if(ptPoint.y > ptLineStart.y && ptPoint.y < ptLineEnd.y)
        {
            bRes = true;
        }
    }
    else if((Mathf.Approximately(ptPoint.y, ptLineStart.y) || Mathf.Approximately(ptPoint.y, ptLineEnd.y)))
    {
        if(ptPoint.x > ptLineStart.x && ptPoint.x < ptLineEnd.x)
        {
            bRes = true;
        }
    }
    return bRes;
}

ऐसा लगता है कि यह कोड केवल ऊर्ध्वाधर और क्षैतिज रेखा खंडों के साथ काम करेगा। क्या होगा यदि ptLineStart (0,0) है, ptLineEnd है (2,2) और ptPoint है (1, 1)?
VAC

0

जूल्स के उत्तर का # संस्करण:

public static double CalcDistanceBetween2Points(double x1, double y1, double x2, double y2)
{
    return Math.Sqrt(Math.Pow (x1-x2, 2) + Math.Pow (y1-y2, 2));
}

public static bool PointLinesOnLine (double x, double y, double x1, double y1, double x2, double y2, double allowedDistanceDifference)
{
    double dist1 = CalcDistanceBetween2Points(x, y, x1, y1);
    double dist2 = CalcDistanceBetween2Points(x, y, x2, y2);
    double dist3 = CalcDistanceBetween2Points(x1, y1, x2, y2);
    return Math.Abs(dist3 - (dist1 + dist2)) <= allowedDistanceDifference;
}

0

आप उस लाइन सेगमेंट के लिए पॉइंट समीकरणों के साथ लाइन समीकरण को हल करके इसे कर सकते हैं। आप कुछ सीमा लागू कर सकते हैं क्योंकि अच्छी तरह से यह अंतरिक्ष में सबसे अधिक संभावना है जो एक अस्थायी बिंदु मान द्वारा परिभाषित किया गया है और आपको सटीक एक हिट नहीं करना चाहिए। Php में उदाहरण

function getLineDefinition($p1=array(0,0), $p2=array(0,0)){
    
    $k = ($p1[1]-$p2[1])/($p1[0]-$p2[0]);
    $q = $p1[1]-$k*$p1[0];
    
    return array($k, $q);
    
}

function isPointOnLineSegment($line=array(array(0,0),array(0,0)), $pt=array(0,0)){
    
    // GET THE LINE DEFINITION y = k.x + q AS array(k, q) 
    $def = getLineDefinition($line[0], $line[1]);
    
    // use the line definition to find y for the x of your point
    $y = $def[0]*$pt[0]+$def[1];

    $yMin = min($line[0][1], $line[1][1]);
    $yMax = max($line[0][1], $line[1][1]);

    // exclude y values that are outside this segments bounds
    if($y>$yMax || $y<$yMin) return false;
    
    // calculate the difference of your points y value from the reference value calculated from lines definition 
    // in ideal cases this would equal 0 but we are dealing with floating point values so we need some threshold value not to lose results
    // this is up to you to fine tune
    $diff = abs($pt[1]-$y);
    
    $thr = 0.000001;
    
    return $diff<=$thr;
    
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.