Draw an ellipse sqrt-based function

…衆ロ難τιáo~ 提交于 2020-01-17 12:24:09

问题


I'm trying to make a function in Lua or VB based code to draw / plot an ellipse and also a filled ellipse.

I don't have much knowledge about this math and I can use some help.

I googled everything there is to google about drawing ellipses with code but I can't find a good simple working example that i can code into my Lua / VB code.

here are a few websites i visited but couldn't make the code work or couldn't convert the code to Lua or VB properly...

  • https://sites.google.com/site/ruslancray/lab/projects/bresenhamscircleellipsedrawingalgorithm/bresenham-s-circle-ellipse-drawing-algorithm

  • http://groups.csail.mit.edu/graphics/classes/6.837/F98/Lecture6/circle.html

  • http://www.blitzbasic.com/codearcs/codearcs.php?code=2817

  • http://hackipedia.org/Algorithms/Graphics/pdf/A%20Fast%20Bresenham%20Type%20Algorithm%20For%20Drawing%20Ellipses%20by%20John%20Kennedy.pdf

  • https://scratch.mit.edu/projects/49873666/

  • http://www.sourcecodesworld.com/source/show.asp?ScriptID=112

  • How do I draw an ellipse with arbitrary orientation pixel by pixel?

Can anyone help me make code that can draw an ellipse and a filled ellipse?

here is some code I tried to convert to Lua from here:

https://gist.github.com/Wollw/3291916

this code has some problems (missing pixels) and I think it's not converted properly but I don't know how to do it otherwise.

function plotEllipseRect(x0, y0, x1, y1)
   -- values of diameter
   a = math.abs(x1-x0)
   b = math.abs(y1-y0)
   b1 = 2.5
   -- error increment
   dx = 4*(1-a)*b*b
   dy = 4*(b1+1)*a*a
   -- error of 1.step
   err = dx+dy+b1*a*a
   -- e2 = 0

    if (x0 > x1) then -- if called with swapped points
        x0 = x1
        x1 = x1 + a
    end

    if (y0 > y1) then -- .. exchange them 
        y0 = y1
    end

    -- starting pixel 
    y0 = y0 + (b+1)/2
    y1 = y0-b1
    a = a * 8*a
    b1 = 8*b*b

   repeat
        dot(x1, y0)  -- I. Quadrant 
        dot(x0, y0)  -- II. Quadrant 
        dot(x0, y1)  -- III. Quadrant 
        dot(x1, y1)  -- IV. Quadrant

        e2 = 2*err

        if (e2 <= dy) then  -- y step  
            y0 = y0 + 1
            y1 = y1 - 1

            dy = dy + a
            err = err + dy
        end

        if (e2 >= dx or 2*err > dy) then  --  x step 
            x0 = x0 + 1
            x1 = x1 - 1

            dx = dx + b1
            err = err + dx
        end
   until (x0 >= x1)

   while (y0-y1 < b) do   -- too early stop of flat ellipses a=1 
       dot(x0-1, y0)  -- -> finish tip of ellipse 
       y0 = y0 + 1
       dot(x1+1, y0)
       dot(x0-1, y1)
       y1 = y1 - 1
       dot(x1+1, y1)
   end
end

[EDIT:]

I almost got it for the filled one! see the comments in this code below to know what the problem is...

I use EGSL to test this Lua code: http://www.egsl.retrogamecoding.org//pages/downloads.php

function DrawEllipse(xc,yc,w,h)
    local w2  = w * w
    local h2  = h * h
    local fw2 = 4 * w2
    local fh2 = 4 * h2

    xc = xc + w
    yc = yc + h

    local x  = 0
    local y  = h
    local s  = 2 * h2 + w2 * (1 - h)

    while h2 * x <= w2 * y do
        dot(xc + x, yc + y)
        dot(xc - x, yc + y)
        dot(xc + x, yc - y)
        dot(xc - x, yc - y)

        redraw()
        inkey()
        color(int(rnd()*255),int(rnd()*255),int(rnd()*255)) --random color to see changes

        if s >= 0 then
            s = s + fw2 * (1 - y)
            y = y - 1

            color(255,0,255)
            line(xc + x, yc + y, xc - x, yc + y)
            line(xc + x, yc - y, xc - x, yc - y)

        end
        s = s + h2 * ((4 * x) + 6)
        x = x + 1

    end

    x = w
    y = 0
    s = 2 * w2 + h2 * (1 - w)

    line(xc + x, yc + y, xc - x, yc + y) --to prevent the first line to be drawn twice
    redraw()
    inkey()

    s = s + w2 * ((4 * y) + 6)
    y = y + 1

    while w2 * y < h2 * (x-2) do
        line(xc + x, yc + y, xc - x, yc + y)

        redraw()
        inkey()
        color(int(rnd()*255),int(rnd()*255),int(rnd()*255))

        line(xc + x, yc - y, xc - x, yc - y)

        redraw()
        inkey()
        color(int(rnd()*255),int(rnd()*255),int(rnd()*255))

        if s >= 0 then
            s = s + fh2 * (1 - x)
            x = x - 1
        end
        s = s + w2 * ((4 * y) + 6)
        y = y + 1
    end

    dot(xc + x, yc + y)
    dot(xc - x, yc + y)

    redraw()
    inkey()
    color(int(rnd()*255),int(rnd()*255),int(rnd()*255))

    dot(xc + x, yc - y)
    dot(xc - x, yc - y)

    redraw()
    inkey()

end


openwindow (70,70,32,"Resize Window")
color(255,255,0)

    DrawEllipse(10,10,20,20) --works perfect!
    inkey()
    cls()
    DrawEllipse(10,10,10,20) --problems with last 2 horizontal lines between the pixels!
    inkey()
    cls()
    DrawEllipse(10,10,20,10) --works perfect to!

closewindow()

回答1:


The following VB works for me, based on the first link provided; the only difference between mine here and the code at your link is I move xc and yc over, since you cannot can't have negative x or y values for the pixels in a bitmap.

Public Shared Function DrawEllipse(ByVal xc As Integer, ByVal yc As Integer, ByVal w As Integer, ByVal h As Integer, ByVal doFill As Boolean) As Drawing.Bitmap

    Dim w2 As Integer = w * w
    Dim h2 As Integer = h * h
    Dim fw2 As Integer = 4 * w2
    Dim fh2 As Integer = 4 * h2

    // cheat by moving xc and yc so that we can handle quadrants
    xc = w
    yc = h

    Dim bm As New Drawing.Bitmap(w2, h2)

    // first half
    Dim x As Integer = 0
    Dim y As Integer = h
    Dim s As Integer = 2 * h2 + w2 * (1 - h)
    While h2 * x <= w2 * y
        If doFill Then
            For i As Integer = -y To y
                bm.SetPixel(xc + x, yc + i, Drawing.Color.Red)
                bm.SetPixel(xc - x, yc + i, Drawing.Color.Red)
            Next
        Else
            bm.SetPixel(xc + x, yc + y, Drawing.Color.Red)
            bm.SetPixel(xc - x, yc + y, Drawing.Color.Red)
            bm.SetPixel(xc + x, yc - y, Drawing.Color.Red)
            bm.SetPixel(xc - x, yc - y, Drawing.Color.Red)
        End If
        If s >= 0 Then
            s += fw2 * (1 - y)
            y -= 1
        End If
        s += h2 * ((4 * x) + 6)
        x += 1
    End While

    // second half
    x = w
    y = 0
    s = 2 * w2 + h2 * (1 - w)
    While w2 * y <= h2 * x
        If doFill Then
            For i As Integer = -x To x
                bm.SetPixel(xc + i, yc + y, Drawing.Color.Red)
                bm.SetPixel(xc + i, yc - y, Drawing.Color.Red)
            Next
        Else
            bm.SetPixel(xc + x, yc + y, Drawing.Color.Red)
            bm.SetPixel(xc - x, yc + y, Drawing.Color.Red)
            bm.SetPixel(xc + x, yc - y, Drawing.Color.Red)
            bm.SetPixel(xc - x, yc - y, Drawing.Color.Red)
        End If
        If s >= 0 Then
            s += fh2 * (1 - x)
            x -= 1
        End If
        s += w2 * ((4 * y) + 6)
        y += 1
    End While

    Return bm

End Function

(Aside: I used // instead of ' for the comments... just for readability here. If you copy to Visual Studio you'll have to fix that)




回答2:


Ok, I managed to find a solution for the filled ellipse by checking if the pixel from the second half is gonna be drawn in the x-range of the first half of the ellipse.

function drawellipse(xc, yc, w, h, dofill)

    --trouble with the size, 1 pixel to large on x and y to...
    w=w/2 --good solution for making it the right size?
    h=h/2 --good solution for making it the right size?

    local w2 = w * w
    local h2 = h * h
    local fw2 = 4 * w2
    local fh2 = 4 * h2

    -- cheat by moving xc and yc so that we can handle quadrants
    xc = xc + w
    yc = yc + h

    -- first half
    local x = 0
    local y = h
    local s = 2 * h2 + w2 * (1 - h)

    while h2 * x <= w2 * y do
        if dofill then
            for i = -y , y do
                color(0,255,0)
                dot(xc + x, yc + i)
                dot(xc - x, yc + i)
                --redraw()inkey()
            end
        else
            color(255,0,255)
            dot(xc + x, yc + y)
            dot(xc - x, yc + y)
            dot(xc + x, yc - y)
            dot(xc - x, yc - y)
            --redraw()inkey()
        end
        if s >= 0 then
            s =s+ fw2 * (1 - y)
            y =y- 1
        end
        s =s+ h2 * ((4 * x) + 6)
        x =x+ 1
    end

    color(255,0,255)
    line(xc + x,0,xc - x,0)
    test1 = xc + x
    test2 = xc - x
    print(test1 .. '/' .. test2)
    redraw()inkey()

    -- second half
    x = w
    y = 0
    s = 2 * w2 + h2 * (1 - w)
    while w2 * y <= h2 * x do
        if dofill then
            for i = -x , x do
                if not(xc + i > test2 and xc + i < test1) then
                    color(255,255,0)
                    dot(xc + i, yc + y)
                    dot(xc + i, yc - y)
                    redraw()inkey()
                end
            end
        else
            color(0,255,255)
            dot(xc + x, yc + y)
            dot(xc - x, yc + y)
            dot(xc + x, yc - y)
            dot(xc - x, yc - y)
            redraw()inkey()
        end
        if s >= 0 then
            s =s+ fh2 * (1 - x)
            x =x- 1
        end
        s =s+ w2 * ((4 * y) + 6)
        y =y+ 1
    end

end



回答3:


In vb.net you have both Graphics.DrawEllipse and Graphics.DrawArc. In Lua you may be able to use Cairo which I know has a arc function.

If you where to make a ellipse in a GraphicsPath in .Net and where to reverse engineer how it is stored in memory, you would find out that it is stored as four bezier curves. I implemented my own graphics library in vb.net once, and that was how I did it. The best resource I found at the time where a implementation in Actionscript, that I unfortunately was unable to locate aswell as that graphics library I was talking about.

TLDR; You should have a look at bezier curves.




回答4:


A completely different, and very simple take on this, although the ellipse doesn't seem as "pretty" as the other algorithms; this just uses the mathematical definition of an ellipse and, looping over x calculates the y coordinate given x, w, and h.

Public Shared Function DrawEllipse2(ByVal xc As Integer, ByVal yc As Integer, ByVal w As Integer, ByVal h As Integer, ByVal doFill As Boolean) As Drawing.Bitmap
    Dim bm As New Drawing.Bitmap(w * w, h * h)
    For x As Integer = xc - w To xc + w
        Dim y As Integer = CInt((Math.Sqrt(1 - ((x * x) / (w * w)))) * h)
        If doFill Then
            For j As Integer = -y To y
                bm.SetPixel(w + x, h + j, Drawing.Color.Red)
            Next
        Else
            bm.SetPixel(w + x, h + y, Drawing.Color.Red)
            bm.SetPixel(w + x, h - y, Drawing.Color.Red)
        End If
    Next
    Return bm
End Function


来源:https://stackoverflow.com/questions/30384333/draw-an-ellipse-sqrt-based-function

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!