class Graphics

Constants

DEFAULT_SPHERE_ITERATIONS
OCTAHEDRON

defined w/counter-clockwise triangles

SQUARE

Defines counter-clockwise points used in OpenGL TRIANGLE_STRIP to create a circle on the X/Z plane. Don't include center point here; It is added when outputting the circle.

XMINUS
XPLUS
YMINUS
YPLUS
ZMINUS
ZPLUS

Public Class Methods

boxFromCorners(p0, p1) click to toggle source

Build box from corners. All faces are counter-clockwise.

# File examples/ruboids/ruboids/Graphics.rb, line 73
def Graphics.boxFromCorners(p0, p1)
    pa = p0.dup()
    pb = p1.dup()

    # Make sure all coords of pa are < all coords of pb
    if pa.x > pb.x
        tmp = pa.x; pa.x = pb.x; pb.x = tmp
    end
    if pa.y > pb.y
        tmp = pa.y; pa.y = pb.y; pb.y = tmp
    end
    if pa.z > pb.z
        tmp = pa.z; pa.z = pb.z; pb.z = tmp
    end

    Begin(QUAD_STRIP)

    # top
    Vertex(pb.x, pb.y, pa.z)
    Vertex(pa.x, pb.y, pa.z)
    # top/front
    Vertex(pb.x, pb.y, pb.z)
    Vertex(pa.x, pb.y, pb.z)
    # front/bottom
    Vertex(pb.x, pa.y, pb.z)
    Vertex(pa.x, pa.y, pb.z)
    # bottom/back
    Vertex(pb.x, pa.y, pa.z)
    Vertex(pa.x, pa.y, pa.z)
    # back/top
    Vertex(pb.x, pb.y, pa.z)
    Vertex(pa.x, pb.y, pa.z)

    End()

    Begin(QUADS)

    # left
    Vertex(pa.x, pa.y, pb.z)
    Vertex(pa.x, pa.y, pa.z)
    Vertex(pa.x, pb.y, pa.z)
    Vertex(pa.x, pb.y, pb.z)

    # right
    Vertex(pb.x, pa.y, pb.z)
    Vertex(pb.x, pa.y, pa.z)
    Vertex(pb.x, pb.y, pa.z)
    Vertex(pb.x, pb.y, pb.z)

    End()
end
buildCircle(iterations, circle) click to toggle source

Different than buildSphere because we are creating triangles to be used in an OpenGL TRIANGLE_FAN operation. Thus the first point (the center) is always inviolate. We create new points between the remaining points.

# File examples/ruboids/ruboids/Graphics.rb, line 246
def Graphics.buildCircle(iterations, circle)
    oldObj = circle
    # Subdivide each starting line segment (maxlevel - 1) times
    iterations -= 1
    iterations.times {
        # Create a new object. Allocate 2 * the number of points in the
        # the current approximation. Subtract one because the last point
        # (same as the first point) is simply copied.
        newObj = Array.new(oldObj.length * 2 - 1)

        prevP = nil
        j = 0
        oldObj.each { | p |
            if !prevP.nil?
                newObj[j] = prevP
                j += 1

                # New midpoint
                a = Point.midpoint(prevP, p)
                a.normalize!()
                newObj[j] = a
                j += 1
            end
            prevP = p
        }
        newObj[j] = prevP  # Copy last point

        # Continue subdividing new triangles
        oldObj = newObj
    }
    return oldObj
end
buildSphere(iterations, sphere) click to toggle source

Subdivide each triangle in the oldObj approximation and normalize

the new points thus generated to lie on the surface of the unit
sphere.

Each input triangle with vertices labelled [0,1,2] as shown

below will be turned into four new triangles:

                      Make new points
                              a = (0+2)/2
                              b = (0+1)/2
                              c = (1+2)/2
        1
       /\             Normalize a, b, c
      /  \
    b/____\ c         Construct new counter-clockwise triangles
    /\    /\                  [a,b,0]
   /  \  /  \                 [c,1,b]
  /____\/____\                [c,b,a]
 0      a     2               [2,c,a]

The normalize step (which makes each point a, b, c unit distance from the origin) is where we can modify the sphere's shape.

# File examples/ruboids/ruboids/Graphics.rb, line 185
def Graphics.buildSphere(iterations, sphere)
    oldObj = sphere
    # Subdivide each starting triangle (maxlevel - 1) times
    iterations -= 1
    iterations.times {
        # Create a new object. Allocate 4 * the number of points in the
        # the current approximation.
        newObj = Array.new(oldObj.length * 4)

        j = 0
        oldObj.each { | oldt |
            # New midpoints
            a = Point.midpoint(oldt.points[0], oldt.points[2])
            a.normalize!()
            b = Point.midpoint(oldt.points[0], oldt.points[1])
            b.normalize!()
            c = Point.midpoint(oldt.points[1], oldt.points[2])
            c.normalize!()

            # New triangeles. Their vertices are counter-clockwise.
            newObj[j] = Triangle.new(a, b, oldt.points[0])
            j += 1
            newObj[j] = Triangle.new(c, oldt.points[1], b)
            j += 1
            newObj[j] = Triangle.new(c, b, a)
            j += 1
            newObj[j] = Triangle.new(oldt.points[2], c, a)
            j += 1
        }

        # Continue subdividing new triangles
        oldObj = newObj
    }
    return oldObj
end
circle(iterations = DEFAULT_SPHERE_ITERATIONS, counterClockwise = true) click to toggle source

Creates a circle in the X/Z plane. To have the circle's normal point down (-Y), specify clockwise instead of counter-clockwise. To create the circle in another plane, call OpenGL's Rotate() method before calling this.

# File examples/ruboids/ruboids/Graphics.rb, line 225
def Graphics.circle(iterations = DEFAULT_SPHERE_ITERATIONS,
                    counterClockwise = true)
    if @@circles[iterations].nil?
        @@circles[iterations] = buildCircle(iterations, SQUARE)
    end
    circle = @@circles[iterations] 
    
    Begin(TRIANGLE_FAN)
    Vertex(0, 0, 0)
    if counterClockwise
        circle.each { | p | Vertex(p.x, 0, p.z) }
    else
        circle.reverse.each { | p | Vertex(p.x, 0, p.z) }
    end
    End()
end
degreesToRadians(deg) click to toggle source
# File examples/ruboids/ruboids/Graphics.rb, line 46
def Graphics.degreesToRadians(deg)
    return deg * Math::PI / 180.0
end
radiansToDegrees(rad) click to toggle source
# File examples/ruboids/ruboids/Graphics.rb, line 42
def Graphics.radiansToDegrees(rad)
    return rad * 180.0 / Math::PI
end
rotations(v) click to toggle source

Given a vector, return a point containing x, y, z rotation angles.

atan2(x, y) = the angle formed with the x axis by the ray from the origin to the point {x,y}

# File examples/ruboids/ruboids/Graphics.rb, line 54
def Graphics.rotations(v)
    return Point::ORIGIN.dup() if v.nil?
    return v if v == Point::ORIGIN

    x = Math.atan2(v.y, v.z)
    y = Math.atan2(v.z, v.x)
    z = Math.atan2(v.y, v.x)

    rot = Point.new(z, x, y)
    rot.add(Math::PI).multiplyBy(180.0).divideBy(Math::PI)

    rot.x = rot.x.to_i
    rot.y = rot.y.to_i
    rot.z = rot.z.to_i

    return rot
end
sphere(iterations = DEFAULT_SPHERE_ITERATIONS, counterClockwise = true) click to toggle source

sphere() (and buildSphere()) - generate a triangle mesh approximating a sphere by recursive subdivision. First approximation is an octahedron; each level of refinement increases the number of triangles by a factor of 4.

Level 3 (128 triangles) is a good tradeoff if gouraud shading is used to render the database.

Usage: sphere [level] [counterClockwise]

The value level is an integer >= 1 setting the recursion level
        (default = DEFAULT_SPHERE_ITERATIONS).
The boolean counterClockwise causes triangles to be generated
        with vertices in counterclockwise order as viewed from
        the outside in a RHS coordinate system. The default is
        counter-clockwise.

@author Jon Leech (leech@cs.unc.edu) 3/24/89 (C version) Ruby version by Jim Menard (jimm@io.com), May 2001.

# File examples/ruboids/ruboids/Graphics.rb, line 144
def Graphics.sphere(iterations = DEFAULT_SPHERE_ITERATIONS,
                    counterClockwise = true)
    if @@spheres[iterations].nil?
        @@spheres[iterations] = buildSphere(iterations, OCTAHEDRON)
    end
    sphere = @@spheres[iterations] 
    
    Begin(TRIANGLES)
    sphere.each { | triangle |
        triangle.points.each { | p |
            Vertex(p.x, p.y, p.z) if counterClockwise
            Vertex(p.z, p.y, p.x) if !counterClockwise
        }
    }
    End()
end