#algorithm #paint
#алгоритм #Краски
Вопрос:
Я играюсь с небольшим нанесением краски. Я хочу создавать разные кончики кисти (не только простые линии). Основная идея заключается в повторении (тиснении) кончика кисти в соответствии с движениями мыши. Поскольку движения мыши не отправляют требуемое событие для каждого пикселя, перемещаемого мышью. Мой текущий подход заключается в том, что я использую алгоритм Брезенхема для определения пикселей, которые я хочу нарисовать, а затем ставлю штамп на кончик кисти над каждым пикселем. Однако это не очень эффективно, поскольку размер кончика кисти составляет, например, 30×30 пикселей. Я хочу штамповать на 25% ширины кисти, но я не знаю, как это делается хорошим способом. Я мог определить расстояние и штамповать только до тех пор, пока не будет восстановлено расстояние в 25% от кончика кисти.
Есть еще идеи, как реализовать алгоритм тиснения кистью, который учитывает нерегулярные события мыши и позволяет определять интервал?
Спасибо за чтение!
Ответ №1:
Немного запоздал с ответом, но если кто-то ищет ответы, вот как я реализую это в своем коде для проекта на Java.
Шаг равен проценту кисти, поэтому, если это кисть 20×20, то 25 шагов — это 5 пикселей, что является пробелом.
Затем я создаю нормализованный вектор из последнего и текущего положения мыши.
После first
которого происходит первый щелчок. Когда dist больше, чем пробел, итератор создается для обработки всего расстояния, потому что иногда мышь может двигаться быстро, поэтому используется несколько dibs (или «штампов», dibs — это термин в этой области)
iter=space-remn
предназначен для выравнивания его с предыдущим dib.
Предыдущая позиция, добавленная с помощью vector*iter
, возвращает нам позицию для dib.
После того, как мы их все нарисуем, начинаются важные моменты.
remn = dist-iter space-remn;
Остаток (remn) собирается из предыдущего процесса, добавленного к dist (расстоянию) от начальной стадии.
Чтобы понять математику, давайте покажем пример.
кисть = 30×30, шаг = 25%, remn = 2,5, расстояние = 28,5 (включая remn), пробел = 7,5 (30 * 25/100)
Следующий remn = 28,5 (dist)-27,5 (5 изначально 7,5 * 3 раза с момента проверки (<28,5-2,5) выполняется после обновления iter) 7,5 (пробел)-2,5 (предыдущий remn) = 6 пикселей
Таким образом, мышь должна перемещаться на 1,5 пикселя для следующего dib, поскольку 6 пикселей уже пройдены.
В случае else это еще более просто.
dist (в который уже добавлен remn) завершается ошибкой remn=dist.
Например, если у нас осталось 2 пикселя с прошлого раза, мы добавляем расстояние, пройденное мышью, скажем, 3 пикселя, поэтому нам нужно пройти еще 2,5 пикселя для следующего dib.
int size =(Integer) tool.getAttribute("size");
int step = (Integer) tool.getAttribute("step");
double space = size*step/100.0f; //what is actualy need for the check algorithm for the step rate to work
double dist = Point.distance(pZero.getX(),pZero.getY(),last.getX(),last.getY());
int bleed = (int) (size/tilemap[0].getWidth() size/tilemap[0].getHeight());
Point2D.Double vec = new Point2D.Double(pZero.getX()-last.getX(),pZero.getY()-last.getY());
vec.x /= dist;
vec.y /= dist;
dist =remn;
if(first){
//System.out.println("First ");
for(int y=0; y < tilesHigh; y) {
for(int x=0; x < tilesWide; x) {
int pos = x y*tilesWide;
// This should never exceed tilemap.length.
BufferedImage tile = tilemap[pos];
//tool.operate(tile.getGraphics(), new Point(pZero.x-x*tile.getWidth(), pZero.y-y*tile.getHeight()));
tool.operate(tile.getGraphics(), tilemapPointToTilePoint(pZero, pos));
}
}
first = false;
}else {
if(dist>=space){//check to see if the mouse distance is enoght for a step(space)
iter=space-remn;
//test=0;
//System.out.println("pZero=" pZero);
while(iter<dist-remn){//fills the gap between with at the rate of step(space),if you move the mouse fast you use to get those
//do stuff
pZero.x =(int) Math.round(last.x (vec.x*iter));
pZero.y =(int) Math.round(last.y (vec.y*iter));
//int pos = xyToIndex(pZero.x, pZero.y);
//test ;
//System.out.println("iter = " iter " remn=" remn " space=" space);
//System.out.println("pIter=" pZero);
//System.out.println("Second ");
for(int y=0; y < tilesHigh; y) {//bleed
for(int x=0; x < tilesWide; x) {
int pos = x y*tilesWide;
// This should never exceed tilemap.length.
BufferedImage tile = tilemap[pos];
//tool.operate(tile.getGraphics(), new Point(pZero.x-x*tile.getWidth(), pZero.y-y*tile.getHeight()));
tool.operate(tile.getGraphics(), tilemapPointToTilePoint(pZero, pos));
}
}
iter = space;
}
//System.out.println("last = " last);
//System.out.println("test=" test);
remn = dist-iter space-remn;
}else remn = dist;
}
Ответ №2:
Алгоритм Brasenham — это алгоритм аппроксимации, поскольку вы уже выполняете аппроксимацию вместо сглаживания, вы могли бы пойти этим путем дальше…
С небольшой модификацией алгоритма, где вы получаете ширину кисти в качестве параметра (или, что еще лучше, расстояние печати):
- пороговым значением является расстояние печати * множитель ориентации вектора (45 135 225 315 градусов = sqrt (2))
- вместо рисования пикселей по X, Y -> подсчет пикселей
- когда количество пикселей > = пороговое значение, выведите кисть в местоположениях X, Y и сбросьте количество пикселей
Используйте значение с плавающей запятой для счетчика, и если пиксели не квадратные, то добавляйте разное количество для каждого пройденного шага deltaY или deltaX и учитывайте ориентацию вектора от x1, y1 до x2, y2, чтобы получить реальное расстояние, которое нужно добавить для каждого пикселя.
Довольно простая модификация…
Комментарии:
1. Я хочу выразить пороговое значение в процентах от кончика кисти. Я думал о ширине кончика кисти в пикселях (= = расстояние печати) в качестве порогового значения. Однако мне трудно понять, почему вы умножаете расстояние печати на множитель векторной ориентации (что это такое?). Не могли бы вы объяснить? Спасибо!