Geometric primitives factory

Announcing a simple class for creating basic geometric shapes
http://www.ews.uiuc.edu/~dherring/Shapes.java

This class creates the TriangleArray geometry for a number of different shapes.

Currently included:

  • sphere and ellipsoid (slices/stacks specification)
  • geodesic sphere and ellipsoid (try it!)
  • torus

Planning:

  • disk
  • cone
  • tube (cylinder)

Please give me feedback on ways to improve these methods. I hope they prove useful to someone.

Later,
Daniel

Cool! Will try adding them to my scene graph and let you know how it goes ;p

After implementing some other things, I’ve started redesigning Shapes.java.

Instead of having constructors for circles/ellipses with varying radii, Shapes.java will create only basic shapes (sphere, geodesicSphere, box, …) with unit lengths and radii. Another class (GeometricMath) can then be used to stretch and manipulate these shapes into spheres, ellipses and boxes of varying dimensions.

Any thoughts? Breaking things up will reduce efficiency slightly, but I think it makes things cleaner and more powerful.

Later,
Daniel

You could always separate the primitive in different classes. Maybe have individual classes: sphere, torus, etc… Then you can also have a PrimitiveFactory class, that would attempt to re-use geometries (by storing them in a hashtable or something), instead of creating new ones all the time.

An idea that I’ve seen somewhere is to have auxiliary ParametricFunction2D and ParametricFunction3D classes, that primitive could implement to describe a shape by a function instead of complex-looking for-loops.

A Line Function could then be implemented as:

 
class LineFunction
    implements ParametricFunction2D
{

    public LineFunction(Point2d point2d, Point2d point2d1)
    {
        a = new Point2d();
        b = new Point2d();
        a.set(point2d);
        b.set(point2d1);
    }

    public void f(double d, Point2d point2d)
    {
        point2d.set(fx(d), fy(d));
    }

    public double fx(double d)
    {
        return (a.x + (b.x - a.x * d);
    }

    public double fy(double d)
    {
        return (a.y + (b.y - a.y * d);
    }

    private Point2d a;
    private Point2d b;

And this way, we need a more general method which takes any ParametricFunction2D and returns a ready to use geometry.

Not sure how useful it’s this approach. Just a thought.

And again, if using OpenGl, shouldn’t we take advantage of the fact that GLU exists? There are many utility functions already built-in, such as gluCylinder and the Quadric stuff. But I don’t think the problem of how to have Xith taking advantage of the underlying low-level API has been solved yet.

great stuff :slight_smile: I haven’t checked it out yet but we really do need a better (and more officially supported) package than TestUtils IMHO.

Is GLU even in JOGL? I don’t think we should use it even if we could because, as you said, it ties you more to the underlying API.

Personally, I like the idea of having individual classes. It makes the code more clear in meaning, and you can have nicly defined (as in javadoc) relationships like Cube extends Rectangle :slight_smile:

Is Geometry the best class for the primitive methods to return do you think? I think Shape3D might make more sense to a newbie, as it removes having to create a Shape3D passing the Geometry. And getting the geometry is only a single call away anyway (as is setting a custom appearence). If we had a class for each primitive that extended Shape3D (or another primitive) - that makes a lot of sense IMHO.

For example:

TransformGroup tg = new TransformGroup();
tg.addChild(new Cube(4f));

is a bit nicer than what we currenlty have:
TransformGroup tg = new TransformGroup();
tg.addChild(new Shape3D(TestUtils.createCubeViaTriangles(4,3,0,0,true,true)));

Using the javax.vecmath classes to specify the location and the likes is also better - as: new Cube (4, new Vector3f(1f,1f,1f)) is a bit more meaningfull than new Cube (4,1,1,1).

If we do go with this way then I think a package for the primitives would be best suited - com.xith3d.primitives.

Once this code is more or less complete, I’ll commit it in.

Cheers,

Will.

edit: btw - I’m glad you started this topic as I was just about to bring this issue up myself

I don’t see the need for an individual class per shape. Yes, they have this in Java3D; but no, I don’t think its a good idea.

My proposal:

  • Shapes.java generates the basic shapes. (disk, sphere, cone, cylinder, cube, torus, …) Each shape would be generated with unit length/radius where possible. Each shape would be generated from scratch (no caching).
  • GeometricMath.java can manipulate those shapes into a more complete set. Basic ops: scaling, shifting, joining, … Spheres become ellipsoids, cubes become prisms, …

Examples:


// Standard sphere of radius 1
Shape3D sphere=Shapes.sphere(slices, stacks);
// Ellipsoid based on a geodesic sphere
Shape3D ellipse=GeometricMath.multiply(Shapes.geodesicSphere(tesselations), new Vector3f(1, 0.8f, 0.5f));
// Elliptical toroid on a tilted axis
Shape3D toroid=GeometricMath.multiply(Shapes.torus(innerRadius, outerRadius, slices, stacks), new Matrix3f(1,.2f,.3f, 0,1,0, .2f,0,2));

This, to me, seems more straightforward than having “Ellipse extends Sphere” (or the other way around). The Java3D sphere class seems pointless. Looking at it, we have

  • Sphere extends Primitive (why not extend Geometry or Shape3D directly? I believe because they wanted caching.)
  • It has constructors to vary the radius, feature set (normals, texture coords, etc.), and appearance
  • Its methods only allow you to query the constructor parameters, duplicate the node, or change the appearance

In short, the Primitive class duplicates a lot of Shape3D functionality, and the Sphere class doesn’t let you do many meaningful things with Spheres. The only useful thing it does is return the same object if you request a new Sphere with the same parameters… but to do this requires keeping a reference to the old object; thereby preventing garbage collection. Just imagine the (pointless) code:


// Randomly display 200 different size spheres, 3 at a time
for(int i=0; i<200; i++)
{
if(i>2)
{
// randomly remove an old sphere
...
}
// generate the new sphere
...=new Sphere(i+1);
// display it
...
}

Yes, this is a lame example, but wouldn’t Java3D’s Sphere class prevent any of these 200 Spheres from being garbage collected even though only three are ever being used at a time?

The way my code is shaping up:

Shapes.java: Creates Shape3D objects. This is a newbie-friendly front-end to auto-select from among:

  • LineShapes.java: Create objects of type LineArray (not implemented yet)
  • LineStripShapes.java: Create objects of type LineStripArray (not implemented yet)
  • TriangleShapes.java: Creates objects of type TriangleArray (current default)
  • TriangleStripShapes.java: Creates objects of type TriangleStripArray (not implemented yet)
  • QuadShapes.java: Creates objects of type QuadArray (not implemented yet)

GeometricMath.java: skew/scale/offset/join any of the above

The base shapes are currently box, disk, sphere, geodesic sphere, cone, cylinder, and torus.

For each basic shape, length/radius defaults to 1 where possible. Passing in
int features=GeometryArray.COLOR_3 | GeometryArray.NORMALS | …
triggers the code to generate the necessary data.

sounds like your doing a good job - but I still think individual classes are better. I’m not sure if we should worry about code optimisation in the API - I’m sure you could think up a way to reuse the objects or something anyway. Taking your example - oldSphere.resize(1f) would do the trick for example.

Going the O-O approach lets you take advantage of some of the nice OO features, such as

testCollision(Rectangle r).

The thing is they are still Shape3D’s at the end of it, they just have a bit more info in them.

Having Cube and Sphere as specialised Rectangle and Ellipse objeccts (by extending them) isn’t nessesary but it’s very little work and just adds some more primitives to play with.

Personally, I can’t see any disadvantages with taking this approach - only advantages from an O-O design, code readability, and maintainability perspective.

Shape3D sphere=new Sphere(slices, stacks);

looks better than

Shape3D sphere=Shapes.sphere(slices, stacks);

from a readability point of view for example - the first code is very self explanitory.

Cheers,

Will.

… must get more sleep… must finish math homework…
I’m probably gonna take a break on this for a week to get some other business in order, but here’s my current code.
http://www.ews.uiuc.edu/~dherring/alpha1.zip

Take a look at what’s there; its a reasonably good outline of my vision for this package. Shapes.java, TriangleShapes.java, and GeometricMath.java are mostly finished so you can play with things to see how they work.

Sample


import primitives.*;
...
// Show off the basic shapes
Shape3D cube=Shapes.cube();
Shape3D sphere=Shapes.sphere();
Shape3D geodesicSphere=Shapes.geodesicSphere();
Shape3D cone=Shapes.cone();
Shape3D cylinder=Shapes.cylinder(1);
Shape3D donut=Shapes.torus(0.5f);
Shape3D saucer=Shapes.disk();
Shape3D floppy=Shapes.ring(0.75f);
...
// Show off GeometricMath
Shape3D prism=Shapes.cube();
GeometricMath.add(shape, new Matrix3f(0,0,1, 0,0,1, 0,0,0));
Shape3D ellipsoid=Shapes.geodesicSphere();
GeometricMath.multiply(ellipsoid, new Vector3f(1,0.8f,0.5f));
Shape3D triblob=Shapes.geodesicSphere();
GeometricMath.deform(shape, new Matrix3f(1,0,.5f, -.4f,.5f,0, .2f,0,.8f));

Feel free to fill in the blanks. :wink:
Daniel

I was having a look around the code, and I think it would be very useful to add texture coordinates to the geometries too.

The way I undestand Xith, if the geometry doesn’t have such information, then textures simply won’t work. Am I mistaken?

Hi Pedro,
I believe you are correct. As of now, my classes don’t support textures.

There are several things to do before these classes are ready for general release:

  • Add a “grid” object (a bunch of adjacent triangles/squares); maybe some other shapes as well
  • Fix up normal generation
  • Add texture support
  • Rework GeometricMath.deform into a general quadratic function (i.e. x <- x+a+Bx+x_transposeC*x)
  • Have the appropriate GeometricMath functions also recalculate the normals
  • Implement the QuadShapes, LineShapes, and TriangleStripShapes classes
  • Implement the j3d…Primititive compatibility layer (to simplify ports, and because some people prefer the syntax)
  • Refactor some algorithms to remove redundant calculations
  • Many other steps to world domination :wink:

Later,
Daniel

Your code works really well for me so far. Great job and thanks for taking the initiative! As a side effect, I hope you are not seing the world around you as collection of triangles and vertices :wink: I’m looking forward to see all these new features indeed ::slight_smile:

Just a quick question: when do you think you’ll be adding texture support?

And perhaps, we should leave the world dominatio task to pink & the brain* :stuck_out_tongue:

  • the cartoon

EDIT: spelling

Changes:
Shapes.java - modified the methods to select from white, colored, or textured objects
TriangleShapes.java - added texture support
GeometricMath.java - removed deform(); added quadratic()

Get the new files at
http://www.ews.uiuc.edu/~dherring/alpha2.zip

Bug: The texture coordinates for geodesicSphere are currently calculated incorrectly at the wrap between 0 and 2 Pi. My opinion is that this sphere should really have 20 texture sets; one for each face of the icosahedron. This is a long-term todo while I think about it.