Какие функции используются для построения кривых на графике? и как вычислить кратчайшее расстояния от точки до линии?

bicyclist

Новичок
Стоят 2 задачи:
1. есть несколько точек на графике. Надо достроить кривую, которая бы пересекала все эти точки.
2. есть произвольная точка, нужно вычислить расстояние от неё до ближайшей точки кривой из пункта 1. То есть найти наименьшее расстояние от точки до кривой.

нашёл только функцию кривой безье отсюда http://blog.kislenko.net/show.php?id=2523, но она только для 4 точек, для произвольного количества точек не нашёл. И, если кривая круто изгибается, то она по этой функции может не проходить через все точки, а только тянуться к ним. Меня такое не устраивает.
Код:
 function cubicbezier($img, $col, $curves, $n = 20) {
  for ($curve = 0; $curve < count($curves); $curve++) {
   list($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3) = $curves[$curve];
   $pts = [];
   for($i = 0; $i <= $n; $i++) {
    $t = $i / $n;
    $t1 = 1 - $t;
    $a = pow($t1, 3);
    $b = 3 * $t * pow($t1, 2);
    $c = 3 * pow($t, 2) * $t1;
    $d = pow($t, 3);
    $x = round($a * $x0 + $b * $x1 + $c * $x2 + $d * $x3);
    $y = round($a * $y0 + $b * $y1 + $c * $y2 + $d * $y3);
    $pts[$i] = array($x, $y);
   }
   for ($i = 0; $i < $n; $i++) {
    imageline($img, $pts[$i][0], $pts[$i][1], $pts[$i+1][0], $pts[$i+1][1], $col);
   }
  }
 }
Я так думаю, мне нужна функция. чтобы из 4-5 опорных точек кривой получить 20 точек на этой же кривой, и перебором сравнивать расстояние от каждой из них до точки из задачи 2. Как в php найти больше точек кривой, если некоторые известны?
 

ksnk

прохожий
А что за проблемы то ? Если кривая, предположительно гладка и особо без претензий, то просто для каждой точки вычисляешь "направление" - отрезок, проходящий через эту точку и совпадающий с направлением из точки n-1 к точке n+1, с центром в вершине. Либо одинаковой длины, либо пропорционально расстоянию. Получается по 2 точки для каждой вершины. "направление" для конечных дублируеш из ближайшей. Вот по этим точкам и строишь кривые Безье. Получается гладко и проходит через все точки.
 

bicyclist

Новичок
я не очень понял. а функций в php для этого нет? можно поподробнее. как это будет в коде?
 

AnrDaemon

Продвинутый новичок
А функции в PHP для этого вам придётся написать самому. В этом и заключается работа программиста.
 

ksnk

прохожий
Так, например. Относительно мутная часть - построение массива pairs...
PHP:
<?php

// settings
$config = (object)[
//границы картинки
    'maxx' => 400,
    'maxy' => 200,
    // Длина "отрезка" кривой бейзье - длина отрезка между сопряженными точками,
    // уменьшенных на стерень 2. /8 - похоже на разумное значение
    'idivider' => 3,
    'apoints' => [[5, 5], [100, 15], [150, 50], [170, 150]], // тестовое значение начальных точек
];


if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$new_points = json_decode($_REQUEST['points']);
    if (is_array($new_points)) {
$config->apoints = $new_points;
    };
    if(isset($_REQUEST['x']) && isset($_REQUEST['y'])){
$config->apoints[]=[$_REQUEST['x'],$_REQUEST['y']];
    }
}


// so lets dpaw a picture

$img = imageCreateTrueColor($config->maxx, $config->maxy);

$bgcol = imageColorallocate($img, 245, 245, 245);
$red = imagecolorallocate($img, 255, 0, 0);
$green = imagecolorallocate($img, 0, 255, 0);

function cubicbezier($img, $col, $config, $n = 20)
{
$pairs = []; // если первый и последний point совпадают - зацикливаем
    $circle = false;
    $points = $config->apoints;
    if ($points[0] == $points[count($points) - 1]) {
$circle = true;
        array_pop($points);
    }
foreach ($points as $idx => $point) {
if (!$circle) {
if ($idx <= 0) $pointa = $points[$idx]; else $pointa = $points[$idx - 1];
            if ($idx >= count($points) - 1) $pointb = $points[$idx]; else $pointb = $points[$idx + 1];
        } else {
if ($idx <= 0) $pointa = $points[count($points) - 1]; else $pointa = $points[$idx - 1];
            if ($idx >= count($points) - 1) $pointb = $points[0]; else $pointb = $points[$idx + 1];
        }
$difx = ($pointb[0] - $pointa[0]) >> $config->idivider;
        $dify = ($pointb[1] - $pointa[1]) >> $config->idivider;
        $p0 = $points[$idx];
        $pairs[$idx] = [
[$p0[0] - $difx, $p0[1] - $dify, $p0[0], $p0[1]],
            [$p0[0], $p0[1], $p0[0] + $difx, $p0[1] + $dify]
];
    }
//print_r($points);print_r($pairs);
    for ($curve = $circle ? 0 : 1; $curve < count($pairs); $curve++) {
list($x0, $y0, $x1, $y1) = $pairs[$curve == 0 ? count($pairs)-1 : $curve - 1][1];
        list($x2, $y2, $x3, $y3) = $pairs[$curve][0];
        $pts = [];
        for ($i = 0; $i <= $n; $i++) {
$t = $i / $n;
            $t1 = 1 - $t;
            $a = pow($t1, 3);
            $b = 3 * $t * pow($t1, 2);
            $c = 3 * pow($t, 2) * $t1;
            $d = pow($t, 3);
            $x = round($a * $x0 + $b * $x1 + $c * $x2 + $d * $x3);
            $y = round($a * $y0 + $b * $y1 + $c * $y2 + $d * $y3);
            $pts[$i] = array($x, $y);
        }
for ($i = 0; $i < $n; $i++) {
imageline($img, $pts[$i][0], $pts[$i][1], $pts[$i + 1][0], $pts[$i + 1][1], $col);
        }
}
}

# fill background
imageFill($img, 0, 0, $bgcol);

cubicbezier($img, $green, $config);

foreach ($config->apoints as $point) {
imagearc($img, $point[0], $point[1], 4, 4, 0, 360, $red);
}

ob_start();
imagePNG($img);
$image_data = ob_get_clean();

$html = <<<HTML
<!doctype html>
<html>
<header></header>
<body>
<form method="POST">
<textarea name="points" style="height:{{config.maxy}}px;">
{{points}}
</textarea>

<input type="image" src="data:image/png;base64,{{base64image}}"
alt="Submit" width="{{config.maxx}}" height="{{config.maxy}}"><br>
<button type="submit">submit</button>
</form>
<pre>{{debug}}</pre>
</body>
</html>
HTML;

$data=[
'config.maxx'=>$config->maxx,
    'config.maxy'=>$config->maxy,
    'points'=>json_encode($config->apoints, JSON_UNESCAPED_UNICODE),
    'base64image'=>base64_encode($image_data),
    'debug'=>'',// was: print_r($_POST, true)
];
foreach($data as $k=>$v){
$html=str_replace('{{'.$k.'}}',$v,$html);
}

echo $html;
 
Последнее редактирование:

bicyclist

Новичок
Привет. понадобилось разморозить тему. Как продолжить кривую за пределы заданных изначально точек? то есть если заданы 4 точки, и по ним построена кривая, от 1 точки до 4 через 2 и 3. Как продолжить кривую до 1 точки и после 4 точки?
 

ksnk

прохожий
Поменял немного код, теперь действительно, если повторить начальную точку в конце - фигура замкнется. 1646
 
Сверху