Добавлен , опубликован

Основы Интерфейса

Содержание:
Image - в вакрафте в редакторе триггеров представлен как отдельный тип данных.
является 2d-мерной картинкой, которую можно создать на карте.

Что нужно сделать, чтобы создать саму картинку image?

кратко: я не часто пользуюсь image, но несколько раз исследовал этот тип. Может быть этот тип не такой уж универсальный, чтобы нашло широкое применение в массах. Но с помощью нее тоже можно творить магию (заменять курсоры, подсвечивать области, радиусы). Может быть основная масса людей сочтет ресурс бесполезным, ои уже знает ответы. но я напишу. Часто натыкаюсь на проблемы, когда изображение не создается, и ты сидишь и паришься. Решил раз и навсегда написать и закрыть для себя этот вопрос, если я к нему опять вернусь.
  1. у вас должна быть рабочая картинка в формате blp или tga. В рефордже возможно доступны и другие форматы текстур, сам разбиться с другими форматами не стал. Картинка должна обладать ряд свойств, иначе она у вас не будет отображаться или будет растягиваться. Начнем урок, для примера необходимо создать круг. В графическом редакторе рисуем или берем какое-нибудь изображение
  1. для создания картинки необходимо задавать размер в степени 2-ки n^2 x m^2, например 64x64, 256x256 или 512x512 и тд). В противном случае края будут растянуты или изображения не будут отображаться.
я намеренно создал и импортировал одну и ту же иконку разных форматов index1.tga и index1.blp (см. рис ниже размер не соответствует степени 2, растянут вниз), чтобы проверить как они работают.
К сожалению, бывает так, что не одна из них не создается из-за неправильных размеров.
чтобы соответствовать этим размерам. создана другая иконка круга с 128x128. Теперь blp и tga создаются, но появилась другая проблема: изображения растягиваются. Вы можете с такими проблемами столкнутся:
Это происходит из-за отсутствия прозрачной рамки вокруг картинки. Это обязательное требование не только к image, но и у uberplasts и др.
  1. необходимо обвести по краям изображения альфа-каналом, короче рамка толщиной 1 пиксель (как в интернете пишут). Внимание: бывает и 1 пикселя не достаточно, поэтому мне приходилось толщину увеличивать. Варкрафт требует обвести картинку. прозрачным слоем вокруг, иначе картинка получится растянутой, поэтому обязательно нужно края альфа-каналом обработать. Вы можете взять изображение и вставить в прозрачный фон.
Есть различные редакторы и способы:
  • рисуем в paint или фотошопе. paint - редактор старый, и на нем нельзя работать с прозрачностью Есть обновленный paint.net, но о нем попозже. Поэтому переходим к фотошопу, и там задаем прозрачность, которую нельзя было добавить в древнем paint.
Прозрачность задаем в фотошопе.
  • рисуем в paint картинку.
  • создать пустой прозрачный фон с размерами n^2 x m^2, а сверху слоем добавить картинку.
  • внутри фона нужно красиво разместить картинку и размеры, оставив прозрачную рамку. Можно и в самих редакторах просчитать заранее рамку пикселями, я ранее так занимался. Но это мб неудобно. Проще размещать картинку. Но если изображение простое, как раз для индикаторов, это другой вопрос.
  • выделяем "волшебной палочкой рамку" рамку, и добавляем в отдельный слой. Зачем? а чтобы добавить выделенной области альфа-канал. Несмотря на то, что в фотошопе фон прозрачный, ему все равно нужно задать альфа-канал.
Основная суть: Нужно выделить область, и “сохранить как выделенную область..”. Во вкладках “каналы” найти наш слой, и добавить прозрачность. Об этом я расписывал ранее в этой теме или рассмотреть можно тут
прозрачность задаем в paint.net
Используется похожий способ. Выделяете любым способом область, у меня круг, поэтому проще мне “Волшебной палочкой” выделить области вокруг него. Далее можно работать по-разному со слоями. Выделенные области можно добавить в слой, можно в свойства слоя задать прозрачность, как делал ранее в фотошопе. Но в данном случае, слои нас не интересуют совсем. Я как-то не смог с ними ничего путного сделать (и не тестировал в paint.net), чтобы в варкрафте работало. Методом тыка нашел два способа:
  • если нужна частично полупрозрачная текстура, достаточно в заливке или любое действие с рисованием задать цвет с альфа-каналом. Прямо в цвете можно задать цвета и прозрачность. Если задать полную прозрачность 255 из 255, то почему-то paint.net больше не хочет перекрашивать (учтите: с полной прозрачностью не хочет раскрашивать цветом)
  • если нужна полная прозрачность. Выделяете область, например, “Волшебной палочкой”, и удаляете и их из изображения кнопкой “delete” Это особенно полезно, когда нужно края обвести альфа-каналом..в фотошопе такое такое вырезание/удаление областей нифига не работает (мб какое действие или настройка надо?). В paint.net сохраняемся, профит.
прога blp lab
В самом программе все это есть. Она и размеры подгоняет и прозрачный слой добавляет
  1. правильно использовать триггеры. К примеру вы создали image, но у вас не отобразилось. Нужно прорисовать изображение нативкой SetImageRenderAlways(img, true)
разбор дефолтной функции
Разбор функции
функция создания image:
native CreateImage takes string file, real sizeX, real sizeY, real sizeZ, real posX, real posY, real posZ, real originX, real originY, real originZ, integer imageType returns image
file - текстура
size x,y,z - размеры текстуры
pos x,y,z - координаты
origin x,y,z - центр изображения картинки. Изначально центр лежит в нижем левом углу (0,0), а растягивается в верхний правый угол из-за размеров (width,height). Из-за нее может неправильно центр картинки создается. чтобы картинка была в центре координат укажите: origin x = widht/2 , origin y = height/2
integer imageType - тип image. Существуют еще типы, которые отличаются еще порядком отрисовки. Чем меньше число, тем выше рисуется изображение. OcclusionMark будет всегда выше уберсплата. Индикатор всегда будет выше OcclusionMark.
INVALID = -1 не показывает
SHADOW = 0 invalid, не показывает
SELECTION = 1
INDICATOR = 2
OCCLUSION_MASK = 3
UBERSPLAT = 4 может менять цвет. в зависимости от дня/ночи. Ночью картинка немного затемняется.
TOPMOST = 5 invalid, не показывает
примеры функции
--@string file
--@real sizeX
--@real sizeY
--@real x
--@real y
--@integer imagetype
function CreateRectangularImage(file,sizeX,sizeY,x,y,imagetype)
    bj_lastCreatedImage = CreateImage(file, sizeX, sizeY, 0., x, y, 0., sizeX / 2., sizeY / 2., 0., imagetype)
    SetImageRenderAlways(bj_lastCreatedImage, true)
    return bj_lastCreatedImage
end

--@string file
--@real size
--@real x
--@real y
--@integer imagetype
function CreateSquareImage(file,size,x,y,imagetype)
    bj_lastCreatedImage = CreateImage(file, size, size, size, x, y, 0., size / 2., size / 2., 0., imagetype)
    SetImageRenderAlways(bj_lastCreatedImage, true)
    return bj_lastCreatedImage
end

--@player p
--@string file
--@real size
--@real x
--@real y
--@integer imagetype
function CreateSquareImageForPlayer(p,file,size,x,y,imagetype)
    bj_lastCreatedImage = CreateImage(file, size, size, 0., x, y, 0., size / 2., size / 2., 0., imagetype)
    SetImageRenderAlways(bj_lastCreatedImage, GetLocalPlayer() == p)
    return bj_lastCreatedImage
end

--@player p
--@string file
--@real sizeX
--@real sizeY
--@real x
--@real y
--@integer imagetype
function CreateRectangularImageForPlayer(p,file,sizeX,sizeY,x,y,imagetype)
    bj_lastCreatedImage = CreateImage(file, sizeX, sizeY, 0., x, y, 0., sizeX / 2., sizeY / 2., 0., imagetype)
    SetImageRenderAlways(bj_lastCreatedImage, GetLocalPlayer() == p)
    return bj_lastCreatedImage
end
пример ошибки
вот, что происходит, когда не обрабатывают границы. они могут быть размазаны. Приведу пример изображения. тип 2 - это индикатор, и тип 4 - отражает свет от неба, немного синенький от того, что ночь в игре.
в фотошопе видно, что не замазали края.
Но на удивление, некоторые изображения могут работать без багов (можно указать тип 2 или какой нибудь. но так не всегда бывает. если с изображением повезет).
Связано, это с типами. Однако, не все типы хорошо работают.
Просто в 4 типе идет отражение от неба, поэтому полосы и образовались в том месте. Мне кажется, что если бы замазать границы прозрачным слоем, то такого бы не было
  1. Правильно импортировать текстуру. Об этом хочу поговорить еще раз. Дело в том, что в свое время я сильно настрадался из этого, и все мои тесты были ошибочны.
До написании этой статьи я тестировал. При тесте пробовал создать текстуру tga. А потом тоже самое пробовал blp. Искал ошибки или почему та не работает. Короче, в одной blp-текстуре указывал путь
"war3mapImported\block 4x4.blp" - и иконка работала
Затем в карту импортировал tga-текстуру
"war3mapImported\block 4x4.tga" - такое нельзя делать. У обоих текстур одинаковый путь и название. И заменит игра на какую то одну, даже, если в триггерах указан в пути формат .blp или .tga. Ибо в один прекрасный день решил тестировать только tga, заменяя, тк ими проще, не нужно конвертить. И все время показывала игра, что работает. А когда blp удалил, оказывалось, что сильно ошибался. Тогда мелкие изменения вносил, и трудно заметить было отличия одной от другой
  1. размытие изображения при растяжении (масштабировании).
Давайте-ка просто примеры рассмотрим
images 4x4 pixels
Создаем картинку 4x4 пикселей без прозрачной рамки
Этот квадрат почему-то не хочет создаваться норм.
Изменяю размеры до 126x126. Можно было бы это оставить, но не знаю точно. Варкрафт предъявляет обязательно требование рамкой обводить, иначе растягивается. Вдруг завтра текстура не будет работать. Хотя кто знает. Мб пригодится для индикатора, где нужно чиста изображение одним цветом.
С добавлением альфа-канала текстура не растягивается куда-то вправо-верхний угол
Теперь все отображается. С альфа-каналом может происходить такое:
Вывод: это происходит из-за растяжения импортной картинки до заданных размеров. То есть мы растянули из 4 до 128. Если бы не альфа-канал, размытия не было никакого.
Еще больший размер вызывает еще большее размытие:
image 8x8 pixels
Пробовал создать другую текстуру 8x8 pixels.
Без прозрачной рамки растягивается изображение. Показываю повторно, чтобы понять из-за чего. Если от требуемого размера отнять 1-2 пикселя на каждую сторону, то такого размытия не будет, как с квадратом 4x4 пикс.
Добавляем прозрачную рамку толщиной 1 пиксель
То тут тоже самое происходит, но размытия по краям уже меньше
Вывод: чем выше разрешение картинки, тем меньше размытия.
Такие ошибки можно замечать у текстур (это происходят и с альфой, и без альфы). Сравните два изображения, чем выше качество, тем выше отрисовка:
тут края смазаны от заливки. Если в редакторе увеличить размеры до пикселя. Ниже скрин, я не смог вам целое изображение показать, тк на нем не видно этого.
На самом деле не стоит переживать об этом. Везде есть эти смазывания/смешивания на переходе цветов. Это норма. Просто нужно качество разрешения картинки повысить. Если размер image слишком превышает размеры импортной текстуры, то пользователь замечает эти пиксели.
Проще с изображениями как квадрат/прямоугольник, где окрашен одним цветом. Но вы видели, что происходит квадратиком 4x4 с добавлением альфа-канала при масштабировании. Мб стоит не добавлять эту прозрачную рамку?
еще одна вещь с размытием краев
причина во всем в заливке в paint.net. В фотошопе такого не происходило бы. Когда заливал цветом квадрат, она зачем-то еще мне края смазывает. На белом цвете это было не видно, а вот на синем.
Полупрозрачные края можно удалить. Выделяем прямоугольником, и удаляем выделенную область через кнопку “delete”.
  1. Особенность Image, или точнее игры. Весь показ / прорисовка image, или как это называется Image Render завязана на точке создания image. Если в экран игрока попадает эта точка, то игра прорисовывает image. Стоит сдвинуть экран, и Image исчезает (это заметно на больших изображениях).
Это не только происходит с Image, но и с моделями. То что не влезает (или точнее центр изборажения не попадает в экран), то не прорисовывается. пример
это ограничения введены специально, дабы уменьшить подвисания игры. Чем больше объектов отрисовывается на экране, тем больше падает fps. В рефордже можно включить, введя в чат: "/fps"
Есть еще нюанс, пример, отображение радиуса атаки юнита. Если юнит стоит у границы карты, и размер картинки большой, что даже вылезает за пределы карты, то она не отобразится.
Жаль нельзя отобразить эффекты. Пример, подсвечить цветом игрока землю при выборе земли для застройки здания. В моем проекте, я разбил всю область на прямоугольники. Но они слишком большими получились. И если центр какого-то изображения не попадает в экран, то оно не отрисовывает.
Есть большие извращенные решения - разбивать текстуры на еще более мелкие пиксели. Однако, тут тоже не все просто. Если создать очень много в одном месте, то fps сильно упадет. Если отвести подальше камеру от того места, то fps возвращается в норм. Как понимаете - сильно нагружать не стоит.

Попиксельная графика

Начнем с самых простых форм в качестве примера - а именно с квадрата, прямоугольника. можно точками циклом заполнить каждый вертикальный или горизонтальный ряд пока не упретесь в границы квадрата/прямоугольника
отрисовка простого квадрата/прямоугольника
код отрисовки квадрата/прямоугольника
function CreateRectangleShapeImage(file,size,cx,cy,ax,by,imagetype)
    
    local minx,miny,maxx,maxy = cx-ax,cy-by,cx+ax,cy+by
    local step = (size/2)
    local x,y = minx+step,miny+step

    local sum = 0
    
    --if ((ax*2)/step) * ((by*2)/step) < 4000 then --ограничение
        while x<maxx do
            y = miny+step
            while y<maxy do
                CreateSquareImage(file,size,x,y,imagetype) 
                SetImageColor(bj_lastCreatedImage, 0, 255, 0, 80)
                sum=sum+1
                y=y+step
            end
            x=x+step
        end
    --else
       -- print('ДОСТИГНУТ ЛИМИТ')
    --end
    
    print('sum: '..sum..' , t: '..((ax*2)/step)*((by*2)/step))
end

function CreateSquareShapeImage(file,size,cx,cy,a,imagetype)
    CreateRectangleShapeImage(file,size,cx,cy,a,a,imagetype)
end
прямоугольник c полусторонами 512x256 (1024x512) из мелких пикселей 128x128. (всего 128 пикселей)
квадрат с полусторанами 512x512 (1024x1024) из мелких пикселей 128x128. (всего 256 пикселей)
Можем еще круг нарисовать. Используется тот же самый алгоритм перебора точек прямоугольника, но здесь с некоторым отличием - каждую точку чекаем на попаданием в круг. Есть специальная формула проверки "принадлежит кругу/лежит ли точка в круге". С помощью теоремы Пифагора. (cx-x)^2+(cy-y)^2)^0.5
отрисовка круга
код отрисовки круга
function CreateCircleShapeImage(file,size,cx,cy,radius,imagetype)
    
    local minx,miny,maxx,maxy = cx-radius,cy-radius,cx+radius,cy+radius
    local step = (size/2)
    local x,y = minx-step,miny-step

    local sum = 0
    
    --if (radius^2)*math.pi < 4000 then
        while x<maxx+step do
            y = miny-step
            while y<maxy+step do
                if((cx-x)^2+(cy-y)^2)^0.5 < radius then
                    CreateSquareImage(file,size,x,y,imagetype) 
                    SetImageColor(bj_lastCreatedImage, 0, 255, 0, 80)

                    sum=sum+1
                end
                y=y+step
            end
            x=x+step
        
        end
    --else
    --    print('ДОСТИГНУТ ЛИМИТ')
    --end
    
    print('sum: '..sum..' , S: '..((radius/step)^2)*math.pi)
end
Круг c радиусом 512 из пикселей 128x128 (197 пикселей)
А что если я хочу добавить окружность (кольцо), а не круг. Или изменить цвет? тут тогда надо немного переделать код:
переделанный код отрисовки круга

player = {}

for a=1, 12 do
	player[a] = {}
	player[a].shape = {}
end

function DestroyShapeImage(data,num)
   local sum = #data[num].images
   
   for a=1,sum do
        DestroyImage(data[num].images[a])
   end
    table.remove(data,num)
end

function ColorShapeImage(data,num,r,g,b,alpha)
   local sum = #data[num].images
   
   print(sum)
   
   for a=1,sum do
        SetImageColor(data[num].images[a], r, g, b, alpha)
   end
   
end

function CreateCircleShapeImage(data,file,size,cx,cy,radius,imagetype,line_width,color1,color2)
    
    param={}
    param.type_shape=1 --тип фигуры
    param.images={} --общие пиксели (удалить, сдвинуть, переместить, сдвинуть)
    param.images_color1={} --пиксели одного цвета
    param.images_color2={} --пиксели другого цвета
    
    local radius2
    if line_width then
        if line_width < size then
            line_width = size
        end
        radius2 = radius-(line_width/2)
    end
    
    
    local minx,miny,maxx,maxy = cx-radius,cy-radius,cx+radius,cy+radius
    local step = (size/2)
    local x,y = minx-step,miny-step

    local a1,r1,g1,b1,a2,r2,g2,b2
    if color1 then
        r1,g1,b1,a1 = hex2rgba(color1)
    end
    if color2 then
        r2,g2,b2,a2 = hex2rgba(color2)
    end

    local sum = 0
    local count = 0
    
    if color1 and type(color2)=='nil' then --внутренний круг
        count=((radius2/step)^2)*math.pi
    elseif (color2 and type(color1)=='nil') then --кольцо, контур круга
        count=(((radius/step)^2)*math.pi) - (((radius2/step)^2)*math.pi)
    else --полный круг
        count=((radius/step)^2)*math.pi
    end

    --if count < 4000 then
        while x<maxx+step do
            y = miny-step
            while y<maxy+step do
                if((cx-x)^2+(cy-y)^2)^0.5 < radius then
                    --AddSpecialEffect('land_1.mdx',x,y)
                    
                    
                    
                    if line_width then
                        if ((cx-x)^2+(cy-y)^2)^0.5 < radius2 then
                            if color1 then --внутренний круг
                                param.images[sum+1]=CreateSquareImage(file,size,x,y,imagetype) 
                                param.images_color1[sum+1]=bj_lastCreatedImage
                                SetImageColor(bj_lastCreatedImage, r1, g1, b1, a1)
                                sum=sum+1
                            end
                        else
                            
                            if color2 then --кольцо, контур круга
                                param.images[sum+1]=CreateSquareImage(file,size,x,y,imagetype)
                                param.images_color2[sum+1]=bj_lastCreatedImage
                                SetImageColor(bj_lastCreatedImage, r2, g2, b2, a2)
                                sum=sum+1
                            elseif type(color1)=='nil' and type(color2)=='nil' then --если цвет кольца color2 не задан, то все равно создаем (сбивает счетчик)
                                param.images[sum+1]=CreateSquareImage(file,size,x,y,imagetype)
                                sum=sum+1
                            end
                        end
                    else
                        param.images[sum+1]=CreateSquareImage(file,size,x,y,imagetype) 
                        sum=sum+1
                    end
                    
                    
                end
                y=y+step
            end
            x=x+step
        
        end
    --else
    --    print('ДОСТИГНУТ ЛИМИТ')
    --end
    if sum>0 then
        table.insert(data,param)
    else
        param.images=nil
        param.images_color1=nil 
        param.images_color2=nil 
        param=nil
    end
    print('sum: '..sum..' , S: '..count)
    
    return #data  --номер фигуры
end
CreateCircleShapeImage(data,file,size,cx,cy,radius,imagetype) --отрисовка полного круга
пример: отрисовка круга с радиусом 512 из пикселей размером 128x128, центр которого является (cx=0.cy=0). В сумме получилось 197 пикселей.
CreateCircleShapeImage(player[1].shape,"war3mapImported\\block 32x32.TGA",128,0,0,512,2)
ColorShapeImage(player[1].shape,1, 255, 0, 0, 80) --задаем красный цвет
CreateCircleShapeImage(data,file,size,cx,cy,radius,imagetype,line_width) --отрисовка контура/кольца/окружности
пример: отрисовка круга с радиусом 512 из пикселей размером 128x128, центр которого является (cx=0.cy=0). здесь мы задаем толщину контура = 128. В сумме получилось 48 пикселей.
CreateCircleShapeImage(player[1].shape,"war3mapImported\\block 32x32.TGA",128,0,0,512,2,128)
ColorShapeImage(player[1].shape,1, 255, 0, 0, 80) --задаем зеленый цвет
Можно еще задать два цвета: color1 - внутреннему кругу, так и color2 - внешнему кольцу.
CreateCircleShapeImage(data,file,size,cx,cy,radius,imagetype,line_width,color1,color2) --отрисовка цвета +у каждого цвета свой набор пикселей (можно потом написать функции перекраски этих пикселей)
CreateCircleShapeImage(player[1].shape,"war3mapImported\\block 32x32.TGA",128,0,0,512,2,256,'8000ff00','b100ff00')
можно color1 = цвет внутреннему кругу не указывать, тогда останется кольцо. пиксели внутреннего круга не создаются.
CreateCircleShapeImage(data,file,size,cx,cy,radius,imagetype,line_width,nil,color2)
можно и наоборот, не указывать цвет внешнего кольца, тогда останется только внутренний круг. пиксели внешнего кольца не создаются.
CreateCircleShapeImage(data,file,size,cx,cy,radius,imagetype,line_width,color1,nil)
можно реализлвать поворот круга, но круг по сути круглый. Под каким углом не поверни, он всегда будет круглым. Таково его свойство. Даже теорема Пифагора, определяющая расстояние от центра, не поможет.
раскрыть
Но пиксели по сути квадратные, и какие части торчат снаружи, их все же можно повернуть. есть формула определения координат при повороте. ссылка
Однако, у меня в расчет идет, что все пиксели будут лежать строго в сетке. потом надо будет относительно повернутой точки расчитать координату сетки.
function RoundEx(num)
  local inv = math.floor(math.abs(num) + 0.5)
    return num < 0 and inv * -1 or inv
end

function CoordGride(size,cord)

    --size всегда полож число
    if size>0 then
        return RoundEx(cord/size)*size
    else
        return 0
    end
end
Если только рисунок внутри круга не изменится
отрисовка эллипса (рисует круг, так и овал. можно рисовать круг под углом)
код
Каноническое уравнение эллипса:
x^2 / a^2 + y^2 / b^2 = 1
a и b - полуоси
Если x^2 / a^2 + y^2 / b^2 <= 1 - внутри
Если x^2 / a^2 + y^2 / b^2 < 1 - внутри, без границы
--принадлежность точки эллипсу
--благодарность Prometheus в помощи
function IsPointInEllipse(x, y, cx, cy, a, b, angle)
  x = x - cx
  y = y - cy
  if angle then
    -- (xcosz + ysinz)^2 / a^2 + (ycosz - xsinz)^2 / b^2 = 1
    local sinz = math.sin(angle)
    local cosz = math.cos(angle)
    return (x * cosz + y * sinz)^2 / a^2 + (y * cosz - x * sinz)^2 / b^2 <= 1
  else
    return x^2 / a^2 + y^2 / b^2 <= 1
  end
end

--создаем эллипс. Если угол angle не нужен, то его можно не указывать
function CreateEllipseShapeImage(file,size,cx,cy,rx,ry,imagetype,angle)
    
    local minx,miny,maxx,maxy = cx-rx,cy-ry,cx+rx,cy+ry
    local step = (size/2)
    local x,y = minx-step,miny-step

    local sum = 0
    
    --if (math.pi*rx*ry/step^2) < 4000 then
        while x<maxx+step do
            y = miny-step
            while y<maxy+step do
                if IsPointInEllipse(x,y,cx,cy,rx,ry,angle) then
                    --AddSpecialEffect('land_1.mdx',x,y)
                    
                    CreateSquareImage(file,size,x,y,imagetype) 
                    SetImageColor(bj_lastCreatedImage, 0, 255, 0, 80)

                    sum=sum+1
                end
                y=y+step
            end
            x=x+step
        
        end
    --else
    --    print('ДОСТИГНУТ ЛИМИТ')
    --end
    
    print('sum: '..sum..' , S: '..(math.pi*rx*ry/step^2))
end
Этот код неплохо создает круг. Но с таким же успехом можно и овал создать.
Эллипс с радиусами 512x256 из мелких пикселей 128x128 (97 пикселей)
тот ж самый эллипс, но повернутый на угол 30 градусов (не забудьте перевести в радианы math.rad(30))
Однако, тут проблема у эллипса, как мы видим на картинке. Тут неправильно определены размеры прямоугольной области, где задают пиксели эллипса. Поэтому часть эллипса по краям обрезается. Такие размеры будут правильны только в том случае, если эллипс не повернут на какой то угол. Тут нужно брать половину диагонали прямоугольника, как самый максимальный размер в ширину и высоту.
слегка переделанный код эллипса
--округление в большую/меньшую сторону
--в большую: 0.5 => 1.0 or 0.3 => 0.0
--в меньшую: -0.5 => -1. or -0.3 => 0.0
function RoundEx(num)
  local inv = math.floor(math.abs(num) + 0.5)
    return num < 0 and inv * -1 or inv
end

function CoordGride(size,cord)

    --size всегда полож число
    if size>0 then
        return RoundEx(cord/size)*size
    else
        return 0
    end
end

function CreateEllipseShapeImage(data,file,size,cx,cy,rx,ry,imagetype,angle,line_width,color1,color2)

    param={}
    param.type_shape=1 --тип фигуры
    param.images={} --общие пиксели (удалить, сдвинуть, переместить, сдвинуть)
    param.images_color1={} --пиксели одного цвета
    param.images_color2={} --пиксели другого цвета
    
    local S = (math.pi*rx*ry) --площадь эллипса
    
    local a,b 
    if line_width then
        if size>line_width then
            line_width = size
        end
        a,b = rx-line_width,ry-line_width --полуоси внутреннего эллипса
    
        if color2 and type(color1)=='nil' then
            S=(math.pi*a*b) --площадь внутреннего эллипса
        elseif type(color2)=='nil' then
            S=S-(math.pi*a*b) --площадь внешнего контура
        end
    end
    
    local a1,r1,g1,b1,a2,r2,g2,b2
    if color1 then
        r1,g1,b1,a1 = hex2rgba(color1)
    end
    if color2 then
        r2,g2,b2,a2 = hex2rgba(color2)
    end
    
    local step = (size/2)
    local minx,miny,maxx,maxy = cx-rx,cy-ry,cx+rx,cy+ry
    
    if angle then
        local dg = CoordGride(step,(rx^2+ry^2)^0.5) --диагональ
        minx,miny,maxx,maxy = cx-dg,cy-dg,cx+dg,cy+dg
    end
    local x,y = minx-step,miny-step
    local sum = 0
    

    
    --if (S/step^2) < 4000 then
        while x<maxx+step do
            y = miny-step
            while y<maxy+step do
                if line_width then
                    if IsPointInEllipse(x,y,cx,cy,rx,ry,angle) then
                    
                        if (not IsPointInEllipse(x,y,cx,cy,a,b,angle)) then
                                
                            if color1 then --внешний контур
                                param.images[sum+1]=CreateSquareImage(file,size,x,y,imagetype) 
                                param.images_color1[sum+1]=bj_lastCreatedImage
                                SetImageColor(bj_lastCreatedImage, r1, g1, b1, a1)
                                sum=sum+1
                            elseif type(color1)=='nil' and type(color2)=='nil' then 
                                param.images[sum+1]=CreateSquareImage(file,size,x,y,imagetype) 
                                sum=sum+1
                            end
                        else
                            if color2 then --внутренний эллипс
                                param.images[sum+1]=CreateSquareImage(file,size,x,y,imagetype) 
                                param.images_color2[sum+1]=bj_lastCreatedImage
                                SetImageColor(bj_lastCreatedImage, r2, g2, b2, a2)
                                sum=sum+1
                            end
                        end
                    end
                        
                else
                    if IsPointInEllipse(x,y,cx,cy,rx,ry,angle) then
                        --AddSpecialEffect('land_1.mdx',x,y)
                        
                        param.images[sum+1]=CreateSquareImage(file,size,x,y,imagetype) 
                        sum=sum+1
                    end
                end
                y=y+step
            end
            x=x+step
        
        end
    --else
    --    print('ДОСТИГНУТ ЛИМИТ')
    --end
    if sum>0 then
        table.insert(data,param)
    else
        param.images=nil
        param.images_color1=nil 
        param.images_color2=nil 
        param=nil
    end
    
    print('sum: '..sum..' , S: '..(S/step^2))
    return #data --номер фигуры
end
CreateEllipseShapeImage(data,file,size,cx,cy,rx,ry,imagetype) - чтобы создать круг из эллипса, размеры rx,ry должны одинаковыми быть.
пример, создаем эллипс-круг с радиусом 512, состоящих из мелких пикселей 128x128.
CreateEllipseShapeImage(player[1].shape,"war3mapImported\\block 32x32.TGA",128,0,0,512,512,2)
ColorShapeImage(player[1].shape,1, 0, 255, 0, 80) --перекрашиваем в зеленый
пример, создаем эллипс-овал с полуосями 512x256 или 256x512, состоящих из мелких пикселей 128x128.
CreateEllipseShapeImage(player[1].shape,"war3mapImported\\block 32x32.TGA",128,0,0,512,256,2)
ColorShapeImage(player[1].shape,1, 0, 255, 0, 80) --перекрашиваем в зеленый
CreateEllipseShapeImage(player[1].shape,"war3mapImported\\block 32x32.TGA",128,0,0,256,512,2)
ColorShapeImage(player[1].shape,1, 0, 255, 0, 80) --перекрашиваем в зеленый
можно повернуть эллипс на какой-то угол. сначала попробовал круг-эллипс повернуть, но особых изменении не обнаружено, на какой бы угол не поворачивал. А вот овал совсем другое дело. Отличается от первого рисунка. Внимание: мне все еще казалось, что где-то по краям до сих пор урезаются пиксели. пробовал увеличить прямоугольную область задания пикселей, дабы посмотреть есть ли урезка. но никак рисунок никаких изменени не показывает - а значит, все правильно.
CreateEllipseShapeImage(player[1].shape,"war3mapImported\\block 32x32.TGA",128,0,0,512,256,2,math.rad(30))
ColorShapeImage(player[1].shape,1, 0, 255, 0, 80) --перекрашиваем в зеленый
Можно задать контур круга. Задал толщину контура = ноль, но код крректирует ее равной заданному size.
CreateEllipseShapeImage(player[1].shape,"war3mapImported\\block 32x32.TGA",128,0,0,512,512,2,nil,0)
ColorShapeImage(player[1].shape,1, 0, 255, 0, 80) --перекрашиваем в зеленый
контур эллипса
CreateEllipseShapeImage(player[1].shape,"war3mapImported\\block 32x32.TGA",128,0,0,512,256,2,nil,0)
ColorShapeImage(player[1].shape,1, 0, 255, 0, 80)
или задание цветов color1 и color2 - внутреннего эллипса и внешнего контура эллипса. если цвет не указан, то данные пиксели могут не создаться.
CreateEllipseShapeImage(player[1].shape,"war3mapImported\\block 32x32.TGA",128,0,0,512,256,2,nil,0,'8000ff00','b100ff00')
эллипс без угла, и эллипс под углом 45 град
отрисовка сектора круга (конуса)
"конусом" называть это неправильно, тк конус в большинстве случаев это 3d-фигура. Круг можно разбить на "угловые" секторы.
вики
пример
код углового сектора круга
function IsPointInSector(x,y,cx,cy,orientation,width,radius,deg)
    local lenght= ((x-cx)^2 + (y-cy)^2)^0.5
    
    local cosa
    local sina
    
    if deg then --ввод только deg
        orientation= math.rad(orientation)
        cosa = math.cos(orientation)
        sina = math.sin(orientation)
    else
        cosa = math.cos(orientation)
        sina = math.sin(orientation)
    end
    
    local angle= math.acos(cosa*(x-cx) / lenght+ sina*(y-cy) / lenght)
    angle = math.deg(angle)
    
    return ( angle <= width / 2 ) and ( lenght >= radius )

    --по идее lenght <= radius - внутри сектора, а нам нужно чтобы снаружи сектора не выходило. поэтому меняем на (lenght >= radius)
end
Хотите сделать собственный "фонарик". Или видимость противника, задать свой обзор видимости юниту. Помню, как сам учился в одной статье про векторы И у меня осталось в памяти это
В интернете можно найти полно других формул 2D геометрических фигур.
Пример треугольник. существуют методы определения принадлежности точки треугольнику
Метод сравнения площадей
--Площадь треугольника по координатам с помощью формулы герона
function TriS(x1,y1,x2,y2,x3,y3)
    return RAbsBJ(x1*(y2-y3)+x2*(y3-y1)+x3*(y1-y2))/2
end

--Метод сравнения площадей: Принадлежность точки (x;y) треугольнику (x1;y1);(x2;y2);(x3;y3).
function IsCoordsInTriangle(x,y,x1,y1,x2,y2,x3,y3)
    return R2I(TriS(x1, y1, x2, y2, x3, y3))==R2I(TriS(x1, y1, x2, y2, x, y)+TriS(x2, y2, x3, y3, x, y)+TriS(x1, y1, x3, y3, x, y))
end
Метод относительности
Тут координаты нужно вводить в строгом порядке.
ссылка
	--Принадлежность точки (aPx,aPy) треугольнику (aAx,aAy), (aBx,aBy), (aCx,aCy)  методом относительности положения
	function IsPointIn_Relat(aPx, aPy, aAx, aAy, aBx, aBy, aCx, aCy, orientation)

		--функция определения точки относительно вектора
		local function Q(ax, ay, bx, by, atx, aty)
			return atx * (by - ay) + aty * (ax - bx) + ay * bx - ax * by
		end
		--выбираем определённую ориентацию по вершинам(чтоб было по порядку)
		
		if orientation == 1 or type(orientation)=='nil' then
			-- универсальный
			local q1 = Q(aAx, aAy, aBx, aBy, aPx, aPy)
			local q2 = Q(aBx, aBy, aCx, aCy, aPx, aPy)
			local q3 = Q(aCx, aCy, aAx, aAy, aPx, aPy)
			return ((q1 >= 0) and (q2 >= 0) and (q3 >= 0)) or ((q1 < 0) and (q2 < 0) and (q3 < 0))
		elseif orientation == 2 then
			--для строгой ориентации по часовой
			local q1 = Q(aAx, aAy, aBx, aBy, aPx, aPy)
			local q2 = Q(aBx, aBy, aCx, aCy, aPx, aPy)
			local q3 = Q(aCx, aCy, aAx, aAy, aPx, aPy)
			return (q1 >= 0) and (q2 >= 0) and (q3 >= 0)
		if orientation == 3 then
			--для строгой ориентации против часовой
		  	local q1 = Q(aAx, aAy, aBx, aBy, aPx, aPy)
			local q2 = Q(aBx, aBy, aCx, aCy, aPx, aPy)
			local q3 = Q(aCx, aCy, aAx, aAy, aPx, aPy)
			return (q1 <= 0) and (q2 <= 0) and (q3 <= 0);
		end
	end
Векторный метод
После всех оптимизаций формул, как всё сошлось, я реализовал его в коде, где он показал себя вполне успешным и результативным. Аж эффективнее 2-х предыдущих методов:
Алгоритм такой:
  1. одну вершину треугольника помещаем в координаты (0;0);
  2. две стороны, выходящие из этой вершины, представляем как вектора.
Таким образом из всего этого появляется система простых условий нахождения точки P между векторами b и c
--Векторный метод: Принадлежность точки (aPx,aPy) треугольнику (aAx,aAy), (aBx,aBy), (aCx,aCy)
function IsPIn_Vector(aPx, aPy, aAx, aAy, aBx, aBy, aCx, aCy)

  --переносим треугольник точкой А в (0;0).
  local Bx,By = aBx-aAx,aBy-aAy
  local Cx,Cy = aCx-aAx,aCy-aAy
  local Px,Py = aPx-aAx,aPy-aAy
  local m = (Px*By - Bx*Py)/(Cx*By - Bx*Cy)
  
  if (m >= 0) and (m <= 1) then
    local l = (Px - m*Cx) / Bx
    if (l >= 0) and ((m + l) <= 1) then
      return true
	end
  end
end
Метод сложения углов
Мы считаем, что вершины даны в порядке обхода. Будем последовательно рассматривать углы с вершиной в точке P (для которой определяем положение) и лучами, проходящими через соседние вершины многоугольника. Теперь, если просуммировать все эти углы по порядку (как ориентированные углы), то получится некоторая величина ψ
В том случае, если точка лежит внутри многоугольника, ψ=±2π. Иначе ψ=0
Тут в сумме должен получится 360 градусов. Если мы не получаем такого кол-ва, значит, либо у вас неправильный многоугольник.
Метод геометрического луча
Это достаточно известный метод, особенно когда определяется принадлежность точки многоугольникам. Часто данный метод называют «трассировка луча», хотя это не совсем правильно, т.к. трассировка луча — это расчёт хода световых лучей в 3D сцене.
Суть в том, что из данной точки пускается луч по какой-либо оси в каком-либо направлении.
Затем проверяются пересечения со сторонами многоугольника и ведётся подсчёт пересечений.
Таким образом если кол-во пересечений чётное, то значит точка лежит вне многоугольника, если же кол-во НЕчётное, то значит точка лежит внутри.
--метод геометрического луча: Принадлежность точки (aPx,aPy) треугольнику (aAx,aAy), (aBx,aBy), (aCx,aCy)
function IsPIn_RayCast(aPx, aPy, aAx, aAy, aBx, aBy, aCx, aCy)	
	local cros_cnt = 0; -- кол-во пересечений граней (лучом из точки)
	-- луч пускаем по оси X вправо
	-- 1-я сторона треугла
	local s2 = (aPy - aAy) / (aBy - aAy)
	local s1 = aAx - aPx + s2 * (aBx - aAx)
	if (s1 >= 0) and (s2 >= 0) and (s2 <= 1) then
		cros_cnt=cros_cnt+1
	end
	--2-я сторона треугла
	s2 = (aPy - aBy) / (aCy - aBy)
	s1 = aBx - aPx + s2 * (aCx - aBx)
	if (s1 >= 0) and (s2 >= 0) and (s2 <= 1) then
		cros_cnt=cros_cnt+1
	end
	-- 3-я сторона треугла
	if cros_cnt < 2 then
		s2 = (aPy - aCy) / (aAy - aCy)
		s1 = aCx - aPx + s2 * (aAx - aCx)
		if (s1 >= 0) and (s2 >= 0) and (s2 <= 1) then
			cros_cnt=cros_cnt+1
		end
	end
	return cros_cnt == 1
end
Эти методы эффективны не только для вычисления принадлежности точки треугольнику. Его можно использовать и для вычисления любого многоугольника, только чутка подправить, и рассматреть случаи.
отрисовка многоугольника
нам нужны для примера создать вершины многоугольника. Специально для своей задачи создал "неправильный не выпуклый многоугольник". Нужно было вершины задать для проверки
вершины
	print('начало')
	param={}
    
    param.vertexs = {}
    param.vertexs.x = {}
    param.vertexs.y = {}
    
    param.vertexs.x[1]=450; param.vertexs.y[1]=450
    param.vertexs.x[2]=450; param.vertexs.y[2]=180
    param.vertexs.x[3]=150; param.vertexs.y[3]=180
    param.vertexs.x[4]=150; param.vertexs.y[4]=450
    param.vertexs.x[5]=200; param.vertexs.y[5]=450
    param.vertexs.x[6]=200; param.vertexs.y[6]=250
    param.vertexs.x[7]=400; param.vertexs.y[7]=250
    param.vertexs.x[8]=400; param.vertexs.y[8]=450
как вычислить площадь неправильного многоугольника
код
TimerStart(CreateTimer(),0.5,false,function()
	print('начало')
	param={}
    
    param.vertexs = {}
    param.vertexs.x = {}
    param.vertexs.y = {}
    
    param.vertexs.x[1]=450; param.vertexs.y[1]=450
    param.vertexs.x[2]=450; param.vertexs.y[2]=180
    param.vertexs.x[3]=150; param.vertexs.y[3]=180
    param.vertexs.x[4]=150; param.vertexs.y[4]=450
    param.vertexs.x[5]=200; param.vertexs.y[5]=450
    param.vertexs.x[6]=200; param.vertexs.y[6]=250
    param.vertexs.x[7]=400; param.vertexs.y[7]=250
    param.vertexs.x[8]=400; param.vertexs.y[8]=450
	
    local max = #param.vertexs.x
    print('max:',max)
    local sx,sy=0,0
    print('init sx,sy:',sx,sy)
    for a=max, 2, -1 do
        sx = sx+ (param.vertexs.x[a]*param.vertexs.y[a-1])
        sy = sy+ (param.vertexs.y[a]*param.vertexs.x[a-1])
        print(a..'-sx,sy:',sx,sy)
    end
    sx = sx+ (param.vertexs.x[1]*param.vertexs.y[max])
    sy = sy+ (param.vertexs.y[1]*param.vertexs.x[max])
    print('last sx,sy:',sx,sy)
    

    local S = math.abs((sx-sy)/2)
    print('S:',S)
end)
По коду получилось: 41000
Можно условно сосчитать площадь двух прямоугольников:
300 x 270 = 81000
200 x 200 = 40000
Как узнать направление обхода двухмерного многоугольника. по часовой стрелке или против направлена какая-либо тройка точек
Применение: В основном это определяет принадлежность точки многоугольнику в одном из методов с углами. Еще можно применить это знание для разбивки многоугольника с прямыми углами на тр-ки или прямоугольники. необходимо понимание, что тройка точек образует тр-к, который лежит внутри многоугольника. А не снаружи. Этот прием заприметил случайно, ища инфу про многоугольник. Помню, разбивал многоугольник на прямоугольники немного по-другому.
На рисунке показано разбивка многоугольника на прямоугольники. перебирают три точки. Нкжно смотреть, чтобы тре-к лежал внутри, а не снаружи. Главное условие, чтобы отрезки-диагонали от пр-ков не пересекались со сторонами многоугольника
  1. точки 123, 234 нельзя подобрать, тк диагональ пересекается
  2. точками 345 отсекаем прямоугольник, и эти точки удалем из бд массива. при этом образуется 9 точка, ее нужно вписать в массив точек (желательно в том порядке, каком многоугольник).
  3. точки 678 нельзя подбирать, тк диагональ снаружи
  4. точками 812 отсекаем прямоугольник, и эти точки удалем из бд массива. при этом образуется 10 точка, ее нужно вписать в массив точек (желательно в том порядке, каком многоугольник).
  5. в итоге остаются точки 6-7-10-9, которые образуют последний пр-к
функции
Функция, вычисляющая удвоенную знаковую площадь треугольника:
function triangle_area_2(x1,y1, x2,y2, x3,y3)
	return (x2 - x1) * (y3 - y1) - (y2 - y1) * (x3 - x1);
end
Функция, возвращающая обычную площадь треугольника:
function triangle_area(x1,y1, x2,y2, x3,y3)
	return math.abs(triangle_area_2 (x1, y1, x2, y2, x3, y3)) / 2.0;
end
Функция, проверяющая, образует ли указанная тройка точек поворот по часовой стрелке:
function clockwise(x1,y1, x2,y2, x3,y3)
	return triangle_area_2(x1, y1, x2, y2, x3, y3) < 0;
end
Функция, проверяющая, образует ли указанная тройка точек поворот против часовой стрелки:
function counter_clockwise(x1,y1, x2,y2, x3,y3)
	return triangle_area_2 (x1, y1, x2, y2, x3, y3) > 0;
end
Является ли многоугольник выпуклым
Многоугольник является выпуклым, тогда и только тогда, когда для каждого его ребра, все его вершины лежат по одну сторону от прямой, проведенной через это ребро.
мы применим функцию triangle_area из разделеа "Как узнать направление обхода двухмерного многоугольника".
определения на выпуклость многоугольника (собственный)
	print('начало')
	param={}
    
    param.vertexs = {}
    param.vertexs.x = {}
    param.vertexs.y = {}
    
    param.vertexs.x[1]=450; param.vertexs.y[1]=450
    param.vertexs.x[2]=450; param.vertexs.y[2]=180
    param.vertexs.x[3]=150; param.vertexs.y[3]=180
    param.vertexs.x[4]=150; param.vertexs.y[4]=450
	--для проверки можно стереть нижние координаты 5-8 точек, тогда многоугольник станет выпуклым
    param.vertexs.x[5]=200; param.vertexs.y[5]=450 
    param.vertexs.x[6]=200; param.vertexs.y[6]=250
    param.vertexs.x[7]=400; param.vertexs.y[7]=250
    param.vertexs.x[8]=400; param.vertexs.y[8]=450

	function triangle_area_2(x1,y1,x2,y2,x3,y3)
		return (x2 - x1) * (y3 - y1) - (y2 - y1) * (x3 - x1);
	end
	--возвращает индекс массива. сбрасывает к началу, если превышает
	--остаток от деления % не помогает, тк нумерация идет от 1, а не с 0
	function index(x,max)
		if x> max then
			x=x%max
			if x== 0 then return 1 end
		end
		return x
	end

	local max = #param.vertexs.x
	local a,b,c = 1,index(2,max),index(3,max) --индексы
	local x1,y1, x2,y2, x3,y3 --координаты трех точек
	local flag_clockwise
	local f = true
		
		x1,y1=param.vertexs.x[a],param.vertexs.y[a]
		x2,y2=param.vertexs.x[b],param.vertexs.y[b]
		x3,y3=param.vertexs.x[c],param.vertexs.y[c]
		flag_clockwise = (triangle_area_2(x1,y1, x2,y2, x3,y3) < 0)

	print('начало цикла')
	repeat
		
		x1,y1=param.vertexs.x[a],param.vertexs.y[a]
		x2,y2=param.vertexs.x[b],param.vertexs.y[b]
		x3,y3=param.vertexs.x[c],param.vertexs.y[c]
		
		if flag_clockwise ~= (triangle_area_2(x1,y1, x2,y2, x3,y3) < 0) then
			f = false
			print('на '..a..'-итерации прервана')
			break
		end
		print(a..'-итерация')
		a=a+1
		b,c = index(a+1,max),index(a+2,max)
	until a>=max

	print('выпуклый ли n-угольник:',f)
	print('конец')
определение на выпуклость (по гайду)
-- Получение знака параметра
-- Возвращает -1,0,1
function math.sign(value)
    return value < 0 and -1 or value > 0 and 1 or 0
end
--возвращает индекс массива. сбрасывает к началу, если превышает
--остаток от деления % не помогает, тк нумерация идет от 1, а не с 0
function index(x,max)
	if x> max then
		x=x%max
		if x== 0 then return 1 end
	end
	return x
end 
  
function GetVector(i,p)
	local max = #polygon.x
	if (i==max) then
		p.x=polygon.x[1]-polygon.x[max]
		p.y=polygon.y[1]-polygon.y[max]
	else
  		p.x=polygon.x[i+1]-polygon.x[i]
		p.y=polygon.y[i+1]-polygon.y[i]
    end
end

polygon = {}
polygon.x = {}
polygon.y = {}

polygon.x[1]= 0; polygon.y[1]= 6;
polygon.x[2]=-4; polygon.y[2]= 5;
polygon.x[3]=-5; polygon.y[3]= 2;
polygon.x[3]=-1; polygon.y[3]= 1; --невыпуклый

polygon.x[4]=-5; polygon.y[4]=-1;
polygon.x[5]=-2; polygon.y[5]=-4;
polygon.x[6]= 4; polygon.y[6]=-3;
polygon.x[7]= 6; polygon.y[7]= 1;
polygon.x[8]= 4; polygon.y[8]= 5;
polygon.x[8]= 1; polygon.y[8]= 1; --невыпуклый

local v1,v2={},{}
local N = #polygon.x

GetVector(N,v1);
GetVector(1,v2);
T=v1.x*v2.y-v2.x*v1.y;
Z=math.sign(T);
P=1.0;
i=1;
Q=true;
while (Q and (i<N)) do
	GetVector(i,v1);
	j = index(i+1,N)
	GetVector(i+1,v2);
	T=v1.x*v2.y-v2.x*v1.y;
	P=P*Z*math.sign(T)
	print('i=',i,'; T=',T,'; P=',P);
	print(i..'-итерация, точка '..i..'-'..j)
	if (P<0) then 
	    Q=false
	    print('вектор '..i..'-'..j..' меняет напр')
	end
	i=i+1
end
if Q then
print('Многоугольник выпуклый.')
else 
print('Многоугольник невыпуклый.');
end
Можно еще было сумму углов вычислять.
принадлежность точки многоугольнику (алгоритмы)
метод сравнения площадей
Этот метод использовался для определения принадлежности точки треугольнику. Но это будет работать только с выпуклыми многоугольниками (треугольники, квадраты, прямоугольники, пятиугольники итд).
Для этого метода нужно знать общую площадь, потом пройтись циклом по сторонам многоугольника, и сложить площади треугольников по формуле герона - так получить суммарную. Если общая площадь равна суммарной, то точка принадлежит многоугольнику. Однако, это метод может из-за плавающих точек выйти с погрешностями.
Метод для "не выпуклых" многоугольников, особенно вогнутых с отверстиями, может не подойти. Перебор сторон выделяет лишние участки (отверстия), которые не должны быть. Суммарная площадь получается больше. Здесь будет больше не точностей, нужны доп проверки (если можно, то я не знаю как)
Суммарная площадь должна быть равной 41000. Но из-за не точностей, площадь другая.
алгоритм из вики метода сравнения площадей
В чем суть алгоритма?
цикл перебирает 3 точки (n,n+1,n+2), и по этим трем точкам создаем общую площадь. А еще относительно этих трех точек и точки x,y рисует 3 площади (суммарная). тут тоже похожий принцип с сравнениями площадями тр-ков. вычисления и сравнения площадей треугольников заменяются вычислениями и сравнениями их удвоенных площадей. Если общая площадь равна суммарной, значит лежит внутри тре-ка. Иначе, цикл перебирает след 3 точки. И так до сих пор, пока не выяснится, лежит, ли точка внутри.
Короче, перебираем все тр-ки. И точка x,y должна попадать во все тр-ки. Если попала в один, цикл обрываем - выводим на экран, что принадлежит многоугольнику. Если не попала, то цикл обрывается тоже - выводим, что не попала.
Недостаток тот же: это работает с выпуклыми многоугольниками. Если взять мой многоугольник, то там не со всеми тр-ками работает верно.
Пример точка x300,y=220 не лежит в тре-ках точек (3,4,5), точек (4,5,6), точек (5,6,7), точек (6,7,8), точек (7,8,1), точек (8,1,2). Лежит внутри тре-ков точек (1,2,3), точек (2,3,4)
код
TimerStart(CreateTimer(),0.5,false,function()
	print('начало')
	param={}
    
    param.vertexs = {}
    param.vertexs.x = {}
    param.vertexs.y = {}
    
    param.vertexs.x[1]=450; param.vertexs.y[1]=450
    param.vertexs.x[2]=450; param.vertexs.y[2]=180
    param.vertexs.x[3]=150; param.vertexs.y[3]=180
    param.vertexs.x[4]=150; param.vertexs.y[4]=450
	--для проверки можно стереть нижние координаты 5-8 точек, тогда многоугольник станет выпуклым прямоугольником
    param.vertexs.x[5]=200; param.vertexs.y[5]=450
    param.vertexs.x[6]=200; param.vertexs.y[6]=250
    param.vertexs.x[7]=400; param.vertexs.y[7]=250
    param.vertexs.x[8]=400; param.vertexs.y[8]=450
	
	local function IsPointInsidePolygon(p,x,y)
		local i1, i2, n, N, S, S1, S2, S3, flag;
		N = #p.x;
		print('max points:',N)
		n = 1
		repeat
			flag = 0;
			if n < N then
				i1 = n + 1
			else
				i1 = 1
			end
			while (flag == 0) do
				i2 = i1 + 1;
				if (i2 >= N) then
					i2 = 1;
				end
				local a
				if n < N then
					a = n + 1
				else
					a = 1
				end
				if (i2 == a)then
					break;
				end
				S = math.abs(p.x[i1] * (p.y[i2] - p.y[n]) +p.x[i2] * (p.y[n] - p.y[i1]) +p.x[n]  * (p.y[i1] - p.y[i2]));
				S1 = math.abs(p.x[i1] * (p.y[i2] - y) +p.x[i2] * (y - p.y[i1]) +x * (p.y[i1] - p.y[i2]));
				S2 = math.abs(p.x[n] * (p.y[i2] - y) +p.x[i2] * (y - p.y[n])+x * (p.y[n] - p.y[i2]));
				S3 = math.abs(p.x[i1] * (p.y[n] - y) +p.x[n] * (y - p.y[i1]) +x * (p.y[i1] - p.y[n]));
				if (S == S1 + S2 + S3) then
					flag = 1;
					break;
				end
				i1 = i1 + 1;
				if (i1 >= N) then
					i1 = 1;
					break;
				end
			end
			
			if (flag == 0)then
				print(n,'exit')
				break;
			end	
		
			n=n+1
		until n<N --or (flag == 1)
		return flag;
	end

	local a = IsPointInsidePolygon(param.vertexs,300,220)
	if a == 1 then
		print('точка принадлежит многоугольнику')
	else
		print('точка не принадлежит многоугольнику')
	end
	
end)
метод относительности
Суть в том, что проверяется, где лежит точка относительно выбранного вектора (справа или слева). Тут все стороны в строгом порядке лежат, и циклом перебирают векторы-отрезки, и чекаем где расположена точка.
ссылка
мини-код для проверки
    --функция определения точки относительно вектора
		local function Q(ax, ay, bx, by, atx, aty)
			return atx * (by - ay) + aty * (ax - bx) + ay * bx - ax * by
		end
    
    local aPx,aPy=300,200
    local b = true
    
    local max = #param.vertexs.x
    if Q(param.vertexs.x[max], param.vertexs.y[max], param.vertexs.x[1], param.vertexs.y[1], aPx, aPy) < 0 then
        for a=1, max-1 do
            
			--если <0 - точка лежит справа от вектора
			-- >0 - точка лежит слева от вектора
			-- = 0 - точка лежит на одной прямой с вектором
            if Q(param.vertexs.x[a], param.vertexs.y[a], param.vertexs.x[a+1], param.vertexs.y[a+1], aPx, aPy) < 0 then
                print('точка x,y не лежит справа от отрезка '..a..'-'..a+1)
                b = false
                break
            end
        
        end
    else
        b = dalse
        print('точка x,y не лежит справа от отрезка'..max..'-1')
    end
    
    if b then
        print('точка x,y принадлежит многоугольнику')
    else
        print('точка x,y не принадлежит многоугольнику')
    end
Тут тоже метод подойдет для выпуклых многоугольников больше. Для не выпуклых многоугольника нужно вводить доп проверки.
В зависимости от направления можно делать проверки. Иду по часовой. Синие векторы-отрезки показывают, что точка x,y лежит справа. Что значит, находится внутри многоугольника. Оранжевые векторы-отрезки показывают, что тоже слева. Что рушит всю проверку. Из-за перегибов такое часто встречается.
метод трассировки луча (геометрический луч)
суть в том, что пускают луч в одну сторону. И проверяют пересекает ли луч отрезок. Циклом пробегаемся по всем сторонам многоугольника, и ведем подсчет пересечении. Если четное, значит, точка не принадлежит n-угольнику, лежит где-то снаружи. Если не четное, значит, что внутри.
заведём счётчик пересечений и проинициализируем его нулём (либо просто заведём переменную типа bool, показывающую чётность числа пересечений)
код счетчика
	print('начало')
	param={}
    
    param.vertexs = {}
    param.vertexs.x = {}
    param.vertexs.y = {}
    
    param.vertexs.x[1]=450; param.vertexs.y[1]=450
    param.vertexs.x[2]=450; param.vertexs.y[2]=180
    param.vertexs.x[3]=150; param.vertexs.y[3]=180
    param.vertexs.x[4]=150; param.vertexs.y[4]=450
    param.vertexs.x[5]=200; param.vertexs.y[5]=450
    param.vertexs.x[6]=200; param.vertexs.y[6]=250
    param.vertexs.x[7]=400; param.vertexs.y[7]=250
    param.vertexs.x[8]=400; param.vertexs.y[8]=450

--пускаем луч вправо
function GetRayX(p,rayY,a,b)
	return ( rayY - p.y[a] )*( p.x[b] - p.x[a] )/( p.y[b] - p.y[a] ) + p.x[a]
end

	--возвращает индекс массива. сбрасывает к началу, если превышает
	--остаток от деления % не помогает, тк нумерация идет от 1, а не с 0
	function index(x,max)
		if x> max then
			x=x%max
			if x== 0 then return 1 end
		end
		return x
	end
 
function NumIntersect(x,y, poly)
	local num = 0
	local max = #poly.x
	local j
	for i=1, max do
		j = index(i+1,max)
		if(( poly.x[i] > y )~=( poly.y[j] > y ))and( x < GetRayX(poly,y,i,j) )then
			num=num+1
		end
		print(i..'-итерация,точки '..i..'-'..j)
	end
	print('число пересечении точек: '..num)
	return num
end
-- полигон poly должен быть "замкнутым" - первая точка должна совпадать с последней
function InsidePoly(x,y, poly)
	local num = NumIntersect(x,y, poly)
	return math.fmod(num,2)~=0 --возвращает истину, если данное число (Number) нечетно
end

print('принадлежность точки polygon: ',InsidePoly(300,220,param.vertexs)) 
код из вики через bool
суть bool в том, что сначала в переменную bool инициируем false. А при кажом пересечении - инвентируем bool, т.е. меняем состояние bool = not bool
	print('начало')
	param={}
    
    param.vertexs = {}
    param.vertexs.x = {}
    param.vertexs.y = {}
    
    param.vertexs.x[1]=450; param.vertexs.y[1]=450
    param.vertexs.x[2]=450; param.vertexs.y[2]=180
    param.vertexs.x[3]=150; param.vertexs.y[3]=180
    param.vertexs.x[4]=150; param.vertexs.y[4]=450
    param.vertexs.x[5]=200; param.vertexs.y[5]=450 
    param.vertexs.x[6]=200; param.vertexs.y[6]=250
    param.vertexs.x[7]=400; param.vertexs.y[7]=250
    param.vertexs.x[8]=400; param.vertexs.y[8]=450

	--возвращает индекс массива. сбрасывает к началу, если превышает
	--остаток от деления % не помогает, тк нумерация идет от 1, а не с 0
	function index(x,max)
		if x> max then
			x=x%max
			if x== 0 then return 1 end
		end
		return x
	end

	--эта функция использует деление
	function pnpoly1(x,y,p)
		local c = false
		local max = #p.x
		local i,j = 1,2
		
		repeat

			if ((((p.y[i] <= y)and(y < p.y[j])) or ((p.y[j] <= y)and(y < p.y[i]))) and
				(((p.y[j] - p.y[i]) ~= 0) and (x > ((p.x[j] - p.x[i]) * (y - p.y[i]) / (p.y[j] - p.y[i]) + p.x[i])))) then
				  c = not c 
			end
			print(i..'-итерация, точки '..i..'-'..j..' flag:',c)
			i=i+1
			j=index(i+1,max)
		until i>max
		
		return c
	end
	--точно такая функция, только для быстроты функции заменяют деление на умножение
	function pnpoly2(x,y,p)
		local c = false
		local max = #p.x
		local i,j = 1,2
		
		repeat
		
			if ((
			(p.y[i] < p.y[j]) and (p.y[i] <= y) and (y <= p.y[j]) and
			((p.y[j] - p.y[i]) * (x - p.x[i]) > (p.x[j] - p.x[i]) * (y - p.y[i]))
			) or (
			(p.y[i] > p.y[j]) and (p.y[j] <= y) and (y <= p.y[i]) and
			((p.y[j] - p.y[i]) * (x - p.x[i]) < (p.x[j] - p.x[i]) * (y - p.y[i]))
			)) then
				  c = not c 
			end
			print(i..'-итерация, точки '..i..'-'..j..' flag:',c)
			i=i+1
			j=index(i+1,max)
		until i>max
		
		return c
	end

	print(pnpoly1(300,220,param.vertexs))
	print(pnpoly2(300,220,param.vertexs))
метод подсчета углов
Если у вас есть вершины, вы можете вычислить сумму углов, сделанных между тестовой точкой и каждой парой точек, составляющих многоугольник. Если это 2*pi,, то это внутренняя точка. Если оно равно 0, то это внешняя точка.
код
	print('начало')
	param={}
    
    param.vertexs = {}
    param.vertexs.x = {}
    param.vertexs.y = {}
    
    param.vertexs.x[1]=450; param.vertexs.y[1]=450
    param.vertexs.x[2]=450; param.vertexs.y[2]=180
    param.vertexs.x[3]=150; param.vertexs.y[3]=180
    param.vertexs.x[4]=150; param.vertexs.y[4]=450
    param.vertexs.x[5]=200; param.vertexs.y[5]=450
    param.vertexs.x[6]=200; param.vertexs.y[6]=250
    param.vertexs.x[7]=400; param.vertexs.y[7]=250
    param.vertexs.x[8]=400; param.vertexs.y[8]=450


	--возвращает индекс массива. сбрасывает к началу, если превышает
	--остаток от деления % не помогает, тк нумерация идет от 1, а не с 0
	function index(x,max)
		if x> max then
			x=x%max
			if x== 0 then return 1 end
		end
		return x
	end

function InsidePolygon(x,y,p)

	local max,angle,j=#p.x,0
	local x1,y1,x2,y2

	for i=1,max do
		j=index(i+1,max)
		print(i..'-итерация, точка '..i..'-'..j)
		x1 = p.x[i] - x
		y1 = p.y[i] - y
		x2 = p.x[j] - x
		y2 = p.y[j] - y
		angle =angle+ Angle2D(x1,y1,x2,y2);
	end

	if (math.abs(angle) < math.pi) then
		return false
	else
		return true
	end
end

--[[
   Return the angle between two vectors on a plane
   The angle is from vector 1 to vector 2, positive anticlockwise
   The result is between -pi -> pi
]]
function Angle2D(x1,y1,x2,y2)
	local dtheta,theta1,theta2;

	theta1 = math.atan2(y1,x1);
	theta2 = math.atan2(y2,x2);
	dtheta = theta2 - theta1;
	while (dtheta > math.pi )do
		dtheta = dtheta-(2*math.pi)
	end
	while (dtheta < -math.pi) do
		dtheta = dtheta+(2*math.pi)
	end

	return(dtheta)
end

print(InsidePolygon(300,220,param.vertexs))
определение кол-ва пересечение отрезка ab со сторонами полигона
по сути это проверка пересечения между отрезками. Проверка проходит даже, если один из концов касается другого (<=0).
надоело каждый раз писать одно и то же. поэтому появилась на свет эта функция.
применение: это мб пригодиться, например при разбивке многоугольника на треугольники, прямоугольники. Там была необходимость проверять, дабы отрезок ни с кем не пересекался. Также помогает делать обходы вокруг внешнего или внутреннего полигона (отверстия). Здесь надо лучом пускать в разные стороны, но я решил отрезками проверить. В основу лежит четность кол-ва: не четное - лежит внутри, четное - лежит снаружи или внутри отверстия.
function area(x1,y1,x2,y2,x3,y3)
	return (x2-x1) * (y3-y1) - (y2-y1) * (x3-x1);
end
 
function intersect_1 (a,b,c,d)
	if (a > b)then a,b=b,a end
	if (c > d)then c,d=d,c end
	return math.max(a,c) <= math.min(b,d);
end
 
function intersect(x1,y1,x2,y2,x3,y3,x4,y4)
	return intersect_1 (x1, x2, x3, x4)
		and intersect_1 (y1, y2, y3, y4)
		and area(x1,y1,x2,y2,x3,y3) * area(x1,y1,x2,y2,x4,y4) <= 0
		and area(x3,y3,x4,y4,x1,y1) * area(x3,y3,x4,y4,x2,y2) <= 0;
end

--mod работает хорошо в других ЯП, где нумерация идет с 0. в луа нумерация идет с 1
function index(x,max)
    if x> max then x=x%max
        if x== 0 then return 1 end
    elseif x<=0 then x=x%max
        if x== 0 then return max end
    end
    return x
end 
--кол-во пересечении отрезка со сторонами полигона
function CrossSegmentPolygon(x1,y1,x2,y2,p)
    local count,a,b = 0,1,1

    repeat
        a=b
        b=index(a+1,#p.x)
        print(' k'..a..'-'..b..' итерация')

        if intersect(x1,y1,x2,y2,p.x[a],p.y[a],p.x[b],p.y[b]) then
            print(' пересечение')
            count = count+1
        end
    until(a==#p.x)
    print(' конец k: '..a..'-итерация')
    return count
end

polygon={x={420,700,700,420},y={220,220,100,100}}
print('кол-во пересечении: '..CrossSegmentPolygon(450,20,450,300,polygon))
определение кол-ва пересечении сторон многоугольника
В некоторых моментах помонает понять есть ли пересечения между многоугольниками, дабы понять стоит ли дальше программе работать.
    poly = {x={},y={}}
    poly.x[1]=450; poly.y[1]=450
    poly.x[2]=450; poly.y[2]=180
    poly.x[3]=150; poly.y[3]=180
    poly.x[4]=150; poly.y[4]=450
    poly.x[5]=200; poly.y[5]=450
    poly.x[6]=200; poly.y[6]=250
    poly.x[7]=400; poly.y[7]=250
    poly.x[8]=400; poly.y[8]=450

--уголком нижнем праовом углу
--polygon={x={420,700,700,420},y={220,220,100,100}}
--уголком верхнем левом углу
--polygon={x={100,170,170,100},y={700,700,400,400}}
--прикасаются ребром
polygon={x={450,600,600,450},y={450,450,180,180}}
--прикасаются ребром
--polygon={x={450,600,600,450},y={450,450,200,200}}
--взаимодействие ребром
--polygon={x={150,450,150,450},y={180,180,1,1}}


--mod работает хорошо в других ЯП, где нумерация идет с 0. в луа нумерация идет с 1
function index(x,max)
    if x> max then x=x%max
        if x== 0 then return 1 end
    elseif x<=0 then x=x%max
        if x== 0 then return max end
    end
    return x
end 

--определение точки пересечении
function getPointOfIntersection(x1,y1,x2,y2,x3,y3,x4,y4)
	local d,da,db,ta,tb,dx,dy
	
	d = (x1 - x2) * (y4 - y3) - (y1 - y2) * (x4 - x3);
	da = (x1 - x3) * (y4 - y3) - (y1 - y3) * (x4 - x3);
	db = (x1 - x2) * (y1 - y3) - (y1 - y2) * (x1 - x3);
 
	if d==0 then
		return false
	end
 
	ta = da / d;
	tb = db / d;
 
	if (ta >= 0 and ta <= 1 and tb >= 0 and tb <= 1) then
		dx = x1 + ta * (x2 - x1);
		dy = y1 + ta * (y2 - y1);
 
		return dx,dy
	end
 
	return false
end

--кол-во пересечении сторон между многоугольниками
function CountCrossTwoPolygons(p1,p2)
	local b,j,x,y,bool
	local p={x={},y={}}
	for a=1, #p1.x do
		b=index(a+1,#p1.x)
        --print(a..'-'..b..' интерация')
		for i=1,#p2.x do
			j=index(i+1,#p2.x)
			--print(i..'-'..j..' интерация')

			x,y=getPointOfIntersection(p1.x[a],p1.y[a],p1.x[b],p1.y[b],p2.x[i],p2.y[i],p2.x[j],p2.y[j])
			
			if x and y then
			    
			    bool = false
			    for e=1, #p.x do
			        if x==p.x[e] and y==p.y[e] then
			            bool = true
			            break
			        end
			    end
			    if not bool then
			        p.x[#p.x+1]=x
			        p.y[#p.y+1]=y
			    end
		    end
		end
    end

    return #p.x
end

print('кол-во пересечении: '..CountCrossTwoPolygons(poly,polygon))
отрисовка равнобедренного треугольника
код
--создание равнобедренного треугольника
--тут еще можно угол поворота прикрутить для вершин относительно центра
function CreateIsoscelesTriangleShapeImage(file,size,cx,cy,radius,imagetype)

    local minx,miny,maxx,maxy = cx-radius,cy-radius,cx+radius,cy+radius
    local step = (size/2)
    local x,y = minx+step,miny+step

    local Ax,Ay=cx,maxy
    local Bx,By=minx,miny
    local Cx,Cy=maxx,miny
    
    local sum = 0
    
    --if TriS(Ax,Ay,Bx,By,Cx,Cy)/(step^2) <4000 then
        while x<maxx+step do
            y = miny+step
            while y<maxy+step do
                if IsPIn_Vector(x,y,Ax,Ay,Bx,By,Cx,Cy) then
                    CreateSquareImage(file,size,x,y,imagetype) 
                    SetImageColor(bj_lastCreatedImage, 0, 255, 0, 80)

                    sum=sum+1
                end
                y=y+step
            end
            x=x+step
        
        end
    --else
    --    print('ДОСТИГНУТ ЛИМИТ')
    --end
    
    print('sum: '..sum..' , S: '..TriS(Ax,Ay,Bx,By,Cx,Cy)/(step^2))

end
Треугольник, в вписанный в квадрат. Радиусом 512x512 из мелких пикселей 128x128.
отрисовка повернутого квадрата/прямоугольника +задание контура
если я захочу нарисовать повернутый квадрат/прямоугольник на 30 градусов. мне пришлось сильно переделать в отличии от простого прямоугольника, где все просто. Тут нужно повернуть 4 вершины, наш квадрат/прямоугольник превращается в произвольный четырехугольник. Тут уже нужен метод определения принадлежности точки четырехугольнику. Можно использовать перечисленные выше методы многоугольника, приведенные в примере треугольника (они тоже подходят для любого ногоугольника). Также стоит учесть, что немного кол-во пикселей будет другим.
Для четырехугольника еще нужно высчитать площадь. Тк с помощью нее можно посчитать теоритическое количество пикселей. Таким образом зная кол-во, можно ставить ограничение.
Кроме того, я хотел добавить внешний контур квадрату/прямоугольнику. Тут уже усложняется, я не находил эффективного метода. Метод тот же, просто условием отсеиваем:
(лежит в четырехугольнике A) and (не лежит в четырехугольнике B) где B - внутренний четырехугольник, A - большой наружний четырехугольник.
Мне приходило в голову использовать метод относительности: проверяем лежат стороны справа или слева. Но пока до такого не доходят руки.
переделанный код отрисовки квадрата/прямоугольника

player = {}

for a=1, 12 do
	player[a] = {}
	player[a].shape = {}
end

function DestroyShapeImage(data,num)
   local sum = #data[num].images
   
   for a=1,sum do
        DestroyImage(data[num].images[a])
   end
    table.remove(data,num)
end

function ColorShapeImage(data,num,r,g,b,alpha)
   local sum = #data[num].images
   
   print(sum)
   
   for a=1,sum do
        SetImageColor(data[num].images[a], r, g, b, alpha)
   end
   
end

--Принадлежность точки (x;y) произвольному четырёхугольнику (x1;y1);(x2;y2);(x3;y3);(x4;y4). Облегчённая версия, см. примечание.
function IsCoordsIn4GonSimple(x,y,x1,y1,x2,y2,x3,y3,x4,y4)
    return IsCoordsInTriangle(x, y, x1, y1, x2, y2, x3, y3) or IsCoordsInTriangle(x, y, x1, y1, x4, y4, x3, y3)
end

--Принадлежность точки (x;y) произвольному четырёхугольнику (x1;y1);(x2;y2);(x3;y3);(x4;y4). Полная версия, см. примечание.
function IsCoordsIn4Gon(x,y,x1,y1,x2,y2,x3,y3,x4,y4)
    return IsCoordsInTriangle(x, y, x1, y1, x2, y2, x3, y3) or IsCoordsInTriangle(x, y, x1, y1, x4, y4, x3, y3) or IsCoordsInTriangle(x, y, x1, y1, x2, y2, x4, y4)
end


--Угол между 2 векторами, угол между направлениями этих векторов (наименьший угол).
--По определению, угол между двумя векторами находится в промежутке [0°; 180°].
--Косинус угла между ними равен
--@param real dx1,dy1,dx2,dy2
---@return real
function AngleBetweenTwoVectors(dx1,dy1,dx2,dy2) --в радианах (для превращения в градусы заверните в Acos)
    return math.acos((dx1*dx2+dy1*dy2)/((dx1^2+dy1^2)^0.5  * (dx2^2+dy2^2)^0.5))
end
--угол между двумя векторами
function AngleVectors(x1,y1,x2,y2)
    return math.acos((x1*x2+y1*y2)/((x1^2+y1^2)^0.5  * (x2^2+y2^2)^0.5))
end

--Угол между двумя отрезками
function BlzAngleBetweenSegments(Ax1,Ay1,Ax2,Ay2,Bx1,By1,Bx2,By2)
    local dx1,dx2,dy1,dy2=Ax2-Ax1,Bx2-Bx1,Ay2-Ay1,By2-By1
    return AngleBetweenTwoVectors(dx1,dy1,dx2,dy2)
end
--угол между диагоналями четырехугольника/сторонами треугольника и пр
--cx,cy - точка пересечения диагоналей A и B
--Ax,Ay,Bx,By - одна из крайних точек диагоналей
function BlzAngleBetweenDiagonals(cx,cy,Ax,Ay,Bx,By)
    local dx1,dx2,dy1,dy2=Ax-cx,Bx-cx,Ay-cy,By-cy
    return AngleBetweenTwoVectors(dx1,dy1,dx2,dy2)
end

--расстояние между 2 точками
--@param real x1,y1,x2,y2
---@return real
function VectorLength(x1,y1,x2,y2)
    return ((x2-x1)^2 + (y2-y1)^2)^0.5
end

--Площадь четырехугольника по диагоналям и углу между ними
function AreaQuadrilateral_v1(x1,y1,x2,y2,x3,y3,x4,y4)
    
    local x,y=InterLineLine(x1,y1,x3,y3,x2,y2,x4,y4)
    if x then
        local d1 = VectorLength(x1,y1,x3,y3) --длина диагоналя
        local d2 = VectorLength(x2,y2,x4,y4)
        local angle = BlzAngleBetweenDiagonals(x,y,x1,y1,x2,y2)
        return (d1*d1*math.sin(angle))/2
    end
    return 0
end
--Площадь четырехугольника через стороны и углы между этими сторонами
function AreaQuadrilateral_v2(x1,y1,x2,y2,x3,y3,x4,y4)
    
    local a = VectorLength(x1,y1,x2,y2)
    local b = VectorLength(x2,y2,x3,y3)
    local c = VectorLength(x3,y3,x4,y4)
    local d = VectorLength(x4,y4,x1,y1)
    
    local p = (a+b+c+d)/2 --полупериметр четырехугольника
    
    local alpha = BlzAngleBetweenDiagonals(x2,y2,x1,y1,x3,y3)
    local beta = BlzAngleBetweenDiagonals(x4,y4,x3,y3,x1,y1)
    
    return ((p-a)*(p-b)*(p-c)*(p-d)-a*b*c*d*math.cos((alpha+beta)/2))^0.5
end


function function CreateRectangleShapeImage(data,file,size,cx,cy,half_widht,half_height,imagetype,angle,line_width,color1,color2)
        
    param={}
    param.type_shape=1 --тип фигуры
    param.images={} --общие пиксели (удалить, сдвинуть, переместить, сдвинуть)
    param.images_color1={} --пиксели одного цвета
    param.images_color2={} --пиксели другого цвета
    
    
    local step = (size/2)
    local minx,miny,maxx,maxy = cx-half_widht,cy-half_height,cx+half_widht,cy+half_height
    local ax1,ay1,ax2,ay2,ax3,ay3,ax4,ay4
    local bx1,by1,bx2,by2,bx3,by3,bx4,by4
    local S = (half_widht*2-step)*(half_height*2-step)/step^2
    
    if line_width then
        if line_width < step then
            line_width = step
        end
    end
    
    local a1,r1,g1,b1,a2,r2,g2,b2
    if color1 then
        r1,g1,b1,a1 = hex2rgba(color1)
    end
    if color2 then
        r2,g2,b2,a2 = hex2rgba(color2)
    end
        
    if angle then
        local w,h = (half_widht),(half_height)
        local dg = CoordGride(step,(w^2+h^2)^0.5) --диагональ
        
        --векторы
        local dx1,dy1 = minx-cx,miny-cy
        local dx2,dy2 = minx-cx,maxy-cy
        local dx3,dy3 = maxx-cx,maxy-cy
        local dx4,dy4 = maxx-cx,miny-cy
        
        --нужно повернуть вершины
        local cosa = math.cos(angle)
        local sina = math.sin(angle)
        
        --повороты
        ax1,ay1 = cx+(dx1*cosa - dy1*sina),cy+(dx1*sina + dy1*cosa)
        ax2,ay2 = cx+(dx2*cosa - dy2*sina),cy+(dx2*sina + dy2*cosa)
        ax3,ay3 = cx+(dx3*cosa - dy3*sina),cy+(dx3*sina + dy3*cosa)
        ax4,ay4 = cx+(dx4*cosa - dy4*sina),cy+(dx4*sina + dy4*cosa)
        
        S = (AreaQuadrilateral_v1(ax1,ay1,ax2,ay2,ax3,ay3,ax4,ay4)-step)/step^2
        
        if line_width then
            --векторы
            dx1,dy1 = (minx+line_width)-cx,(miny+line_width)-cy
            dx2,dy2 = (minx+line_width)-cx,(maxy-line_width)-cy
            dx3,dy3 = (maxx-line_width)-cx,(maxy-line_width)-cy
            dx4,dy4 = (maxx-line_width)-cx,(miny+line_width)-cy
            
            bx1,by1 = cx+(dx1*cosa - dy1*sina),cy+(dx1*sina + dy1*cosa)
            bx2,by2 = cx+(dx2*cosa - dy2*sina),cy+(dx2*sina + dy2*cosa)
            bx3,by3 = cx+(dx3*cosa - dy3*sina),cy+(dx3*sina + dy3*cosa)
            bx4,by4 = cx+(dx4*cosa - dy4*sina),cy+(dx4*sina + dy4*cosa)
            
            if type(color2)=='nil' then
                S = S-((AreaQuadrilateral_v1(bx1,by1,bx2,by2,bx3,by3,bx4,by4)-step)/step^2)
            elseif type(color1)=='nil' and color2 then
                S = ((AreaQuadrilateral_v1(bx1,by1,bx2,by2,bx3,by3,bx4,by4)-step)/step^2)
            end
        end
        
        --print('SSSS',S)
        --AddLightningEx("HWPB", true, ax1, ay1, 0, ax2, ay2, 0)
        --AddLightningEx("CLPB", true, ax2, ay2, 0, ax3, ay3, 0)
        --AddLightningEx("CLPB", true, ax3, ay3, 0, ax4, ay4, 0)
        --AddLightningEx("CLPB", true, ax4, ay4, 0, ax1, ay1, 0)
        
        --помечаем область поиска
        minx,miny,maxx,maxy = cx-dg,cy-dg,cx+dg,cy+dg
        
        --print(minx,miny,maxx,maxy)
    
    end
    
    local x,y = minx+step,miny+step

    local function Rectangle_Outline(x,y,minx,miny,maxx,maxy,line_width)
        if line_width then
            
            local maxX = maxx-line_width
            local maxY = maxy-line_width
            local minX = minx+line_width
            local minY = miny+line_width
            
            return((x >= maxX and x<= maxx)or(x <= minX and x>= minx))or((y >= maxY and y<= maxy)or(y <= minY and y>= miny))
        end
    
    end
    --площадь контура прямоугольника
    local function AreaRectangle(widht,height,step,line_width)
         
        if line_width then
            
            local w = (widht-step)-(line_width*2)
            local h = (height-step)-(line_width*2)

            S=((widht-step)*(height-step))-(w*h)
        else
            S=(widht-step)*(height-step)
        end
        return S
    
    end

    

    local sum = 0
    
    if angle then
        --if S < 4000 then --ограничение
            if line_width then
                while x<maxx do
                    y = miny+step
                    while y<maxy do
                        if IsCoordsIn4GonSimple(x,y,ax1,ay1,ax2,ay2,ax3,ay3,ax4,ay4)then
                        
                            if (not IsCoordsIn4GonSimple(x,y,bx1,by1,bx2,by2,bx3,by3,bx4,by4)) then --IsCoordsIn4GonSimple или IsCoordsIn4Gon
                                
                                if color1 then --внешний контур
                                    param.images[sum+1]=CreateSquareImage(file,size,x,y,imagetype) 
                                    param.images_color1[sum+1]=bj_lastCreatedImage
                                    SetImageColor(bj_lastCreatedImage, r1, g1, b1, a1)
                                    sum=sum+1
                                elseif type(color1)=='nil' and type(color2)=='nil' then 
                                    param.images[sum+1]=CreateSquareImage(file,size,x,y,imagetype) 
                                    sum=sum+1
                                end
                            else
                                if color2 then --внутренний квадрат
                                    param.images[sum+1]=CreateSquareImage(file,size,x,y,imagetype) 
                                    param.images_color2[sum+1]=bj_lastCreatedImage
                                    SetImageColor(bj_lastCreatedImage, r2, g2, b2, a2)
                                    sum=sum+1
                                end
                            end
                        end
                        y=y+step
                    end
                    x=x+step
                
                end
            else
                while x<maxx do
                    y = miny+step
                    while y<maxy do
                        if IsCoordsIn4GonSimple(x,y,ax1,ay1,ax2,ay2,ax3,ay3,ax4,ay4) then --IsCoordsIn4GonSimple или IsCoordsIn4Gon
                            --AddSpecialEffect('land_1.mdx',x,y)
                      
                            param.images[sum+1]=CreateSquareImage(file,size,x,y,imagetype)
                            sum=sum+1
                        end
                        y=y+step
                    end
                    x=x+step
                
                end
            end
            
            
        --else
           -- print('ДОСТИГНУТ ЛИМИТ')
        --end
    else
        S = AreaRectangle(half_widht*2,half_height*2,step,line_width)/step^2
        --if S < 4000 then --ограничение
            while x<maxx do
                y = miny+step
                while y<maxy do
                    if line_width then
                        if Rectangle_Outline(x,y,minx,miny,maxx,maxy,line_width) then
                            --AddSpecialEffect('land_1.mdx',x,y)
                              
                            if color1 then --внешний контур
                                param.images[sum+1]=CreateSquareImage(file,size,x,y,imagetype) 
                                param.images_color1[sum+1]=bj_lastCreatedImage
                                SetImageColor(bj_lastCreatedImage, r1, g1, b1, a1)
                                sum=sum+1
                            elseif type(color1)=='nil' and type(color2)=='nil' then 
                                param.images[sum+1]=CreateSquareImage(file,size,x,y,imagetype) 
                                sum=sum+1
                            end 
                        else
                            if color2 then --внутренний квадрат
                                param.images[sum+1]=CreateSquareImage(file,size,x,y,imagetype) 
                                param.images_color2[sum+1]=bj_lastCreatedImage
                                SetImageColor(bj_lastCreatedImage, r2, g2, b2, a2)
                                sum=sum+1
                            end
                        end
                    else
                        --AddSpecialEffect('land_1.mdx',x,y)
                            
                        param.images[sum+1]=CreateSquareImage(file,size,x,y,imagetype) 
                        sum=sum+1
                    end
                    y=y+step
                end
                x=x+step
            
            end
        --else
           -- print('ДОСТИГНУТ ЛИМИТ')
        --end
    end
    
    if sum>0 then
        table.insert(data,param)
    else
        param.images=nil
        param.images_color1=nil 
        param.images_color2=nil 
        param=nil
    end
    
    print('count: '..sum..' , S: '..S)--AreaRectangle(half_widht*2,half_height*2,step,line_width)/step^2)

    return #data  --номер фигуры
end

function CreateSquareShapeImage(data,file,size,cx,cy,a,imagetype,angle,line_width,color1,color2)
    return CreateRectangleShapeImage(data,file,size,cx,cy,a,a,imagetype,angle,line_width,color1,color2)
end
CreateSquareShapeImage(data,file,size,cx,cy,a,imagetype) --создаем полный квадрат
пример, создаем квадрат с стороной, половина которой равна 512 (1024x1024). Состоит из пикселей размером 128x128. Квадрат создан в центре cx=0,cy=0. Всего получилось 225 пикселей.
CreateSquareShapeImage(player[1].shape,"war3mapImported\\block 32x32.TGA",128,0,0,512,2)
ColorShapeImage(player[1].shape,1, 0, 255, 0, 80) --перекрашиваем в зеленый цвет
CreateSquareShapeImage(data,file,size,cx,cy,a,imagetype,angle) --создаем повернутый квадрат
пример, создаем повернутый на 30 град квадрат с стороной, половина которой равна 512. Состоит из пикселей размером 128x128. Квадрат создан в центре cx=0,cy=0.
CreateSquareShapeImage(player[1].shape,"war3mapImported\\block 32x32.TGA",128,0,0,512,2,math.rad(30))
ColorShapeImage(player[1].shape,1, 0, 255, 0, 80) --перекрашиваем в зеленый цвет
CreateSquareShapeImage(data,file,size,cx,cy,a,imagetype,nil,line_width) - создаем контур квадрата с заданной толщиной line_width. Угол angle можно не указывать, зачем нам поворачивать его?
пример создаем квадрат с размером половины стороны a=512, состоящих из мелких пикселей размером 128x128. Указываем толщину контура.
CreateSquareShapeImage(player[1].shape,"war3mapImported\\block 32x32.TGA",128,0,0,512,2, nil,128)
ColorShapeImage(player[1].shape,1, 0, 255, 0, 80) --перекрашиваем в зеленый цвет
CreateSquareShapeImage(data,file,size,cx,cy,a,imagetype,angle,line_width) - делаем то же самое, но теперь нужно повернуть.
пример создаем квадрат в 30 градусов с размером половины стороны a=512, состоящих из мелких пикселей размером 128x128. Указываем толщину контура.
CreateSquareShapeImage(player[1].shape,"war3mapImported\\block 32x32.TGA",128,0,0,512,2, math.rad(30),128)
ColorShapeImage(player[1].shape,1, 0, 255, 0, 80) --перекрашиваем в зеленый цвет
Теперь осталось добавить цвета color1 и color2, которыми будут раскрашены внутренний квадрат, так и внешний контур квадрата. Что не сложно, но надо условия прочекать.
CreateSquareShapeImage(data,file,size,cx,cy,a,imagetype,angle,line_width,color1,color2) - задают повернутый квадрат со цветами color1,color2.
пример квадрат с размером половины стороны a=512, и толщиной контура в 0. По факту, если контур меньше, чем размер пикселя size, то толщина принимает этот размер=128. И задаем цвет внутреннему квадрату, так и внешнему. Всего 225 пикселей.
CreateSquareShapeImage(player[1].shape,"war3mapImported\\block 32x32.TGA",128,0,0,512,2, nil,0,'80ff0000','b100ff00')
теперь давайте-ка попробуем изменим немного код, не указывая один из цветов. Например, если цвет внутреннего квадрата color2 не указан (nil), то пиксели внутреннего квадрата не создаются. А это значит, что их не будет видно. Получается некое отверстие с внешним контуром квадрата.
CreateSquareShapeImage(player[1].shape,"war3mapImported\\block 32x32.TGA",128,0,0,512,2, nil,0,'80ff0000',nil)
Или можно отобразить только внутренний квадрат, не указывая цвет внешнего контура color1 (nil):
CreateSquareShapeImage(player[1].shape,"war3mapImported\\block 32x32.TGA",128,0,0,512,2, nil,0,nil,'b100ff00')
пример повернутый в 30 градусов квадрат с размером половины стороны a=512, и толщиной контура в 0. По факту, если контур меньше, чем размер пикселя size, то толщина принимает этот размер. И задаем цвет внутреннему квадрату, так и внешнему. Всего 257 пикселей.
CreateSquareShapeImage(player[1].shape,"war3mapImported\\block 32x32.TGA",128,0,0,512,2, math.rad(30),0,'80ff0000','b100ff00')
тот же повернутый в 30 градусов, но без задания цвета внутреннему квадрату color2. Пиксели просто не создаются.
CreateSquareShapeImage(player[1].shape,"war3mapImported\\block 32x32.TGA",128,0,0,512,2, math.rad(30),0,'80ff0000',nil)
Или не указывать цвет внешнего контура color1:
CreateSquareShapeImage(player[1].shape,"war3mapImported\\block 32x32.TGA",128,0,0,512,2, math.rad(30),0,nil,'b100ff00')
Аналогичная ситуация с прямоугольником:
CreateRectangleShapeImage(data,file,size,cx,cy,half_widht,half_height,imagetype,angle,line_width,color1,color2)
по сути это те же самые характеристики, поэтому я не думаю, что стоит объяснять. также работу прямоугольника. квадратная функция - это обертка прямоугольной.
код
function CreateSquareShapeImage(data,file,size,cx,cy,a,imagetype,angle,line_width,color1,color2)
    return CreateRectangleShapeImage(data,file,size,cx,cy,a,a,imagetype,angle,line_width,color1,color2)
end
отрисовка отрезка/линии
отрисовки прямого отрезка (алгоритм Брезенхема)
изначально код имеет вот такой вид. я его переписал на lua.
изначальный код
function plotLine(file,size,imagetype,x0,y0,x1,y1)
   local dx =  math.abs(x1-x0)
   local dy = -math.abs(y1-y0) 
   local err,sx,sy, e2 = dx+dy
   
   if x0<x1 then
    sx=1
    else
    sx=-1
    end 
    if y0<y1 then
    sy=1
    else
    sy=-1
    end

    local sum = 0
   while(true) do
        CreateSquareImage(file,size,x0,y0,imagetype) 
        SetImageColor(bj_lastCreatedImage, 0, 255, 0, 80)
        sum = sum+1
      if (x0==x1 and y0==y1) then
        break
      end
      e2 = 2*err
        if (e2 >= dy) then
            err = err+dy
            x0 = x0+sx
        end
        if (e2 <= dx) then
            err = err+dx
            y0 = y0+sy
        end
   end
   print('sum:',sum)
end
проблема изначального кода в шаге. Во всех алгоритмах Брезенхэма учет шага идет в 1 пиксель. А нам нужно не 1 пиксель, у нас целое изображение. 1 пиксель это много нужно images создать в варкрафте, и будет плохо. нужно задать размер шага. Поэтому алгоритм никуда не годится.
пример отрезок из точки (0,0) в точку (512,512). Это 513 images создал.
переделанный код (версия 1)
function putpixel(file,size,imagetype,x,y,a)
    CreateSquareImage(file,size,x,y,imagetype)
    if a then
        a = math.abs(a)//1
        if a> 255 then
            a = 255
        elseif a<0 then
            a = 0
        end
        SetImageColor(bj_lastCreatedImage, 0, 255, 0, 255-a)
    else
        SetImageColor(bj_lastCreatedImage, 0, 255, 0, 80)
    end
    return bj_lastCreatedImage
end

function plotLine(file,size,imagetype,x0,y0,x1,y1)
    local step = size/2
    x0 = CoordGride(size,x0)
    y0 = CoordGride(size,y0)
    x1 = CoordGride(size,x1)
    y1 = CoordGride(size,y1)
   
    local dx =  math.abs(x1-x0)
    local dy = -math.abs(y1-y0) 
    local err,sx,sy, e2 = dx+dy
   
    if x0<x1 then
        sx=step
    else
        sx=-step
    end 
    if y0<y1 then
        sy=step
    else
        sy=-step
    end

    local sum = 0
	while(true) do
        putpixel(file,size,imagetype,x0,y0)
        sum = sum+1
      if (x0==x1 and y0==y1) then
        break
      end
      e2 = 2*err
        if (e2 >= dy) then
            err = err+dy
            x0 = x0+sx
        end
        if (e2 <= dx) then
            err = err+dx
            y0 = y0+sy
        end
   end
   print('sum:',sum)
end
переделанный код (версия 2)
function drawLine(file,size,imagetype,x1,y1,x2,y2)
   local step = size/2
   x1 = CoordGride(size,x1)
   y1 = CoordGride(size,y1)
   x2 = CoordGride(size,x2)
   y2 = CoordGride(size,y2)
   local dx,dy = (x2 - x1)/step,(y2 - y1)/step
   
   CreateUnit(Player(0),FourCC('hfoo'),x1,y1,0)
   CreateUnit(Player(0),FourCC('hfoo'),x2,y2,0)
   
    local sign_x,sign_y = step,step
    local pdx,pdy,es,el,x,y,error, t
    if(dx > 0) then 
        sign_x = step
    elseif(dx == 0) then 
        sign_x = 0
    else 
        sign_x = -step
    end
    
    if(dy > 0) then 
        sign_y = step
    elseif(dy == 0) then 
        sign_y = 0
    else 
        sign_y = -step
    end  
        
    if dx < 0 then dx = -dx end
    if dy < 0 then dy = -dy end
        
    if dx > dy then
        pdx, pdy = sign_x, 0
        es, el = dy, dx
    else
        pdx, pdy = 0, sign_y
        es, el = dx, dy
    end
        
    x, y = x1, y1
    error, t = el/2, 0        
        
    putpixel(file,size,imagetype,x,y)
    
    
    while t < el do
        error = error - es
        if error < 0 then
            error = error + el
            x = x + sign_x
            y = y + sign_y
        else
            x = x + pdx
            y = y + pdy
        end
        t = t + 1
        putpixel(file,size,imagetype,x,y)
    end
end
plotLine и drawLine две разные функции, взятые из разных источников. Но выполняют один и тот же алгоритм.
создаем отрезок из точки (0,0) в точку (512,512). линия состоит из пикселей 128x128
drawLine("war3mapImported\\block 32x32.TGA",128,2,0,0,512,512)
выскажу предположение, что в plotLine и drawLine есть какая-то не точность. Мне кажется, что эти функции создают один лишний пиксель. должно быть 8, а тут 9. Но юниты точно создаются на концах отрезках, то мое суждение мб не верно.
отрисовка отрезка (Xiaolin Wu's line algorithm)
lua код
--Anti-aliased line
--The algorithm could be modified to draw an anti-aliased line.
function plotLineAA(file,size,imagetype,x0,y0,x1,y1)
    local step = size/2
    x0 = CoordGride(size,x0)
    y0 = CoordGride(size,y0)
    x1 = CoordGride(size,x1)
    y1 = CoordGride(size,y1)
   
    local dx,dy = math.abs(x1-x0),math.abs(y1-y0)
    local sx,sy
    if x0<x1 then
        sx = step
    else
        sx = -step
    end
    if y0<y1 then
        sy = step
    else
        sy = -step
    end 
    local err = dx-dy, e2, x2,ed; --error value e_xy
    if dx+dy == 0 then
        ed = 1
    else
        ed = (dx^2+dy^2)^0.5
    end
    
    local sum = 0
    while true do
        --pixel loop
        putpixel(file,size,imagetype,x0,y0,80*math.abs(err-dx+dy)/ed)
        sum = sum + 1
        e2 = err; x2 = x0;
        if (2*e2 > -dx) then -- x step
            if (x0 == x1) then
                break;
            end
            if (e2+dy < ed)then
                putpixel(file,size,imagetype,x0,y0+sy,80*(e2+dy)/ed)
                sum = sum + 1
            end    
            err = err-dy; x0 = x0+sx; 
        end
    
        if (2*e2 <= dy)then --y step
            if (y0 == y1)then
                break;
            end
            if (dx-e2 < ed)then 
                putpixel(file,size,imagetype,x2+sx,y0,80*(dx-e2)/ed)
                sum = sum + 1
            end
            err = err+dx; y0 = y0+sy;
        end
    end
    print('sum',sum)
end
Итог: получился немного не естественный цвет, привык к полупрозрачно-зеленому цвету. А тут она жутко ярко зеленый. Ошибка мб в вычислении. Старался немного подправить, задавая альфу-канал.
отрисовка линии круга (алгоритм Брезенхема)
взял код и переделал функцию, чтобы там был шаг не 1 пиксель.
код отрисовки линии круга
function plotCircle(file,size,imagetype,xm, ym, r)
    xm = CoordGride(size,xm)
    ym = CoordGride(size,ym)
    r = CoordGride(size,r)
   local step = size/2
   local x,y,err = xm-r, ym+0, 2-2*r
   local sum = 0
   while (x < 0) do
        --   I. Quadrant
        putpixel(file,size,imagetype,xm-x,ym+y)
        --  II. Quadrant
        putpixel(file,size,imagetype,xm-y,ym-x)
        -- III. Quadrant
        putpixel(file,size,imagetype,xm+x,ym-y)
        --  IV. Quadrant
        putpixel(file,size,imagetype,xm+y,ym+x)
		
		sum = sum + 4
      r = err
        if (r <= y) then
            y = y +step
            err = err +y*2+step 
        end       
        if (r > x or err > y) then 
            x = x +step
            err = err +x*2+step 
        end 
   end
   print('sum:',sum)
end
рисуем круговую линию с радиусом 512, состоящую из 128x128 пикселей.
plotCircle("war3mapImported\\block 32x32.TGA",128,2,0, 0, 512)
отрисовка контура эллипса
Примечание: алгоритм Брезенхэма для эллипса схож с алгоритмом круга, но в эллипсе тут две симметрии: a - большая полуось, b - маленькая полуось. Поэтому алгоритм может слегка изменен. Честно, многое не понравилось. В интернете много статей, но мало примеров. не очень понимаю данный алгоритм, иначе давно подправил под себя. Нужно сесть за парту, и внимательно посмотреть статьи и формулы. Поэтому я сразу побежал искать и воровать готовый код. Но готовых функции не много нашлось на рунете (иначе еще пришлось с недобитками разбираться), искал даже на зарубежье. И еще самому пришлось переписывать с чужого ЯП на родной lua. Но в итоге, все равно недоволен, получается эллипс не такой, функция работает только если полуоси a>b. Пробовал поменять x,y местами, в расчетах, итд.
код отрисовки эллипса (алгоритм Брезенхема)
как итог смог переписать с lua несколько функции. Расчитаны были на шаг в 1 пиксель. Поэтому немного добавил размера шага - step. Но не все с добавлением размера step, работают как надо. Только две последние работают норм. Остальные не рабочие удалены
--Алгоритм Брезенхема для рисования эллипса
--https://studassistent.ru/charp/rastrovaya-razvertka-okruzhnosti-i-ellipsa-c
function ellipse(file,size,imagetype,x,y,a,b)
    x = CoordGride(size,x)
    y = CoordGride(size,y)
    local step = size/2

    local col, i, row, bnew
    local a_square, b_square, two_a_square, two_b_square, four_a_square, four_b_square, d
         
    b_square = b * b;
    a_square = a * a;
    row = b;
    col = 0;
    two_a_square = a_square << 1;
    four_a_square = a_square << 2;
    four_b_square = b_square << 2;
    two_b_square = b_square << 1;
                    
    d = two_a_square * ((row - 1) * (row)) + a_square + two_b_square * (1 - a_square);
    while (a_square * (row) > b_square * (col))do
        putpixel(file,size,imagetype, col + x, row + y)
        putpixel(file,size,imagetype, col + x, y - row)
        putpixel(file,size,imagetype, x - col, row + y)
        putpixel(file,size,imagetype, x - col, y - row)
        if (d >= 0)then
            row=row-step
            d = d- (four_a_square * (row))
        end
        d = d+(two_b_square * (3 + (col << 1)))
        col=col+step
    end
    d = two_b_square * (col + 1) * col + two_a_square * (row * (row - 2) + 1) + (1 - two_a_square) * b_square;

    while ((row) + 1 > 0)do
        putpixel(file,size,imagetype, col + x, row + y)
        putpixel(file,size,imagetype, col + x, y - row)
        putpixel(file,size,imagetype, x - col, row + y)
        putpixel(file,size,imagetype, x - col, y - row)
        if (d <= 0)then
            col=col+step
            d = d+(four_b_square * col)
        end
        row=row-step
        d = d+(two_a_square * (3 - (row << 1)))
    end
     
end

--https://www.geeksforgeeks.org/midpoint-ellipse-drawing-algorithm/
function midptellipse(file,size,imagetype,xc,yc,rx,ry)
    xc = CoordGride(size,xc)
    yc = CoordGride(size,yc)
    rx = CoordGride(size,rx)
    ry = CoordGride(size,ry)
    local step = size/2

    local dx, dy, d1, d2, x, y
    
    
    if rx>ry then
        x,y = 0,ry
     
        -- Initial decision parameter of region 1
        d1 = (ry^2) - (rx^2 * ry) + (0.25 * rx^2)
        dx = 2 * ry^2 * x;
        dy = 2 * rx^2 * y;
     
        -- For region 1
        while (dx < dy) do
            -- Print points based on 4-way symmetry
            if x+xc == -x+xc then --чтоб дважды не выделяла точку
                putpixel(file,size,imagetype,x+xc,y+yc)
                putpixel(file,size,imagetype,-x+xc,-y+yc)
            else
                putpixel(file,size,imagetype,x+xc,y+yc)
                putpixel(file,size,imagetype,-x+xc,y+yc)
                putpixel(file,size,imagetype,x+xc,-y+yc)
                putpixel(file,size,imagetype,-x+xc,-y+yc)
            end
            
            -- Checking and updating value of
            -- decision parameter based on algorithm
            if (d1 < 0) then
                x=x+step
                dx = dx + (2*step * ry * ry);
                d1 = d1 + dx + (ry * ry);
            else
                x=x+step
                y=y-step
                dx = dx + (2*step * ry * ry);
                dy = dy - (2*step * rx * rx);
                d1 = d1 + dx - dy + (ry * ry);
            end
        end
     
        -- Decision parameter of region 2
        d2 = ((ry * ry) * ((x + 0.5) * (x + 0.5))) +((rx * rx) * ((y - 1) * (y - 1))) -(rx * rx * ry * ry);
     
        -- Plotting points of region 2
        while (y >= 0)do
     
            -- Print points based on 4-way symmetry
            if y+yc == -y+yc then --чтоб дважды не выделяла точку
                putpixel(file,size,imagetype,x+xc,y+yc)
                putpixel(file,size,imagetype,-x+xc,y+yc)
            else
                putpixel(file,size,imagetype,x+xc,y+yc)
                putpixel(file,size,imagetype,-x+xc,y+yc)
                putpixel(file,size,imagetype,x+xc,-y+yc)
                putpixel(file,size,imagetype,-x+xc,-y+yc)
            end
            -- Checking and updating parameter
            -- value based on algorithm
            if (d2 > 0)then
                y=y-step
                dy = dy - (2*step * rx * rx);
                d2 = d2 + (rx * rx) - dy;
            else
                y=y-step
                x=x+step
                dx = dx + (2*step * ry * ry);
                dy = dy - (2*step * rx * rx);
                d2 = d2 + dx - dy + (rx * rx);
            end
        end
    elseif rx<ry then
        x,y = rx,0
     
        -- Initial decision parameter of region 1
        d1 = (rx * rx) - (ry * ry * rx) + (0.25 * ry * ry)
        
        dy = 2 * rx * rx * y;
        dx = 2 * ry * ry * x;
     
        -- For region 1
        while (dy < dx) do
            -- Print points based on 4-way symmetry
            if y+yc == -y+yc then --чтоб дважды не выделяла точку
                putpixel(file,size,imagetype,x+xc,y+yc)
                putpixel(file,size,imagetype,-x+xc,-y+yc)
            else
                putpixel(file,size,imagetype,x+xc,y+yc)
                putpixel(file,size,imagetype,-x+xc,y+yc)
                putpixel(file,size,imagetype,x+xc,-y+yc)
                putpixel(file,size,imagetype,-x+xc,-y+yc)
            end
            
            -- Checking and updating value of
            -- decision parameter based on algorithm
            if (d1 < 0) then
                y=y+step
                dy = dy + (2*step * rx * rx);
                d1 = d1 + dy + (rx * rx);
            else
                y=y+step
                x=x-step
                dy = dy + (2*step * rx * rx);
                dx = dx - (2*step * ry * ry);
                d1 = d1 + dy - dx + (rx * rx);
            end
        end
     
        -- Decision parameter of region 2
        d2 = ((rx * rx) * ((y + 0.5) * (y + 0.5))) +((ry * ry) * ((x - 1) * (x - 1))) -(ry * ry * rx * rx);
     
        -- Plotting points of region 2
        while (x >= 0)do
     
            -- Print points based on 4-way symmetry
            if x+xc == -x+xc then --чтоб дважды не выделяла точку
                putpixel(file,size,imagetype,x+xc,y+yc)
                putpixel(file,size,imagetype,x+xc,-y+yc)
            else
                putpixel(file,size,imagetype,x+xc,y+yc)
                putpixel(file,size,imagetype,-x+xc,y+yc)
                putpixel(file,size,imagetype,x+xc,-y+yc)
                putpixel(file,size,imagetype,-x+xc,-y+yc)
            end
            -- Checking and updating parameter
            -- value based on algorithm
            if (d2 > 0)then
                x=x-step
                dx = dx - (2*step * ry * ry);
                d2 = d2 + (ry * ry) - dx;
            else
                x=x-step
                y=y+step
                dy = dy + (2*step * rx * rx);
                dx = dx - (2*step * ry * ry);
                d2 = d2 + dy - dx + (ry * ry);
            end
        end
    elseif rx==ry then
        plotCircle(file,size,imagetype,xc,yc,rx or ry)
    end
end
    --ellipse("war3mapImported\\block 32x32.TGA",128,2,0,0,512,256)
    midptellipse("war3mapImported\\block 32x32.TGA",128,2,0,0,512,256)
на нижнем скрине две функции отображают абсолютно разные эллипсы
Недостаток: функции недостаточно универсальны. как было сказано, отрисовывает только если a>b. То есть a - большая, b - маленькая. Иначе, рисует неправильную форму эллипса или не похожую. Можно потом только повернуть пиксели под нужный угол
Функция midptellipse стала намного универсальнее, тк можно задавать различные a и b размеры. При a<b пришлось поменять местами x и y оси при данных формулах. А при a==b - фактически круг. Единственное, что не учитывается поворот на угол.
отрисовка линии параболы
код отрисовки
--https://xgm.guru/p/wc3/parabolicmovement
--h и d - высота и длина параболы
function ParabolaZ(h,d,x)
  return (4 * h / d) * (d - x) * (x / d)
end
function ParabolaZ1(y0,y1,h,d,x)
    return (2*(y0 + y1 - 2*h)*(x/d - 1) + (y1 - y0))*(x/d) + y0
end
function ParabolaZ2(y0,y1,h,d,x)
    return ((4 * h / d) * (d - x) + y1 - y0) * (x / d) + y0
end
--рисуем с помощью параболы
function draw_parabola(file,size,imagetype, cx, cy, a, b)
    cx = CoordGride(size,cx)
    cy = CoordGride(size,cy)
    
    local step = size/2 --размер шага
    --начальные координаты, относительно которой дуга строится
    local x0,y0=cx-a,cy 
    --текущие координаты
    local x1,y1=x0,y0
    local D = a*2 --длина дуги
    local H = b -- высота дуги
    local angle = math.rad(0) --угол дуги
    local cosa = math.cos(angle)
    local sina = math.sin(angle)
    
    local d = 0 --текущее расстояние от нач точки
    local h = 0 --высота в точке d
    
    --верхняя дуга
    repeat
        --ищем h в точке d
        h =ParabolaZ(H,D,d)
        
        x1 = x0 + (d * cosa - h * sina)
        y1 = y0 + (d * sina + h * cosa)
        d=d+step
        
        x1=CoordGride(step,x1)
        y1=CoordGride(step,y1)
        putpixel(file,size,imagetype,x1,y1) --пиксель верхней дуги
        --putpixel(file,size,imagetype,x1,-y1) --пиксель нижней дуги
    until(d>D)
end
кривая Безье
квадратная кривая Безье (3 точки)
код
--строит отрезок (англ segment) кривой Безье (без изменения градиента)
function plotQuadBezierSeg(file,size,imagetype,x0,y0,x1,y1,x2,y2)
    x0=x0//1; y0=y0//1; x1=x1//1; y1=y1//1; x2=x2//1; y2=y2//1
    print('start')
    --plot a limited quadratic Bezier segment
    local sx,sy = x2-x1, y2-y1;
    local xx,yy,xy = x0-x1, y0-y1; --relative values for checks
    local cur,dx,dy,err = xx*sy-yy*sx; --curvature
    if (xx*sx > 0 and yy*sy > 0) then --sign of gradient must not change
        error("Assert that math doesn't work")
    end
    if (sx*sx+sy*sy > xx*xx+yy*yy)then -- begin with longer part
        x2 = x0; x0 = sx+x1; y2 = y0; y0 = sy+y1; cur = -cur; -- swap P0 P2
    end
    print('check work')
    if (cur ~= 0)then --no straight line
        if x0 < x2 then
            xx =xx+sx
            sx = 1 -- x step direction
        else
            xx =xx+sx*(-1)
            sx = -1 -- x step direction
        end
        if y0 < y2 then
            yy =yy+sy
            sy = 1 -- y step direction
        else
            yy =yy+sy*(-1)
            sy = -1 -- y step direction
        end
        
        xy = 2*xx*yy; xx = xx^2; yy = yy^2; --differences 2nd degree
        if (cur*sx*sy < 0) then -- negated curvature
            xx = -xx; yy = -yy; xy = -xy; cur = -cur;
        end
        dx = 4.0*sy*cur*(x1-x0)+xx-xy; --differences 1st degree
        dy = 4.0*sx*cur*(y0-y1)+yy-xy;
        xx = xx+xx; yy = yy+yy; err = dx+dy+xy; --error 1st step    
        
        local sum = 0
        while true do                             
            putpixel(file,size,imagetype,x0,y0); --plot curve
            if (x0 == x2 and y0 == y2)then -- last pixel -> curve finished 
                print('exit check:',sum)
                return 
            end 
            
            y1 = 2*err < dx; --save value for test of y step
            if (2*err > dy)then --x step
                x0 =x0+sx; dx =dx-xy; dy =dy+yy; err =err+dy 
            end 
            
            if (y1)then --y step
                y0 =y0+sy; dy =dy-xy; dx =dx+xx; err =err+dx 
                print('y step: ',y0)
            end 
            --ранее было другое условие dy < dx
            if (dy < 0 and dx > 0 )then --gradient negates -> algorithm fails
                break
            end
            sum=sum+1
        end
        print('check:',sum)
    end
    plotLine(file,size,imagetype,x0,y0, x2,y2); --plot remaining part to end */
    print('end')
end

--для построения любой квадратичной кривой Безье. 
--Первая процедура разделяет кривую при изменении горизонтального и вертикального градиента
function plotQuadBezier(file,size,imagetype,x0,y0,x1,y1,x2,y2)
    x0=x0//1
    y0=y0//1
    x1=x1//1
    y1=y1//1
    x2=x2//1
    y2=y2//1
    
    --plot any quadratic Bezier curve
    local x,y = x0-x1, y0-y1
    local t,r = x0-2*x1+x2
    if (x*(x2-x1) > 0) then -- horizontal cut at P4? 
        if (y*(y2-y1) > 0)then -- vertical cut at P6 too?
            if (math.abs((y0-2*y1+y2)/t*x) > math.abs(y)) then -- which first?
                x0 = x2; x2 = x+x1; y0 = y2; y2 = y+y1; -- swap points
            end -- now horizontal cut at P4 comes first
            t = (x0-x1)/t;
            r = (1-t)*((1-t)*y0+2.0*t*y1)+t*t*y2; -- By(t=P4)
            t = (x0*x2-x1*x1)*t/(x0-x1); -- gradient dP4/dx=0
            x = math.floor(t+0.5); y = math.floor(r+0.5);
            r = (y1-y0)*(t-x0)/(x1-x0)+y0; -- intersect P3 | P0 P1
            plotQuadBezierSeg(file,size,imagetype,x0,y0, x,math.floor(r+0.5), x,y);
            r = (y1-y2)*(t-x2)/(x1-x2)+y2; -- intersect P4 | P1 P2
            x1 = x; x0 = x1; y0 = y; y1 = math.floor(r+0.5); -- P0 = P4, P1 = P8
        end
    end
    if ((y0-y1)*(y2-y1) > 0)then -- vertical cut at P6?
        t = y0-2*y1+y2; t = (y0-y1)/t;
        r = (1-t)*((1-t)*x0+2.0*t*x1)+t*t*x2; -- Bx(t=P6)
        t = (y0*y2-y1*y1)*t/(y0-y1); -- gradient dP6/dy=0
        x = math.floor(r+0.5); y = math.floor(t+0.5);
        r = (x1-x0)*(t-y0)/(y1-y0)+x0; -- intersect P6 | P0 P1
        plotQuadBezierSeg(file,size,imagetype,x0,y0, math.floor(r+0.5),y, x,y);
        r = (x1-x2)*(t-y2)/(y1-y2)+x2; -- intersect P7 | P1 P2
        x0 = x; x1 = math.floor(r+0.5); y1 = y; y0 = y1; -- P0 = P6, P1 = P7
  end
  plotQuadBezierSeg(file,size,imagetype,x0,y0, x1,y1, x2,y2); -- remaining part
end
создаю кривую Безье по 3 точкам p1(-512,0), p2(0,512), p3(512,0)
plotQuadBezier("war3mapImported\\block 32x32.TGA",128,2,-512,0,0,512,512,0)
код для plotQuadBezierAA
function plotQuadBezierSegAA(file,size,imagetype,x0,y0,x1,y1,x2,y2)

    local sx,sy = x2-x1, y2-y1;
    local xx,yy,xy = x0-x1, y0-y1, xy; --relative values for checks
    local cur, dx, dy, err, ed = xx*sy-yy*sx; --curvature

    if (xx*sx < 0 and yy*sy < 0) then --sign of gradient must not change
        error("Assert that math doesn't work")
    end

    if (sx*sx+sy*sy > xx*xx+yy*yy)then -- begin with longer part
        x2 = x0; x0 = sx+x1; y2 = y0; y0 = sy+y1; cur = -cur;  --swap P0 P2
    end
    if (cur ~= 0)then
        --no straight line
        
        if x0 < x2 then -- x step direction
            xx = xx+sx; 
            sx = 1
        else
            xx = xx+sx*(-1); 
            sx = -1
        end
        if y0 < y2 then -- y step direction
            yy = yy+sy; 
            sy = 1
        else
            yy = yy+sy*(-1); 
            sy = -1
        end
        xy = 2*xx*yy; xx = xx^2; yy = yy^2; --differences 2nd degree
        if (cur*sx*sy < 0) then --negated curvature?
            xx = -xx; yy = -yy; xy = -xy; cur = -cur;
        end
        dx = 4.0*sy*(x1-x0)*cur+xx-xy; --differences 1st degree
        dy = 4.0*sx*(y0-y1)*cur+yy-xy;
        xx = xx+xx; yy = yy+yy; err = dx+dy+xy; --error 1st step
        while true do                            
            cur = math.min(dx+xy,-xy-dy);
            ed = math.max(dx+xy,-xy-dy); --approximate error distance
            ed = 255/(ed+2*ed*cur*cur/(4.*ed*ed+cur*cur)); 
            putpixel(file,size,imagetype,x0,y0,ed*math.abs(err-dx-dy-xy)) -- plot curve
            if (x0 == x2 and y0 == y2) then -- last pixel -> curve finished 
                return
            end
            x1 = x0; cur = dx-err; y1 = 2*err+dy < 0;
            if (2*err+dx > 0)then  -- x step
                if (err-dy < ed) then
                    putpixel(file,size,imagetype,x0,y0+sy,ed*math.abs(err-dy))
                end
                x0 = x0+sx; dx = dx-xy; dy=dy+yy; err = err+dy;
            end
            if (y1)then -- y step
                if (cur < ed)then
                    putpixel(file,size,imagetype,x1+sx,y0,ed*math.abs(cur))
                end
                y0 = y0+sy; dy = dy-xy; dx = dx+xx; err = err+dx; 
            end
            if (dy < dx)then --gradient negates -> close curves
                break
            end 
        end
    end
    plotLineAA(file,size,imagetype,x0,y0,x2,y2) --plot remaining needle to end
end

function plotQuadBezierAA(file,size,imagetype,x0,y0,x1,y1,x2,y2)
    x0=x0//1
    y0=y0//1
    x1=x1//1
    y1=y1//1
    x2=x2//1
    y2=y2//1
    
    --plot any quadratic Bezier curve
    local x,y = x0-x1, y0-y1
    local t,r = x0-2*x1+x2
    if (x*(x2-x1) > 0) then -- horizontal cut at P4? 
        if (y*(y2-y1) > 0)then -- vertical cut at P6 too?
            if (math.abs((y0-2*y1+y2)/t*x) > math.abs(y)) then -- which first?
                x0 = x2; x2 = x+x1; y0 = y2; y2 = y+y1; -- swap points
            end -- now horizontal cut at P4 comes first
            t = (x0-x1)/t;
            r = (1-t)*((1-t)*y0+2.0*t*y1)+t*t*y2; -- By(t=P4)
            t = (x0*x2-x1*x1)*t/(x0-x1); -- gradient dP4/dx=0
            x = math.floor(t+0.5); y = math.floor(r+0.5);
            r = (y1-y0)*(t-x0)/(x1-x0)+y0; -- intersect P3 | P0 P1
            plotQuadBezierSegAA(file,size,imagetype,x0,y0, x,math.floor(r+0.5), x,y);
            r = (y1-y2)*(t-x2)/(x1-x2)+y2; -- intersect P4 | P1 P2
            x1 = x; x0 = x1; y0 = y; y1 = math.floor(r+0.5); -- P0 = P4, P1 = P8
        end
    end
    if ((y0-y1)*(y2-y1) > 0)then -- vertical cut at P6?
        t = y0-2*y1+y2; t = (y0-y1)/t;
        r = (1-t)*((1-t)*x0+2.0*t*x1)+t*t*x2; -- Bx(t=P6)
        t = (y0*y2-y1*y1)*t/(y0-y1); -- gradient dP6/dy=0
        x = math.floor(r+0.5); y = math.floor(t+0.5);
        r = (x1-x0)*(t-y0)/(y1-y0)+x0; -- intersect P6 | P0 P1
        plotQuadBezierSegAA(file,size,imagetype,x0,y0, math.floor(r+0.5),y, x,y);
        r = (x1-x2)*(t-y2)/(y1-y2)+x2; -- intersect P7 | P1 P2
        x0 = x; x1 = math.floor(r+0.5); y1 = y; y0 = y1; -- P0 = P6, P1 = P7
  end
  plotQuadBezierSegAA(file,size,imagetype,x0,y0, x1,y1, x2,y2); -- remaining part
end
plotQuadBezierAA("war3mapImported\\block 32x32.TGA",128,2,-512,0,0,512,512,0)
Построили мы кривые. Однако, лишние пиксели добавлены. Портят подсветку.
Есть специальное уравнение кривой Безье:
уравнения
линейная интерполяция = 1+1 - прямой отрезок
x = (1-t)*P0x + t*P1x
y = (1-t)*P0y + t*P1y
0 << t << 1
квадратичная интерполяция = 1+2+1 - кривая Безье по 3 точкам
x=(1-t)^2*P0x + 2*t*(1-t)*P1x + t^2*P2x
y=(1-t)^2*P0y + 2*t*(1-t)*P1y + t^2*P2y
0 << t << 1
кубическая интерполяция = 1+3+3+1 - кривая Безье по 4 точкам
x=(1-t)^3*P0x + 3*t*(1-t)^2*P1x + 3*t^2*(1-t)*P2x + t^3*P3x
y=(1-t)^3*P0y + 3*t*(1-t)^2*P1y + 3*t^2*(1-t)*P2y + t^3*P3y
0 << t << 1
Зная, уравнения, можно найти координаты начала, середины, конца кривой. Именно, эти точки дважды, а то и трижды пикселит алгоритм.
исправленный код
--для построения любой квадратичной кривой Безье. 
--Первая процедура разделяет кривую при изменении горизонтального и вертикального градиента
--https://svg-art.ru/?tag=%D0%BE%D0%BD%D0%BB%D0%B0%D0%B9%D0%BD-%D0%B8%D0%BD%D1%81%D1%82%D1%80%D1%83%D0%BC%D0%B5%D0%BD%D1%82
function plotQuadBezier(file,size,imagetype,x0,y0,x1,y1,x2,y2)
    x0=x0//1
    y0=y0//1
    x1=x1//1
    y1=y1//1
    x2=x2//1
    y2=y2//1
    
    local t = 0.5
    P1x=((1-t)^2*x0 + 2*t*(1-t)*x1 + t^2*x2) //1
    P1y=((1-t)^2*y0 + 2*t*(1-t)*y1 + t^2*y2) //1
    P0x,P0y = x0,y0
    P2x,P2y = x2,y2
    c1=0
    print('a1',x0,y0,x1,y1,x2,y2)
    print('a2',P0x,P0y,P1x,P1y,P2x,P2y)
    
        function plotLine7(file,size,imagetype,x0,y0,x1,y1)
            print('izi',P1x,P1y)
            local step = size/2
            x0 = CoordGride(size,x0)
            y0 = CoordGride(size,y0)
            x1 = CoordGride(size,x1)
            y1 = CoordGride(size,y1)
            
           
            local dx =  math.abs(x1-x0)
            local dy = -math.abs(y1-y0) 
            local err,sx,sy, e2 = dx+dy
           
            if x0<x1 then
                sx=step
            else
                sx=-step
            end 
            if y0<y1 then
                sy=step
            else
                sy=-step
            end

            local sum = 0
           while(true) do
                x0 = CoordGride(step,x0)
                y0 = CoordGride(step,y0)
                if P1x==x0 and P1y==y0 and c1>0 then
                    print('рисуем середину не рисуем')
                elseif P1x==x0 and P1y==y0 and c1==0 then
                    putpixel(file,size,imagetype,x0,y0)
                    c1=c1+1
                    print('середина нарисована:',c1)
                    sum=sum+1
                else
                    print('pixel',x0,y0)
                    putpixel(file,size,imagetype,x0,y0)
                    sum = sum+1
                end
                
              if (x0==x1 and y0==y1) then
                break
              end
              e2 = 2*err
                if (e2 >= dy) then
                    err = err+dy
                    x0 = x0+sx
                end
                if (e2 <= dx) then
                    err = err+dx
                    y0 = y0+sy
                end
           end
           print('sum:',sum)
        end

    --https://question-it.com/questions/2323696/popikselno-krivaja-beze
    --строит отрезок (англ segment) кривой Безье (без изменения градиента)
    function plotQuadBezierSeg(file,size,imagetype,x0,y0,x1,y1,x2,y2)
        x0=x0//1
        y0=y0//1
        x1=x1//1
        y1=y1//1
        x2=x2//1
        y2=y2//1

        print('start')
        --plot a limited quadratic Bezier segment
        local sx,sy = x2-x1, y2-y1;
        local xx,yy,xy = x0-x1, y0-y1; --relative values for checks
        local cur,dx,dy,err = xx*sy-yy*sx; --curvature
        if (xx*sx > 0 and yy*sy > 0) then --sign of gradient must not change
            error("Assert that math doesn't work")
        end
        if (sx*sx+sy*sy > xx*xx+yy*yy)then -- begin with longer part
            x2 = x0; x0 = sx+x1; y2 = y0; y0 = sy+y1; cur = -cur; -- swap P0 P2
        end
        print('check work')
        if (cur ~= 0)then --no straight line
            if x0 < x2 then
                xx =xx+sx
                sx = 1 -- x step direction
            else
                xx =xx+sx*(-1)
                sx = -1 -- x step direction
            end
            if y0 < y2 then
                yy =yy+sy
                sy = 1 -- y step direction
            else
                yy =yy+sy*(-1)
                sy = -1 -- y step direction
            end
            
            xy = 2*xx*yy; xx = xx^2; yy = yy^2; --differences 2nd degree
            if (cur*sx*sy < 0) then -- negated curvature
                xx = -xx; yy = -yy; xy = -xy; cur = -cur;
            end
            dx = 4.0*sy*cur*(x1-x0)+xx-xy; --differences 1st degree
            dy = 4.0*sx*cur*(y0-y1)+yy-xy;
            xx = xx+xx; yy = yy+yy; err = dx+dy+xy; --error 1st step    
            
            local sum = 0
            while true do
                x0 = CoordGride(size/2,x0)
                y0 = CoordGride(size/2,y0)
                x0 = x0//1
                y0 = y0//1
                
                --это чтоб дважды не помечал пиксель
                if (x0==P0x)and(y0==P0y)then
                    --print('начальный пиксель не рисуем')
                elseif (x0==P2x)and(y0==P2y) then
                    --print('конечный пиксель не рисуем')
                else
                    putpixel(file,size,imagetype,x0,y0); --plot curve
                    sum=sum+1
                end
                
                
                if (x0 == x2 and y0 == y2)then -- last pixel -> curve finished 
                    return 
                end 
                
                y1 = 2*err < dx; --save value for test of y step
                if (2*err > dy)then --x step
                    x0 =x0+sx; dx =dx-xy; dy =dy+yy; err =err+dy 
                end 
                
                if (y1)then --y step
                    y0 =y0+sy; dy =dy-xy; dx =dx+xx; err =err+dx 
                end 
                
                if (dy < 0 and dx > 0) then --gradient negates -> algorithm fails
                    break
                end
                sum=sum+1
            end

            print('check:',sum)
        end
        if not(x0==x2 and y0==y2) then
            plotLine7(file,size,imagetype,x0,y0, x2,y2); --plot remaining part to end */
        end
        print('end')
    end
    
    
    
    --plot any quadratic Bezier curve
    local x,y = x0-x1, y0-y1
    local t,r = x0-2*x1+x2
    if (x*(x2-x1) > 0) then -- horizontal cut at P4? 
        if (y*(y2-y1) > 0)then -- vertical cut at P6 too?
            if (math.abs((y0-2*y1+y2)/t*x) > math.abs(y)) then -- which first?
                x0 = x2; x2 = x+x1; y0 = y2; y2 = y+y1; -- swap points
            end -- now horizontal cut at P4 comes first
            t = (x0-x1)/t;
            r = (1-t)*((1-t)*y0+2.0*t*y1)+t*t*y2; -- By(t=P4)
            t = (x0*x2-x1*x1)*t/(x0-x1); -- gradient dP4/dx=0
            x = math.floor(t+0.5); y = math.floor(r+0.5);
            r = (y1-y0)*(t-x0)/(x1-x0)+y0; -- intersect P3 | P0 P1
       
            plotQuadBezierSeg(file,size,imagetype,x0,y0, x,math.floor(r+0.5), x,y);
            r = (y1-y2)*(t-x2)/(x1-x2)+y2; -- intersect P4 | P1 P2
            x1 = x; x0 = x1; y0 = y; y1 = math.floor(r+0.5); -- P0 = P4, P1 = P8
        end
    end
    if ((y0-y1)*(y2-y1) > 0)then -- vertical cut at P6?
        t = y0-2*y1+y2; t = (y0-y1)/t;
        r = (1-t)*((1-t)*x0+2.0*t*x1)+t*t*x2; -- Bx(t=P6)
        t = (y0*y2-y1*y1)*t/(y0-y1); -- gradient dP6/dy=0
        x = math.floor(r+0.5); y = math.floor(t+0.5);
        r = (x1-x0)*(t-y0)/(y1-y0)+x0; -- intersect P6 | P0 P1
       
        plotQuadBezierSeg(file,size,imagetype,x0,y0, math.floor(r+0.5),y, x,y);
        r = (x1-x2)*(t-y2)/(y1-y2)+x2; -- intersect P7 | P1 P2
        x0 = x; x1 = math.floor(r+0.5); y1 = y; y0 = y1; -- P0 = P6, P1 = P7
  end
  plotQuadBezierSeg(file,size,imagetype,x0,y0, x1,y1, x2,y2); -- remaining part
end
Исправил только первую часть кода.
А с примером линией Ву я не стал заморачиваться, много проверок нужно.

Прикрепление изображения к объектам

Не секрет, что все изображения images являются индикаторами, кругами выбора под ногами, уберсплатами и пр. Обычно можно прикрепить какую нибудь картинку, и заставить двигать ее таймером. Однако, некоторые умельцы нашли баг прикреплять изображение без таймера. Можно заменить круг выбора пример

Как победить воду

Image имеет свойство проваливаться под воду. На самом деле, если задать image белый цвет, то можно увидеть, что под водой image находится на самом дне.

1. Можно поднять высоту

код
тут проверка такова: мы проверяем, опущена ли дно на глубину моря. можно проверить по типу терраина.
выделю два типа терраина:
  • PATHING_TYPE_FLOATABILITY - это море, мелководье (там где можно плавать)
  • PATHING_TYPE_WALKABILITY - это земля, мелководье (там где можно ходить)
второй тип введен для того, чтобы исключить мелководье из первого типа. опускается только вот в море
local zTesterLocation = Location(0,0)
local getTerrainZ = function(x,y)
MoveLocation(zTesterLocation, x, y)
return GetLocationZ(zTesterLocation)
end

local x,y,z=0,0,0
z = getTerrainZ(x,y)
if (not IsTerrainPathable(x,y,PATHING_TYPE_FLOATABILITY)) and IsTerrainPathable(x,y,PATHING_TYPE_WALKABILITY) then
	SetImageConstantHeight(image,true,z+128.)
else
	SetImageConstantHeight(image,true,z)
end
Вывод:
image видно, что поднимается выше, и меняет положение от высоты, но изображение все равно не видно. Как будто вода выше находится, чем image. И плюс высота предопределена неравномерно. И еще в мелководье image будет ниже, чем вода. это вариант не помогает, тк какую бы высоту не задавай, изображение все равно находится под водой.

2. Подобрать тип image

код
Shadow хорошо лежит над водой.
if (not IsTerrainPathable(x,y,PATHING_TYPE_FLOATABILITY)) then
	SetImageType(build_menu[n].image[num],1) --задаем тип Shadow=1
else
	SetImageType(build_menu[n].image[num],2) --возвращаем тип Indicator=2
end
Вывод:
Подбирал разные типы image 5,4,3,2,1. Удивительно, что только Shadow=1 как тип сработал, остальные не помогают победить воду. Эта тень Shadow лежит над водой: над морем и над мелководьем. Высоту задавать не нужно, иначе поднимает выше изображение, чем нужно. заметка: тень не лежит на поверхности ровно, будет изображение опущено, либо наклонено. это говорит о том, что она охватывает дно, т.е. рельеф.

3. есть нативка ​Set​Image​Above​Water

Мне эта нативка показалась подозрительной, ведь в ней указано слово water (вода). в гугл вбил, и переводится как установить изображение выше воды
native SetImageAboveWater takes image whichImage, boolean flag, boolean useWaterAlpha returns nothing
comment
Draws the specified image above the water if the flag is true. The second boolean (useWaterAlpha) doesnt seem to do much. Every imagetype other than 1 doesnt seem to appear above water.
Перевод:
Рисует указанное изображение над водой, если флаг установлен. Второе логическое значение (useWaterAlpha), похоже, мало что дает. Кажется, что любой тип изображения, кроме 1, не появляется над водой.
Вывод: я в рефе сначала потестил, и показалось, что ничего эта нативка особо не делает. Я даже тип менял. А раньше она работала? Пробовал и типы менять итд. Потом выяснил, что эта нативка работает только у типа shadow=1. Изначально, особо не заметишь разницу, до и после.
Тут немного в разных местах, но разница видна. Тень опускается над дно, охватывая рельеф. А теперь, лежит над поверхностью воды ровно

Итог

Итог: из трех предоставленных вариантов помогает только 2,3. Если их объедить, тк нативка работает только у типа shadow=1. А первый вариант особо и не пригодился, высоту не зачем менять. Но стоит учесть, что типы image определяют уровень наслаивания, и чем выше индекс типа, тем выше тип иконки будет над другим (но это не точно, тк не тестил. хотя это не так ваэно, кто будет испольховать image в большом кол-ве?)

Save / Load

Image после сохранения/загрузки игры не сохраняется. Обычно, если сохранить в глобальную переменную, она работает. Но если пытаться сохранять в массив переменной или таблицы lua, то в переменных ничего не сохраняется по нормальному. GetHandleId возвращает псевдо-хэндлы. При след игре их уже не будет существовать. Unryze нашел способ решения хранить в хэш-таблице. пример карты Unryze
Или вам нужно все пересоздать. Это уже было рассказано здесь что-то похожее было с фреймами, только там еще приводило к фаталам. К счастью, image не вызывает фатал. С image можно сделать нечто похожее как и с фреймами, просто пересоздайте заново. Однако, есть неприятные баги. Почему то если в ячейке [1] массива сохранен был имейдж, и потом записать новый
set im[1]=CreateImage(..)
то новый имедж не отображается в игре. Несмотря на все способы отобразить, отрисовать.😨 Уже не помню как решал..🤔но решение есть, только не помню

Содержание
`
ОЖИДАНИЕ РЕКЛАМЫ...
Чтобы оставить комментарий, пожалуйста, войдите на сайт.