require 'opengl' module Fox HANDLE_SIZE = 4.0 # # OpenGL point object # class FXGLPoint < FXGLObject # Point position, in model coordinates (a 3-element array) attr_accessor :pos # # Returns an initialized FXGLPoint instance. # If no arguments are passed to #new, the initial point position is # (0.0, 0.0, 0.0). You can specify a different initial position by # passing in the x, y and z coordinates individually: # # aPoint = FXGLPoint.new(x, y, z) # # or as a 3-element array: # # aPoint = FXGLPoint.new([x, y, z]) # def initialize(*args) super() if args.length == 0 @pos = [0.0, 0.0, 0.0] elsif args.length == 3 @pos = [args[0], args[1], args[2]] else @pos = args[0] end end # # Return the bounding box (an FXRangef instance) for this point. # def bounds FXRangef.new(@pos[0], @pos[0], @pos[1], @pos[1], @pos[2], @pos[2]) end # # Draw this point into _viewer_ (an FXGLViewer instance). # def draw(viewer) GL::Color(0.0, 0.0, 1.0) GL::PointSize(HANDLE_SIZE) GL::Begin(GL::POINTS) GL::Vertex(@pos) GL::End() end # # Perform hit test for this point in _viewer_ (an FXGLViewer instance). # def hit(viewer) GL::Begin(GL::POINTS) GL::Vertex(@pos) GL::End() end end # # OpenGL line object # class FXGLLine < FXGLObject # Starting point for line [FXGLPoint] attr_accessor :fm # End point for line [FXGLPoint] attr_accessor :to # # Return an initialized FXGLLine instance. # # If no arguments are passed to #new, the initial starting and ending # points for the line are (-0.5, 0.0, 0.0) and (0.5, 0.0, 0.0), respectively. # You can specify different initial start and end points by passing in # another FXGLLine instance from which to copy the start and end point # values, e.g. # # aLine = FXGLLine.new(anotherLine) # # or by passing in the x, y and z coordinates individually: # # aLine = FXGLLine.new(x0, y0, z0, x1, y1, z1) # def initialize(*args) super() if args.length == 0 @fm = FXGLPoint.new(-0.5, 0.0, 0.0) @to = FXGLPoint.new( 0.5, 0.0, 0.0) elsif args.length == 1 @fm = args[0].fm @to = args[0].to else @fm = FXGLPoint.new(args[0], args[1], args[2]) @to = FXGLPoint.new(args[3], args[4], args[5]) end end # # Return the bounding box (an FXRangef instance) for this line. # def bounds FXRangef.new([@fm.pos[0], @to.pos[0]].min, [@fm.pos[0], @to.pos[0]].max, [@fm.pos[1], @to.pos[1]].min, [@fm.pos[1], @to.pos[1]].max, [@fm.pos[2], @to.pos[2]].min, [@fm.pos[2], @to.pos[2]].max) end # # Draw this line into _viewer_ (an FXGLViewer instance). # def draw(viewer) GL::Color(1.0, 0.0, 0.0) GL::PointSize(HANDLE_SIZE) GL::Begin(GL::LINES) GL::Vertex(@fm.pos) GL::Vertex(@to.pos) GL::End() end # # Perform hit-test for this line in _viewer_ (an FXGLViewer instance). # def hit(viewer) GL::Begin(GL::LINES) GL::Vertex(@fm.pos) GL::Vertex(@to.pos) GL::End() end end # # OpenGL cube object # class FXGLCube < FXGLShape # Cube width [Float] attr_accessor :width # Cube height [Float] attr_accessor :height # Cube depth [Float] attr_accessor :depth # # Return an initialized FXGLCube instance. # # One option is to initialize the cube with a specified origin, # width, height and depth: # # aCube = FXGLCube.new(x, y, z, w, h, d) # # If left unspecified, the width (_w_), height (_h_) and depth (_d_) # default to 1.0. # # Another option is to initialize the cube with a specified origin, # width, height, depth and material: # # aCube = FXGLCube.new(x, y, z, w, h, d, material) # # where the _material_ is an FXMaterial instance. # def initialize(*args) if args.length == 7 super(args[0], args[1], args[2], SHADING_SMOOTH|STYLE_SURFACE, args[6], args[6]) else super(args[0], args[1], args[2], SHADING_SMOOTH|STYLE_SURFACE) end @width = args[3] ? args[3] : 1.0 @height = args[4] ? args[4] : 1.0 @depth = args[5] ? args[5] : 1.0 setRange(FXRangef.new(-0.5*@width, 0.5*@width, -0.5*@height, 0.5*@height, -0.5*@depth, 0.5*@depth)) end # # Draws this cube into _viewer_ (an FXGLViewer instance). # def drawshape(viewer) xmin, xmax = -0.5*@width, 0.5*@width ymin, ymax = -0.5*@height, 0.5*@height zmin, zmax = -0.5*@depth, 0.5*@depth # Draw low face GL::Begin(GL::TRIANGLE_STRIP) GL::Normal(0.0, 0.0, -1.0) GL::Vertex(xmin, ymin, zmin) GL::Vertex(xmin, ymax, zmin) GL::Vertex(xmax, ymin, zmin) GL::Vertex(xmax, ymax, zmin) GL::End() # Draw east face GL::Begin(GL::TRIANGLE_STRIP) GL::Normal(1.0, 0.0, 0.0) GL::Vertex(xmax, ymin, zmin) GL::Vertex(xmax, ymax, zmin) GL::Vertex(xmax, ymin, zmax) GL::Vertex(xmax, ymax, zmax) GL::End() # Draw high face GL::Begin(GL::TRIANGLE_STRIP) GL::Normal(0.0, 0.0, 1.0) GL::Vertex(xmax, ymin, zmax) GL::Vertex(xmax, ymax, zmax) GL::Vertex(xmin, ymin, zmax) GL::Vertex(xmin, ymax, zmax) GL::End() # Draw west face GL::Begin(GL::TRIANGLE_STRIP) GL::Normal(-1.0, 0.0, 0.0) GL::Vertex(xmin, ymin, zmax) GL::Vertex(xmin, ymax, zmax) GL::Vertex(xmin, ymin, zmin) GL::Vertex(xmin, ymax, zmin) GL::End() # Draw north face GL::Begin(GL::TRIANGLE_STRIP) GL::Normal(0.0, 1.0, 0.0) GL::Vertex(xmin, ymax, zmin) GL::Vertex(xmin, ymax, zmax) GL::Vertex(xmax, ymax, zmin) GL::Vertex(xmax, ymax, zmax) GL::End() # Draw south face GL::Begin(GL::TRIANGLE_STRIP) GL::Normal(0.0, -1.0, 0.0) GL::Vertex(xmin, ymin, zmax) GL::Vertex(xmin, ymin, zmin) GL::Vertex(xmax, ymin, zmax) GL::Vertex(xmax, ymin, zmin) GL::End() end end # # OpenGL cone object # class FXGLCone < FXGLShape # Cone fidelity SLICES_NUMBER = 20 STACKS_NUMBER = 20 LOOPS = 4 # Cone height [Float] attr_accessor :height # Cone base radius [Float] attr_accessor :radius # Number of slices (default is 20) [Integer] attr_accessor :slices # Number of stacks (default is 20) [Integer] attr_accessor :stacks # Number of loops (default is 4) [Integer] attr_accessor :loops # # Returns an initialized FXGLCone instance. # # One option is to initialize the cone with a specified origin, # height and radius: # # aCone = FXGLCone.new(x, y, z, h, r) # # If left unspecified, the height (_h_) and radius (_r_) default to 1.0. # # Another option is to initialize the cone with a specified origin, # height, radius and material: # # aCone = FXGLCone.new(x, y, z, h, r, material) # # where the _material_ is an FXMaterial instance. # def initialize(*args) if args.length == 5 super(args[0], args[1], args[2], SHADING_SMOOTH|STYLE_SURFACE) elsif args.length == 6 super(args[0], args[1], args[2], SHADING_SMOOTH|STYLE_SURFACE, args[5], args[5]) end @height = args[3] ? args[3] : 1.0 @radius = args[4] ? args[4] : 1.0 @slices = SLICES_NUMBER @stacks = STACKS_NUMBER @loops = LOOPS setRange(FXRangef.new(-@radius, @radius, 0, @height, -@radius, @radius)) end # # Draw this cone into _viewer_ (an FXGLViewer instance). # def drawshape(viewer) quad = GLU::NewQuadric() GLU::QuadricDrawStyle(quad, GLU::FILL) GL::PushMatrix() GL::Rotate(-90, 1, 0, 0) GLU::Cylinder(quad, @radius, 0, @height, @slices, @stacks) GLU::QuadricOrientation(quad, GLU::INSIDE) GLU::Disk(quad, 0, @radius, @slices, @loops) GLU::DeleteQuadric(quad) GL::PopMatrix() end end # # OpenGL cylinder object # class FXGLCylinder < FXGLShape # Cylinder fidelity SLICES_NUMBER = 20 STACKS_NUMBER = 20 LOOPS = 4 # Cylinder height [Float] attr_accessor :height # Cylinder radius [Float] attr_accessor :radius # Number of slices (default is 20) [Integer] attr_accessor :slices # Number of stacks (default is 20) [Integer] attr_accessor :stacks # Number of loops (default is 4) [Integer] attr_accessor :loops # # Returns an initialized FXGLCylinder instance. # # One option is to initialize the cylinder with a specified origin, # height and radius: # # aCylinder = FXGLCylinder.new(x, y, z, h, r) # # If left unspecified, the height (_h_) and radius (_r_) default to 1.0. # # Another option is to initialize the cylinder with a specified origin, # height, radius and material: # # aCylinder = FXGLCylinder.new(x, y, z, h, r, material) # # where the _material_ is an FXMaterial instance. # def initialize(*args) if args.length == 5 super(args[0], args[1], args[2], SHADING_SMOOTH|STYLE_SURFACE) else super(args[0], args[1], args[2], SHADING_SMOOTH|STYLE_SURFACE, args[5], args[5]) end @height = args[3] ? args[3] : 1.0 @radius = args[4] ? args[4] : 1.0 @slices = SLICES_NUMBER @stacks = STACKS_NUMBER @loops = LOOPS setRange(FXRangef.new(-@radius, @radius, 0, @height, -@radius, @radius)) end # # Draw this cylinder into _viewer_ (an FXGLViewer instance). # def drawshape(viewer) quad = GLU::NewQuadric() GLU::QuadricDrawStyle(quad, GLU::FILL) GL::PushMatrix() GL::Rotate(-90, 1, 0, 0) GLU::Cylinder(quad, @radius, @radius, @height, @slices, @stacks) GLU::QuadricOrientation(quad, GLU::INSIDE) GLU::Disk(quad, 0, @radius, @slices, @loops) GL::Translate(0, 0, @height) GLU::QuadricOrientation(quad, GLU::OUTSIDE) GLU::Disk(quad, 0, @radius, @slices, @loops) GL::PopMatrix() GLU::DeleteQuadric(quad) end end # # OpenGL sphere object # class FXGLSphere < FXGLShape # Sphere fidelity SLICES_NUMBER = 20 STACKS_NUMBER = 20 # Sphere radius [Float] attr_accessor :radius # Number of slices (default is 20) [Integer] attr_accessor :slices # Number of stacks (default is 20) [Integer] attr_accessor :stacks # # Returns an initialized FXGLSphere instance. # # One option is to initialize the sphere with a specified origin and # radius: # # aSphere = FXGLSphere.new(x, y, z, r) # # If left unspecified, the radius (_r_) defaults to 1.0. # # Another option is to initialize the sphere with a specified origin, # radius and material: # # aSphere = FXGLSphere.new(x, y, z, r, material) # # where the _material_ is an FXMaterial instance. # def initialize(*args) if args.length == 4 super(args[0], args[1], args[2], SHADING_SMOOTH|STYLE_SURFACE) else super(args[0], args[1], args[2], SHADING_SMOOTH|STYLE_SURFACE, args[4], args[4]) end @radius = args[3] ? args[3] : 1.0 @slices = SLICES_NUMBER @stacks = STACKS_NUMBER setRange(FXRangef.new(-@radius, @radius, -@radius, @radius, -@radius, @radius)) end # # Draw this sphere into _viewer_ (an FXGLViewer instance). # def drawshape(viewer) quad = GLU::NewQuadric() GLU::QuadricDrawStyle(quad, GLU::FILL) GLU::Sphere(quad, @radius, @slices, @stacks) GLU::DeleteQuadric(quad) end end end