2011-02-28 14 views
11

मैं थोड़ी देर के लिए जीडी लाइब्रेरी के साथ खेल रहा हूं और बेजियर वक्र एटीएम के साथ अधिक कण।बेजियर घटता में रंग जोड़ने के लिए एल्गोरिदम

मैंने some existant class का उपयोग किया जो मैंने थोड़ा संशोधित किया (गंभीरता से eval() ...)। मुझे पता चला कि यह जेनेरिक एल्गोरिदम का उपयोग किया गया था और जीडी के लिए रूपांतरित किया गया था।

अब मैं इसे दूसरे स्तर पर ले जाना चाहता हूं: मुझे कुछ रंग चाहिए।

लाइन रंग के लिए कोई समस्या नहीं है लेकिन रंग भरें यह कठिन है।

मेरा प्रश्न है:

वहाँ उस के लिए किसी भी विद्यमान एल्गोरिथ्म है? मेरा मतलब गणितीय एल्गोरिदम या कोई भी भाषा पहले से ही कर रही है ताकि मैं इसे PHP + GD में स्थानांतरित कर सकूं?

EDIT2 तो, मैं एक कठिन वक्र के साथ @MizardX समाधान की कोशिश की:

  • 1 स्थिति: 50 - 50
  • अंतिम स्थिति: 50 - 200
  • 1 नियंत्रण बिंदु: 300 - 225
  • दूसरा नियंत्रण बिंदु: 300 - 25

कौन इस दिखाना चाहिए:

http://i.stack.imgur.com/vtzE0.png

और देता है यह:

http://i.stack.imgur.com/waMkU.png

संपादित मैं पहले से ही @MizardX समाधान के बारे में पढ़ा। इसे बनाने के लिए imagefilledpolygon का उपयोग करना।

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

निर्देशांक प्रयोग किया है:

  • पहला बिंदु 100 - 100
  • अंतिम बिंदु है 300 - 100
  • पहले नियंत्रण बिंदु 100 - 0
  • अंतिम नियंत्रण बिंदु 300 - 200

http://i.stack.imgur.com/cWH1y.jpg

नीचे भाग वह है जो मुझे इस तरह के एल्गोरिदम के साथ मिलता है ...

+0

छवियां नीचे हैं ... – Pacerier

उत्तर

8

बेजियर वक्र को पॉलीलाइन/पॉलीगॉन में कनवर्ट करें, और इसे भरें। यदि आप पर्याप्त अंतराल (~ 1 पिक्सेल) पर बेजियर बहुपद का मूल्यांकन करते हैं तो यह आदर्श बेजियर वक्र के समान होगा।

<?php 

// Calculate the coordinate of the Bezier curve at $t = 0..1 
function Bezier_eval($p1,$p2,$p3,$p4,$t) { 
    // lines between successive pairs of points (degree 1) 
    $q1 = array((1-$t) * $p1[0] + $t * $p2[0],(1-$t) * $p1[1] + $t * $p2[1]); 
    $q2 = array((1-$t) * $p2[0] + $t * $p3[0],(1-$t) * $p2[1] + $t * $p3[1]); 
    $q3 = array((1-$t) * $p3[0] + $t * $p4[0],(1-$t) * $p3[1] + $t * $p4[1]); 
    // curves between successive pairs of lines. (degree 2) 
    $r1 = array((1-$t) * $q1[0] + $t * $q2[0],(1-$t) * $q1[1] + $t * $q2[1]); 
    $r2 = array((1-$t) * $q2[0] + $t * $q3[0],(1-$t) * $q2[1] + $t * $q3[1]); 
    // final curve between the two 2-degree curves. (degree 3) 
    return array((1-$t) * $r1[0] + $t * $r2[0],(1-$t) * $r1[1] + $t * $r2[1]); 
} 

// Calculate the squared distance between two points 
function Point_distance2($p1,$p2) { 
    $dx = $p2[0] - $p1[0]; 
    $dy = $p2[1] - $p1[1]; 
    return $dx * $dx + $dy * $dy; 
} 

// Convert the curve to a polyline 
function Bezier_convert($p1,$p2,$p3,$p4,$tolerance) { 
    $t1 = 0.0; 
    $prev = $p1; 
    $t2 = 0.1; 
    $tol2 = $tolerance * $tolerance; 
    $result []= $prev[0]; 
    $result []= $prev[1]; 
    while ($t1 < 1.0) { 
     if ($t2 > 1.0) { 
      $t2 = 1.0; 
     } 
     $next = Bezier_eval($p1,$p2,$p3,$p4,$t2); 
     $dist = Point_distance2($prev,$next); 
     while ($dist > $tol2) { 
      // Halve the distance until small enough 
      $t2 = $t1 + ($t2 - $t1) * 0.5; 
      $next = Bezier_eval($p1,$p2,$p3,$p4,$t2); 
      $dist = Point_distance2($prev,$next); 
     } 
     // the image*polygon functions expect a flattened array of coordiantes 
     $result []= $next[0]; 
     $result []= $next[1]; 
     $t1 = $t2; 
     $prev = $next; 
     $t2 = $t1 + 0.1; 
    } 
    return $result; 
} 

// Draw a Bezier curve on an image 
function Bezier_drawfilled($image,$p1,$p2,$p3,$p4,$color) { 
    $polygon = Bezier_convert($p1,$p2,$p3,$p4,1.0); 
    imagefilledpolygon($image,$polygon,count($polygon)/2,$color); 
} 

?> 

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

मैं नियमित परीक्षण करने के लिए भूल गया

मैं कैसे परिचित आप बेज़ियर घटता के साथ कर रहे हैं पता नहीं है, लेकिन यहाँ एक क्रैश कोर्स है। जैसा आपने कहा था वैसा ही है; यह सही परिणाम नहीं देता है।

  1. मैं अनजाने फिर से इस्तेमाल किया चर नाम $p1 और $p2: अब मैं दो कीड़े तय कर दी है। मैंने उनका नाम बदलकर $prev और $next रखा।
  2. while -loop में गलत साइन इन करें। अब यह काफी हद तक पर्याप्त दूरी के बजाय दूरी कम होने तक लूप करता है।
+0

@MizardX: आपके उत्तर के लिए धन्यवाद। मैंने तुरंत आपका संपादन नहीं देखा और आपके पास अभी भी वही कोड था (केवल मैंने दबाया '$ p1 = $ p1; ' जो बेकार लगता है।) वैसे भी, यह अपेक्षित काम नहीं करता है (मेरे प्रश्न का अद्यतन देखें) – Shikiryu

+0

पहले बिंदु और पहले नियंत्रण बिंदु के बीच की दूरी केवल 50 है, जबकि अंतिम बिंदु और दूसरे नियंत्रण बिंदु के बीच की दूरी 100 है; इस प्रकार आप एक विषम आकार मिलता है। –

+0

... मैं बस गूंगा हूँ ... ठीक है, ऐसा लगता है लेकिन मेरा सर्वर दुर्घटनाग्रस्त हो जाता है अगर पहले और अंतिम बिंदु बहुत करीब हैं (मुझे पता है कि यह नहीं होना चाहिए लेकिन ... यह अभी भी मेरे मामले में हो सकता है ।) क्या कोई समाधान है या मुझे इसे पहले जांचना चाहिए? – Shikiryu

0

वक्र (p_list) के साथ झूठ वाले लगातार बिंदुओं की एक सूची उत्पन्न करें)।

आप वक्र (एल 1) के दो अंतिम बिंदुओं के बीच एक रेखा बनाते हैं।

फिर आपको लाइन का सामान्य (एन 1) मिल जाएगा। इस सामान्य का उपयोग इस सामान्य (डी 1) के साथ दो सबसे दूर बिंदुओं (p_max1, और p_max2) के बीच की दूरी को ढूंढें। इस दूरी को एन अलग इकाइयों (डेल्टा) में विभाजित करें।

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

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

2

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

कोड:

pts={{50,50},{300,225},{300,25},{50,200}}; 
f=BezierFunction[pts]; 
step=.1; (*initial step*) 

While[ (*get the final step - Points no more than .01 appart*) 
    Max[ 
    EuclideanDistance @@@ 
     Partition[Table[f[t],{t,0,1,step}],2,1]] > .01, 
    step=step/2] 

(*plot it*) 
[email protected]@Table[f[t],{t,0,1,step}] 

Image Link

एल्गोरिदम को अनुकूलित किया जा सकता है (यानी कम अंक उत्पन्न करें) यदि आपको अंक के बीच समान पैरामीटर वृद्धि की आवश्यकता नहीं है, जिसका अर्थ है कि आप प्रत्येक बिंदु पर पैरामीटर वृद्धि का चयन कर सकते हैं जो अगली तक बाध्य दूरी सुनिश्चित करता है।

रैंडम उदाहरण:

enter image description here

+0

@beisarius बस सोच रहा है। क्या गणित संकलक सी भाषा में कोड पीढ़ी के लिए प्रदान करता है? यदि हां, तो अतिरिक्त कदमों की आवश्यकता होगी? – DavidC

+0

@ डेविड इस http://reference.wolfram.com/mathematica/CCodeGenerator/tutorial/Overview.html –

+0

धन्यवाद, आपने मुझे गणित की खोज की :) :) – Shikiryu

0

इस उत्तर बहुत @ MizardX के लिए इसी तरह की है, लेकिन एक बहुभुज सन्निकटन के लिए बेज़ियर साथ उपयुक्त अंक खोजने के लिए एक अलग तरीके का उपयोग करता है।

function split_cubic($p, $t) 
{ 
    $a_x = $p[0] + ($t * ($p[2] - $p[0])); 
    $a_y = $p[1] + ($t * ($p[3] - $p[1])); 
    $b_x = $p[2] + ($t * ($p[4] - $p[2])); 
    $b_y = $p[3] + ($t * ($p[5] - $p[3])); 
    $c_x = $p[4] + ($t * ($p[6] - $p[4])); 
    $c_y = $p[5] + ($t * ($p[7] - $p[5])); 
    $d_x = $a_x + ($t * ($b_x - $a_x)); 
    $d_y = $a_y + ($t * ($b_y - $a_y)); 
    $e_x = $b_x + ($t * ($c_x - $b_x)); 
    $e_y = $b_y + ($t * ($c_y - $b_y)); 
    $f_x = $d_x + ($t * ($e_x - $d_x)); 
    $f_y = $d_y + ($t * ($e_y - $d_y)); 

    return array(
     array($p[0], $p[1], $a_x, $a_y, $d_x, $d_y, $f_x, $f_y), 
     array($f_x, $f_y, $e_x, $e_y, $c_x, $c_y, $p[6], $p[7])); 
} 

$flatness_sq = 0.25; /* flatness = 0.5 */ 
function cubic_ok($p) 
{ 
    global $flatness_sq; 

    /* test is essentially: 
    * perpendicular distance of control points from line < flatness */ 

    $a_x = $p[6] - $p[0]; $a_y = $p[7] - $p[1]; 
    $b_x = $p[2] - $p[0]; $b_y = $p[3] - $p[1]; 
    $c_x = $p[4] - $p[6]; $c_y = $p[5] - $p[7]; 
    $a_cross_b = ($a_x * $b_y) - ($a_y * $b_x); 
    $a_cross_c = ($a_x * $c_y) - ($a_y * $c_x); 
    $d_sq = ($a_x * $a_x) + ($a_y * $a_y); 
    return max($a_cross_b * $a_cross_b, $a_cross_c * $a_cross_c) < ($flatness_sq * $d_sq); 
} 

$max_level = 8; 
function subdivide_cubic($p, $level) 
{ 
    global $max_level; 

    if (($level == $max_level) || cubic_ok($p)) { 
     return array(); 
    } 

    list($q, $r) = split_cubic($p, 0.5); 
    $v = subdivide_cubic($q, $level + 1); 
    $v[] = $r[0]; /* add a point where we split the cubic */ 
    $v[] = $r[1]; 
    $v = array_merge($v, subdivide_cubic($r, $level + 1)); 
    return $v; 
} 

function get_cubic_points($p) 
{ 
    $v[] = $p[0]; 
    $v[] = $p[1]; 
    $v = array_merge($v, subdivide_cubic($p, 0)); 
    $v[] = $p[6]; 
    $v[] = $p[7]; 
    return $v; 
} 

function imagefilledcubic($img, $p, $color) 
{ 
    $v = get_cubic_points($p); 
    imagefilledpolygon($img, $v, count($v)/2, $color); 
} 

मूल विचार है कि जब तक हमारे साथ छोड़े गए बिट्स को लगभग सपाट नहीं किया जाता है तब तक क्यूबिक को आधे हिस्से में विभाजित करना है। हर जगह हम क्यूबिक विभाजित करते हैं, हम बहुभुज बिंदु चिपकते हैं।

split_cubic पैरामीटर $t पर दो में क्यूबिक विभाजित करता है। cubic_ok "क्या हम पर्याप्त फ्लैट हैं?" परीक्षा। subdivide_cubic रिकर्सिव फ़ंक्शन है। ध्यान दें कि हम खराब मामलों से वास्तव में हमें खराब करने से बचने के लिए रिकर्सन गहराई पर एक सीमा चिपकते हैं।

आपका स्वयं का प्रतिच्छेदन परीक्षण का मामला:

$img = imagecreatetruecolor(256, 256); 

imagefilledcubic($img, array(
    50.0, 50.0, /* first point */ 
    300.0, 225.0, /* first control point */ 
    300.0, 25.0, /* second control point */ 
    50.0, 200.0), /* last point */ 
    imagecolorallocate($img, 255, 255, 255)); 

imagepng($img, 'out.png'); 

imagedestroy($img); 

इस उत्पादन देता है:

Filled cubic test

मैं समझ नहीं कैसे पीएचपी अच्छी तरह से इस विरोधी उर्फ ​​बनाने के लिए; imageantialias($img, TRUE); काम नहीं कर रहा था।

+1

PHP 'imagefilledpolygon' के लिए एंटी-एलियासिंग का समर्थन नहीं करता है। डेवलपर्स के अनुसार यह इरादा व्यवहार है। PHP मैनुअल पर टिप्पणियां 'imagefilledyolygon' के शीर्ष पर 'imagepolygon' ड्राइंग करने का सुझाव देती हैं। –

+0

अच्छा तरीका भी! :) – Shikiryu

संबंधित मुद्दे