Откриване на сблъсък на мишката със затворени извити форми на Bezier в Canvas

Моята ситуация:

Работя върху рамка HTML5/Canvas/JavaScript за мащабиране на потребителски интерфейс, която след това използвам за проект за уеб приложение за визуализация на данни. Една от функциите, които ми трябват за моята рамка, е да мога да открия дали мишката на потребителя е върху изобразена форма. За по-сложни форми като многоъгълници и форми с криви на Безие това се превръща в предизвикателство.

Намерих два начина за решаване на този проблем:

(1) Един от начините е да нарисувате всичко върху платното два пъти. Първият път всяка форма се запълва с уникален цвят от хеш таблица. Вторият път формите получават своите реални цветове и маски върху първия слой. За да открия сблъсък във формата на мишката, ще трябва да взема цвета на пиксела под мишката от първия слой и да картографирам цвета, който получавам, към съответната форма в хеш-таблицата.

(2) Или мога да използвам алгоритъма за излъчване на лъчи (http://en.wikipedia.org/wiki/Point_in_polygon#Ray_casting_algorithm). Всъщност внедрих този алгоритъм с код, който открива сблъсъци на лъчева линия и сблъсъци на лъч-Безие.

Истинският въпрос:

Не харесвам първия подход, защото всичко трябва да бъде начертано два пъти, което не е евтино от изчислителна гледна точка. Но вторият подход не гарантира точност, поради грешки в закръглянията от изчисленията.

В идеалния случай бих искал да подобря точността на втория подход до почти съвършенство.

Моят опит да подобря точността е да хвърля 4 лъча в различни посоки: отгоре, отляво, отдолу и отдясно. Ако поне един хоризонтален и един вертикален лъч предполагат, че мишката е вътре във формата, тогава заключавам, че точката е вътре във формата. Въпреки че това елиминира повечето грешки, грешки (без задействане) все още възникват, когато мишката е вътре във формата.

Би било страхотно, ако някой може да предложи поправка на алгоритъма за хвърляне на лъчи или може би дори трета опция!

Благодаря предварително.


person Hans    schedule 28.11.2013    source източник


Отговори (1)


Можете да направите лъчево предаване, но можете също да използвате вградената функция в контекста:

var flag = ctx.isPointInPath(x, y);

Това, което трябва да направите, е просто да изградите отново всеки път, който искате да тествате (няма нужда да ги чертаете или запълвате) и да направите този тест.

Има и:

var flag = ctx.isPointInStroke(x, y);

ако искате да вземете предвид и самия щрих, в случай че има ширина > 1. Тази функция обаче все още не се поддържа в IE.

Например:

/// build some polygon/shape/...
ctx.beginPath();;
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.lineTo(x3, y3);
ctx.closePath();

/// no need to fill/stroke it, just test the path:
var flag = ctx.isPointInPath(x, y);

Трябва да направите това за всяка уникална форма, но производителността е доста добра, освен ако нямате безброй форми, в който случай можете да помислите за четворни дървета и други подобни.

За да откриете върху коя форма сте щракнали, можете да съхранявате формите като обекти, вместо да използвате уникален цвят (обаче зависи от действителния сценарий), така че когато итерирате през масива от обекти, ще знаете кой обект проверявате в момента и ако е вярно попадение, тогава прекъснете итерацията.

person Community    schedule 28.11.2013
comment
Невероятен! С удоволствие изхвърлям метода на лъчеобразуване. Тази вградена функция работи перфектно! Благодаря ти много! - person Hans; 29.11.2013
comment
Няма проблем @Hans, радвам се, че можах да помогна :) - person ; 29.11.2013