commit df53c08794f6723b068eb2a921a09a70bfd67285 Author: Val Erastov Date: Mon Aug 11 20:20:49 2014 -0700 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..7ebea6a4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +*.class +/target/ +/out/ +/.idea/ +*.iml + +# Package Files # +*.jar +*.war +*.ear diff --git a/lib/gluegen-java-src.zip b/lib/gluegen-java-src.zip new file mode 100644 index 00000000..4c852d39 Binary files /dev/null and b/lib/gluegen-java-src.zip differ diff --git a/lib/jogl-java-src.zip b/lib/jogl-java-src.zip new file mode 100644 index 00000000..73f4950a Binary files /dev/null and b/lib/jogl-java-src.zip differ diff --git a/src/cad/Cad.java b/src/cad/Cad.java new file mode 100644 index 00000000..ab9dbcc0 --- /dev/null +++ b/src/cad/Cad.java @@ -0,0 +1,342 @@ +package cad; + +import com.jogamp.newt.opengl.GLWindow; +import com.jogamp.opengl.util.FPSAnimator; + +import javax.media.opengl.GL2; +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLCapabilities; +import javax.media.opengl.GLEventListener; +import javax.media.opengl.GLProfile; +import javax.media.opengl.awt.AWTGLAutoDrawable; +import javax.media.opengl.awt.GLJPanel; +import java.awt.*; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; + +public class Cad implements GLEventListener, MouseListener, MouseMotionListener { + + + static { + GLProfile.initSingleton(); // The method allows JOGL to prepare some Linux-specific locking optimizations + } + + public static void main(String[] args) { + + // Get the default OpenGL profile, reflecting the best for your running platform + GLProfile glp = GLProfile.getDefault(); + // Specifies a set of OpenGL capabilities, based on your profile. + GLCapabilities caps = new GLCapabilities(glp); + // Create the OpenGL rendering canvas + GLWindow window = GLWindow.create(caps); + + // Create a animator that drives canvas' display() at the specified FPS. + final FPSAnimator animator = new FPSAnimator(window, 60, true); +// final Animator animator = new Animator(window); + window.addWindowListener(new com.jogamp.newt.event.WindowAdapter() { + @Override + public void windowDestroyNotify(com.jogamp.newt.event.WindowEvent arg0) { + // Use a dedicate thread to run the stop() to ensure that the + // animator stops before program exits. + new Thread() { + @Override + public void run() { + if (animator.isStarted()) + animator.stop(); // stop the animator loop + System.exit(0); + } + }.start(); + } + }); + + window.addGLEventListener(new Cad()); + + window.setSize(640, 480); + window.setTitle("CAD"); + window.setVisible(true); + } + + private float view_rotx = 20.0f, view_roty = 30.0f, view_rotz = 0.0f; + private int gear1, gear2, gear3; + private float angle = 0.0f; + + private int prevMouseX, prevMouseY; + private boolean mouseRButtonDown = false; + + public void init(GLAutoDrawable drawable) { + // Use debug pipeline + // drawable.setGL(new DebugGL(drawable.getGL())); + + GL2 gl = drawable.getGL().getGL2(); + + System.err.println("INIT GL IS: " + gl.getClass().getName()); + + System.err.println("Chosen GLCapabilities: " + drawable.getChosenGLCapabilities()); + + gl.setSwapInterval(1); + + float pos[] = {5.0f, 5.0f, 10.0f, 0.0f}; + float red[] = {0.8f, 0.1f, 0.0f, 1.0f}; + float green[] = {0.0f, 0.8f, 0.2f, 1.0f}; + float blue[] = {0.2f, 0.2f, 1.0f, 1.0f}; + + gl.glLightfv(GL2.GL_LIGHT0, GL2.GL_POSITION, pos, 0); + gl.glEnable(GL2.GL_CULL_FACE); + gl.glEnable(GL2.GL_LIGHTING); + gl.glEnable(GL2.GL_LIGHT0); + gl.glEnable(GL2.GL_DEPTH_TEST); + + /* make the gears */ + gear1 = gl.glGenLists(1); + gl.glNewList(gear1, GL2.GL_COMPILE); + gl.glMaterialfv(GL2.GL_FRONT, GL2.GL_AMBIENT_AND_DIFFUSE, red, 0); + gear(gl, 1.0f, 4.0f, 1.0f, 20, 0.7f); + gl.glEndList(); + + gear2 = gl.glGenLists(1); + gl.glNewList(gear2, GL2.GL_COMPILE); + gl.glMaterialfv(GL2.GL_FRONT, GL2.GL_AMBIENT_AND_DIFFUSE, green, 0); + gear(gl, 0.5f, 2.0f, 2.0f, 10, 0.7f); + gl.glEndList(); + + gear3 = gl.glGenLists(1); + gl.glNewList(gear3, GL2.GL_COMPILE); + gl.glMaterialfv(GL2.GL_FRONT, GL2.GL_AMBIENT_AND_DIFFUSE, blue, 0); + gear(gl, 1.3f, 2.0f, 0.5f, 10, 0.7f); + gl.glEndList(); + + gl.glEnable(GL2.GL_NORMALIZE); + + if (drawable instanceof AWTGLAutoDrawable) { + AWTGLAutoDrawable awtDrawable = (AWTGLAutoDrawable) drawable; + awtDrawable.addMouseListener(this); + awtDrawable.addMouseMotionListener(this); + } + } + + public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { + GL2 gl = drawable.getGL().getGL2(); + + float h = (float) height / (float) width; + + gl.glMatrixMode(GL2.GL_PROJECTION); + + System.err.println("GL_VENDOR: " + gl.glGetString(GL2.GL_VENDOR)); + System.err.println("GL_RENDERER: " + gl.glGetString(GL2.GL_RENDERER)); + System.err.println("GL_VERSION: " + gl.glGetString(GL2.GL_VERSION)); + gl.glLoadIdentity(); + gl.glFrustum(-1.0f, 1.0f, -h, h, 5.0f, 60.0f); + gl.glMatrixMode(GL2.GL_MODELVIEW); + gl.glLoadIdentity(); + gl.glTranslatef(0.0f, 0.0f, -40.0f); + } + + public void dispose(GLAutoDrawable drawable) { + System.out.println("Gears.dispose: " + drawable); + } + + public void display(GLAutoDrawable drawable) { + // Turn the gears' teeth + angle += 2.0f; + + // Get the GL corresponding to the drawable we are animating + GL2 gl = drawable.getGL().getGL2(); + + // Special handling for the case where the GLJPanel is translucent + // and wants to be composited with other Java 2D content + if ((drawable instanceof GLJPanel) && + !((GLJPanel) drawable).isOpaque() && + ((GLJPanel) drawable).shouldPreserveColorBufferIfTranslucent()) { + gl.glClear(GL2.GL_DEPTH_BUFFER_BIT); + } else { + gl.glClear(GL2.GL_COLOR_BUFFER_BIT | GL2.GL_DEPTH_BUFFER_BIT); + } + + // Rotate the entire assembly of gears based on how the user + // dragged the mouse around + gl.glPushMatrix(); + gl.glRotatef(view_rotx, 1.0f, 0.0f, 0.0f); + gl.glRotatef(view_roty, 0.0f, 1.0f, 0.0f); + gl.glRotatef(view_rotz, 0.0f, 0.0f, 1.0f); + + // Place the first gear and call its display list + gl.glPushMatrix(); + gl.glTranslatef(-3.0f, -2.0f, 0.0f); + gl.glRotatef(angle, 0.0f, 0.0f, 1.0f); + gl.glCallList(gear1); + gl.glPopMatrix(); + + // Place the second gear and call its display list + gl.glPushMatrix(); + gl.glTranslatef(3.1f, -2.0f, 0.0f); + gl.glRotatef(-2.0f * angle - 9.0f, 0.0f, 0.0f, 1.0f); + gl.glCallList(gear2); + gl.glPopMatrix(); + + // Place the third gear and call its display list + gl.glPushMatrix(); + gl.glTranslatef(-3.1f, 4.2f, 0.0f); + gl.glRotatef(-2.0f * angle - 25.0f, 0.0f, 0.0f, 1.0f); + gl.glCallList(gear3); + gl.glPopMatrix(); + + // Remember that every push needs a pop; this one is paired with + // rotating the entire gear assembly + gl.glPopMatrix(); + } + + public void displayChanged(GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged) { + } + + public static void gear(GL2 gl, + float inner_radius, + float outer_radius, + float width, + int teeth, + float tooth_depth) { + int i; + float r0, r1, r2; + float angle, da; + float u, v, len; + + r0 = inner_radius; + r1 = outer_radius - tooth_depth / 2.0f; + r2 = outer_radius + tooth_depth / 2.0f; + + da = 2.0f * (float) Math.PI / teeth / 4.0f; + + gl.glShadeModel(GL2.GL_FLAT); + + gl.glNormal3f(0.0f, 0.0f, 1.0f); + + /* draw front face */ + gl.glBegin(GL2.GL_QUAD_STRIP); + for (i = 0; i <= teeth; i++) { + angle = i * 2.0f * (float) Math.PI / teeth; + gl.glVertex3f(r0 * (float) Math.cos(angle), r0 * (float) Math.sin(angle), width * 0.5f); + gl.glVertex3f(r1 * (float) Math.cos(angle), r1 * (float) Math.sin(angle), width * 0.5f); + if (i < teeth) { + gl.glVertex3f(r0 * (float) Math.cos(angle), r0 * (float) Math.sin(angle), width * 0.5f); + gl.glVertex3f(r1 * (float) Math.cos(angle + 3.0f * da), r1 * (float) Math.sin(angle + 3.0f * da), width * 0.5f); + } + } + gl.glEnd(); + + /* draw front sides of teeth */ + gl.glBegin(GL2.GL_QUADS); + for (i = 0; i < teeth; i++) { + angle = i * 2.0f * (float) Math.PI / teeth; + gl.glVertex3f(r1 * (float) Math.cos(angle), r1 * (float) Math.sin(angle), width * 0.5f); + gl.glVertex3f(r2 * (float) Math.cos(angle + da), r2 * (float) Math.sin(angle + da), width * 0.5f); + gl.glVertex3f(r2 * (float) Math.cos(angle + 2.0f * da), r2 * (float) Math.sin(angle + 2.0f * da), width * 0.5f); + gl.glVertex3f(r1 * (float) Math.cos(angle + 3.0f * da), r1 * (float) Math.sin(angle + 3.0f * da), width * 0.5f); + } + gl.glEnd(); + + /* draw back face */ + gl.glBegin(GL2.GL_QUAD_STRIP); + for (i = 0; i <= teeth; i++) { + angle = i * 2.0f * (float) Math.PI / teeth; + gl.glVertex3f(r1 * (float) Math.cos(angle), r1 * (float) Math.sin(angle), -width * 0.5f); + gl.glVertex3f(r0 * (float) Math.cos(angle), r0 * (float) Math.sin(angle), -width * 0.5f); + gl.glVertex3f(r1 * (float) Math.cos(angle + 3 * da), r1 * (float) Math.sin(angle + 3 * da), -width * 0.5f); + gl.glVertex3f(r0 * (float) Math.cos(angle), r0 * (float) Math.sin(angle), -width * 0.5f); + } + gl.glEnd(); + + /* draw back sides of teeth */ + gl.glBegin(GL2.GL_QUADS); + for (i = 0; i < teeth; i++) { + angle = i * 2.0f * (float) Math.PI / teeth; + gl.glVertex3f(r1 * (float) Math.cos(angle + 3 * da), r1 * (float) Math.sin(angle + 3 * da), -width * 0.5f); + gl.glVertex3f(r2 * (float) Math.cos(angle + 2 * da), r2 * (float) Math.sin(angle + 2 * da), -width * 0.5f); + gl.glVertex3f(r2 * (float) Math.cos(angle + da), r2 * (float) Math.sin(angle + da), -width * 0.5f); + gl.glVertex3f(r1 * (float) Math.cos(angle), r1 * (float) Math.sin(angle), -width * 0.5f); + } + gl.glEnd(); + + /* draw outward faces of teeth */ + gl.glBegin(GL2.GL_QUAD_STRIP); + for (i = 0; i < teeth; i++) { + angle = i * 2.0f * (float) Math.PI / teeth; + gl.glVertex3f(r1 * (float) Math.cos(angle), r1 * (float) Math.sin(angle), width * 0.5f); + gl.glVertex3f(r1 * (float) Math.cos(angle), r1 * (float) Math.sin(angle), -width * 0.5f); + u = r2 * (float) Math.cos(angle + da) - r1 * (float) Math.cos(angle); + v = r2 * (float) Math.sin(angle + da) - r1 * (float) Math.sin(angle); + len = (float) Math.sqrt(u * u + v * v); + u /= len; + v /= len; + gl.glNormal3f(v, -u, 0.0f); + gl.glVertex3f(r2 * (float) Math.cos(angle + da), r2 * (float) Math.sin(angle + da), width * 0.5f); + gl.glVertex3f(r2 * (float) Math.cos(angle + da), r2 * (float) Math.sin(angle + da), -width * 0.5f); + gl.glNormal3f((float) Math.cos(angle), (float) Math.sin(angle), 0.0f); + gl.glVertex3f(r2 * (float) Math.cos(angle + 2 * da), r2 * (float) Math.sin(angle + 2 * da), width * 0.5f); + gl.glVertex3f(r2 * (float) Math.cos(angle + 2 * da), r2 * (float) Math.sin(angle + 2 * da), -width * 0.5f); + u = r1 * (float) Math.cos(angle + 3 * da) - r2 * (float) Math.cos(angle + 2 * da); + v = r1 * (float) Math.sin(angle + 3 * da) - r2 * (float) Math.sin(angle + 2 * da); + gl.glNormal3f(v, -u, 0.0f); + gl.glVertex3f(r1 * (float) Math.cos(angle + 3 * da), r1 * (float) Math.sin(angle + 3 * da), width * 0.5f); + gl.glVertex3f(r1 * (float) Math.cos(angle + 3 * da), r1 * (float) Math.sin(angle + 3 * da), -width * 0.5f); + gl.glNormal3f((float) Math.cos(angle), (float) Math.sin(angle), 0.0f); + } + gl.glVertex3f(r1 * (float) Math.cos(0), r1 * (float) Math.sin(0), width * 0.5f); + gl.glVertex3f(r1 * (float) Math.cos(0), r1 * (float) Math.sin(0), -width * 0.5f); + gl.glEnd(); + + gl.glShadeModel(GL2.GL_SMOOTH); + + /* draw inside radius cylinder */ + gl.glBegin(GL2.GL_QUAD_STRIP); + for (i = 0; i <= teeth; i++) { + angle = i * 2.0f * (float) Math.PI / teeth; + gl.glNormal3f(-(float) Math.cos(angle), -(float) Math.sin(angle), 0.0f); + gl.glVertex3f(r0 * (float) Math.cos(angle), r0 * (float) Math.sin(angle), -width * 0.5f); + gl.glVertex3f(r0 * (float) Math.cos(angle), r0 * (float) Math.sin(angle), width * 0.5f); + } + gl.glEnd(); + } + + // Methods required for the implementation of MouseListener + public void mouseEntered(MouseEvent e) { + } + + public void mouseExited(MouseEvent e) { + } + + public void mousePressed(MouseEvent e) { + prevMouseX = e.getX(); + prevMouseY = e.getY(); + if ((e.getModifiers() & e.BUTTON3_MASK) != 0) { + mouseRButtonDown = true; + } + } + + public void mouseReleased(MouseEvent e) { + if ((e.getModifiers() & e.BUTTON3_MASK) != 0) { + mouseRButtonDown = false; + } + } + + public void mouseClicked(MouseEvent e) { + } + + // Methods required for the implementation of MouseMotionListener + public void mouseDragged(MouseEvent e) { + int x = e.getX(); + int y = e.getY(); + Dimension size = e.getComponent().getSize(); + + float thetaY = 360.0f * ((float) (x - prevMouseX) / (float) size.width); + float thetaX = 360.0f * ((float) (prevMouseY - y) / (float) size.height); + + prevMouseX = x; + prevMouseY = y; + + view_rotx += thetaX; + view_roty += thetaY; + } + + public void mouseMoved(MouseEvent e) { + } +} diff --git a/src/cad/Gears.java b/src/cad/Gears.java new file mode 100644 index 00000000..b6bc3899 --- /dev/null +++ b/src/cad/Gears.java @@ -0,0 +1,337 @@ +package cad; + +import com.jogamp.opengl.util.Animator; + +import javax.media.opengl.GL2; +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLEventListener; +import javax.media.opengl.awt.AWTGLAutoDrawable; +import javax.media.opengl.awt.GLCanvas; +import javax.media.opengl.awt.GLJPanel; +import java.awt.*; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +/** + * Gears.java
+ * author: Brian Paul (converted to Java by Ron Cemer and Sven Goethel)

+ * + * This version is equal to Brian Paul's version 1.2 1999/10/21 + */ + +public class Gears implements GLEventListener, MouseListener, MouseMotionListener { + public static void main(String[] args) { + Frame frame = new Frame("Gear Demo"); + GLCanvas canvas = new GLCanvas(); + + final Gears gears = new Gears(); + canvas.addGLEventListener(gears); + + frame.add(canvas); + frame.setSize(300, 300); + final Animator animator = new Animator(canvas); + frame.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) { + // Run this on another thread than the AWT event queue to + // make sure the call to Animator.stop() completes before + // exiting + new Thread(new Runnable() { + public void run() { + animator.stop(); + System.exit(0); + } + }).start(); + } + }); + frame.setVisible(true); + animator.start(); + } + + private float view_rotx = 20.0f, view_roty = 30.0f, view_rotz = 0.0f; + private int gear1, gear2, gear3; + private float angle = 0.0f; + + private int prevMouseX, prevMouseY; + private boolean mouseRButtonDown = false; + + public void init(GLAutoDrawable drawable) { + // Use debug pipeline + // drawable.setGL(new DebugGL(drawable.getGL())); + + GL2 gl = drawable.getGL().getGL2(); + + System.err.println("INIT GL IS: " + gl.getClass().getName()); + + System.err.println("Chosen GLCapabilities: " + drawable.getChosenGLCapabilities()); + + gl.setSwapInterval(1); + + float pos[] = { 5.0f, 5.0f, 10.0f, 0.0f }; + float red[] = { 0.8f, 0.1f, 0.0f, 1.0f }; + float green[] = { 0.0f, 0.8f, 0.2f, 1.0f }; + float blue[] = { 0.2f, 0.2f, 1.0f, 1.0f }; + + gl.glLightfv(GL2.GL_LIGHT0, GL2.GL_POSITION, pos, 0); + gl.glEnable(GL2.GL_CULL_FACE); + gl.glEnable(GL2.GL_LIGHTING); + gl.glEnable(GL2.GL_LIGHT0); + gl.glEnable(GL2.GL_DEPTH_TEST); + + /* make the gears */ + gear1 = gl.glGenLists(1); + gl.glNewList(gear1, GL2.GL_COMPILE); + gl.glMaterialfv(GL2.GL_FRONT, GL2.GL_AMBIENT_AND_DIFFUSE, red, 0); + gear(gl, 1.0f, 4.0f, 1.0f, 20, 0.7f); + gl.glEndList(); + + gear2 = gl.glGenLists(1); + gl.glNewList(gear2, GL2.GL_COMPILE); + gl.glMaterialfv(GL2.GL_FRONT, GL2.GL_AMBIENT_AND_DIFFUSE, green, 0); + gear(gl, 0.5f, 2.0f, 2.0f, 10, 0.7f); + gl.glEndList(); + + gear3 = gl.glGenLists(1); + gl.glNewList(gear3, GL2.GL_COMPILE); + gl.glMaterialfv(GL2.GL_FRONT, GL2.GL_AMBIENT_AND_DIFFUSE, blue, 0); + gear(gl, 1.3f, 2.0f, 0.5f, 10, 0.7f); + gl.glEndList(); + + gl.glEnable(GL2.GL_NORMALIZE); + + if (drawable instanceof AWTGLAutoDrawable) { + AWTGLAutoDrawable awtDrawable = (AWTGLAutoDrawable) drawable; + awtDrawable.addMouseListener(this); + awtDrawable.addMouseMotionListener(this); + } + } + + public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { + GL2 gl = drawable.getGL().getGL2(); + + float h = (float)height / (float)width; + + gl.glMatrixMode(GL2.GL_PROJECTION); + + System.err.println("GL_VENDOR: " + gl.glGetString(GL2.GL_VENDOR)); + System.err.println("GL_RENDERER: " + gl.glGetString(GL2.GL_RENDERER)); + System.err.println("GL_VERSION: " + gl.glGetString(GL2.GL_VERSION)); + gl.glLoadIdentity(); + gl.glFrustum(-1.0f, 1.0f, -h, h, 5.0f, 60.0f); + gl.glMatrixMode(GL2.GL_MODELVIEW); + gl.glLoadIdentity(); + gl.glTranslatef(0.0f, 0.0f, -40.0f); + } + + public void dispose(GLAutoDrawable drawable) { + System.out.println("Gears.dispose: "+drawable); + } + + public void display(GLAutoDrawable drawable) { + // Turn the gears' teeth + angle += 2.0f; + + // Get the GL corresponding to the drawable we are animating + GL2 gl = drawable.getGL().getGL2(); + + // Special handling for the case where the GLJPanel is translucent + // and wants to be composited with other Java 2D content + if ((drawable instanceof GLJPanel) && + !((GLJPanel) drawable).isOpaque() && + ((GLJPanel) drawable).shouldPreserveColorBufferIfTranslucent()) { + gl.glClear(GL2.GL_DEPTH_BUFFER_BIT); + } else { + gl.glClear(GL2.GL_COLOR_BUFFER_BIT | GL2.GL_DEPTH_BUFFER_BIT); + } + + // Rotate the entire assembly of gears based on how the user + // dragged the mouse around + gl.glPushMatrix(); + gl.glRotatef(view_rotx, 1.0f, 0.0f, 0.0f); + gl.glRotatef(view_roty, 0.0f, 1.0f, 0.0f); + gl.glRotatef(view_rotz, 0.0f, 0.0f, 1.0f); + + // Place the first gear and call its display list + gl.glPushMatrix(); + gl.glTranslatef(-3.0f, -2.0f, 0.0f); + gl.glRotatef(angle, 0.0f, 0.0f, 1.0f); + gl.glCallList(gear1); + gl.glPopMatrix(); + + // Place the second gear and call its display list + gl.glPushMatrix(); + gl.glTranslatef(3.1f, -2.0f, 0.0f); + gl.glRotatef(-2.0f * angle - 9.0f, 0.0f, 0.0f, 1.0f); + gl.glCallList(gear2); + gl.glPopMatrix(); + + // Place the third gear and call its display list + gl.glPushMatrix(); + gl.glTranslatef(-3.1f, 4.2f, 0.0f); + gl.glRotatef(-2.0f * angle - 25.0f, 0.0f, 0.0f, 1.0f); + gl.glCallList(gear3); + gl.glPopMatrix(); + + // Remember that every push needs a pop; this one is paired with + // rotating the entire gear assembly + gl.glPopMatrix(); + } + + public void displayChanged(GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged) {} + + public static void gear(GL2 gl, + float inner_radius, + float outer_radius, + float width, + int teeth, + float tooth_depth) + { + int i; + float r0, r1, r2; + float angle, da; + float u, v, len; + + r0 = inner_radius; + r1 = outer_radius - tooth_depth / 2.0f; + r2 = outer_radius + tooth_depth / 2.0f; + + da = 2.0f * (float) Math.PI / teeth / 4.0f; + + gl.glShadeModel(GL2.GL_FLAT); + + gl.glNormal3f(0.0f, 0.0f, 1.0f); + + /* draw front face */ + gl.glBegin(GL2.GL_QUAD_STRIP); + for (i = 0; i <= teeth; i++) + { + angle = i * 2.0f * (float) Math.PI / teeth; + gl.glVertex3f(r0 * (float)Math.cos(angle), r0 * (float)Math.sin(angle), width * 0.5f); + gl.glVertex3f(r1 * (float)Math.cos(angle), r1 * (float)Math.sin(angle), width * 0.5f); + if(i < teeth) + { + gl.glVertex3f(r0 * (float)Math.cos(angle), r0 * (float)Math.sin(angle), width * 0.5f); + gl.glVertex3f(r1 * (float)Math.cos(angle + 3.0f * da), r1 * (float)Math.sin(angle + 3.0f * da), width * 0.5f); + } + } + gl.glEnd(); + + /* draw front sides of teeth */ + gl.glBegin(GL2.GL_QUADS); + for (i = 0; i < teeth; i++) + { + angle = i * 2.0f * (float) Math.PI / teeth; + gl.glVertex3f(r1 * (float)Math.cos(angle), r1 * (float)Math.sin(angle), width * 0.5f); + gl.glVertex3f(r2 * (float)Math.cos(angle + da), r2 * (float)Math.sin(angle + da), width * 0.5f); + gl.glVertex3f(r2 * (float)Math.cos(angle + 2.0f * da), r2 * (float)Math.sin(angle + 2.0f * da), width * 0.5f); + gl.glVertex3f(r1 * (float)Math.cos(angle + 3.0f * da), r1 * (float)Math.sin(angle + 3.0f * da), width * 0.5f); + } + gl.glEnd(); + + /* draw back face */ + gl.glBegin(GL2.GL_QUAD_STRIP); + for (i = 0; i <= teeth; i++) + { + angle = i * 2.0f * (float) Math.PI / teeth; + gl.glVertex3f(r1 * (float)Math.cos(angle), r1 * (float)Math.sin(angle), -width * 0.5f); + gl.glVertex3f(r0 * (float)Math.cos(angle), r0 * (float)Math.sin(angle), -width * 0.5f); + gl.glVertex3f(r1 * (float)Math.cos(angle + 3 * da), r1 * (float)Math.sin(angle + 3 * da), -width * 0.5f); + gl.glVertex3f(r0 * (float)Math.cos(angle), r0 * (float)Math.sin(angle), -width * 0.5f); + } + gl.glEnd(); + + /* draw back sides of teeth */ + gl.glBegin(GL2.GL_QUADS); + for (i = 0; i < teeth; i++) + { + angle = i * 2.0f * (float) Math.PI / teeth; + gl.glVertex3f(r1 * (float)Math.cos(angle + 3 * da), r1 * (float)Math.sin(angle + 3 * da), -width * 0.5f); + gl.glVertex3f(r2 * (float)Math.cos(angle + 2 * da), r2 * (float)Math.sin(angle + 2 * da), -width * 0.5f); + gl.glVertex3f(r2 * (float)Math.cos(angle + da), r2 * (float)Math.sin(angle + da), -width * 0.5f); + gl.glVertex3f(r1 * (float)Math.cos(angle), r1 * (float)Math.sin(angle), -width * 0.5f); + } + gl.glEnd(); + + /* draw outward faces of teeth */ + gl.glBegin(GL2.GL_QUAD_STRIP); + for (i = 0; i < teeth; i++) + { + angle = i * 2.0f * (float) Math.PI / teeth; + gl.glVertex3f(r1 * (float)Math.cos(angle), r1 * (float)Math.sin(angle), width * 0.5f); + gl.glVertex3f(r1 * (float)Math.cos(angle), r1 * (float)Math.sin(angle), -width * 0.5f); + u = r2 * (float)Math.cos(angle + da) - r1 * (float)Math.cos(angle); + v = r2 * (float)Math.sin(angle + da) - r1 * (float)Math.sin(angle); + len = (float)Math.sqrt(u * u + v * v); + u /= len; + v /= len; + gl.glNormal3f(v, -u, 0.0f); + gl.glVertex3f(r2 * (float)Math.cos(angle + da), r2 * (float)Math.sin(angle + da), width * 0.5f); + gl.glVertex3f(r2 * (float)Math.cos(angle + da), r2 * (float)Math.sin(angle + da), -width * 0.5f); + gl.glNormal3f((float)Math.cos(angle), (float)Math.sin(angle), 0.0f); + gl.glVertex3f(r2 * (float)Math.cos(angle + 2 * da), r2 * (float)Math.sin(angle + 2 * da), width * 0.5f); + gl.glVertex3f(r2 * (float)Math.cos(angle + 2 * da), r2 * (float)Math.sin(angle + 2 * da), -width * 0.5f); + u = r1 * (float)Math.cos(angle + 3 * da) - r2 * (float)Math.cos(angle + 2 * da); + v = r1 * (float)Math.sin(angle + 3 * da) - r2 * (float)Math.sin(angle + 2 * da); + gl.glNormal3f(v, -u, 0.0f); + gl.glVertex3f(r1 * (float)Math.cos(angle + 3 * da), r1 * (float)Math.sin(angle + 3 * da), width * 0.5f); + gl.glVertex3f(r1 * (float)Math.cos(angle + 3 * da), r1 * (float)Math.sin(angle + 3 * da), -width * 0.5f); + gl.glNormal3f((float)Math.cos(angle), (float)Math.sin(angle), 0.0f); + } + gl.glVertex3f(r1 * (float)Math.cos(0), r1 * (float)Math.sin(0), width * 0.5f); + gl.glVertex3f(r1 * (float)Math.cos(0), r1 * (float)Math.sin(0), -width * 0.5f); + gl.glEnd(); + + gl.glShadeModel(GL2.GL_SMOOTH); + + /* draw inside radius cylinder */ + gl.glBegin(GL2.GL_QUAD_STRIP); + for (i = 0; i <= teeth; i++) + { + angle = i * 2.0f * (float) Math.PI / teeth; + gl.glNormal3f(-(float)Math.cos(angle), -(float)Math.sin(angle), 0.0f); + gl.glVertex3f(r0 * (float)Math.cos(angle), r0 * (float)Math.sin(angle), -width * 0.5f); + gl.glVertex3f(r0 * (float)Math.cos(angle), r0 * (float)Math.sin(angle), width * 0.5f); + } + gl.glEnd(); + } + + // Methods required for the implementation of MouseListener + public void mouseEntered(MouseEvent e) {} + public void mouseExited(MouseEvent e) {} + + public void mousePressed(MouseEvent e) { + prevMouseX = e.getX(); + prevMouseY = e.getY(); + if ((e.getModifiers() & e.BUTTON3_MASK) != 0) { + mouseRButtonDown = true; + } + } + + public void mouseReleased(MouseEvent e) { + if ((e.getModifiers() & e.BUTTON3_MASK) != 0) { + mouseRButtonDown = false; + } + } + + public void mouseClicked(MouseEvent e) {} + + // Methods required for the implementation of MouseMotionListener + public void mouseDragged(MouseEvent e) { + int x = e.getX(); + int y = e.getY(); + Dimension size = e.getComponent().getSize(); + + float thetaY = 360.0f * ( (float)(x-prevMouseX)/(float)size.width); + float thetaX = 360.0f * ( (float)(prevMouseY-y)/(float)size.height); + + prevMouseX = x; + prevMouseY = y; + + view_rotx += thetaX; + view_roty += thetaY; + } + + public void mouseMoved(MouseEvent e) {} +} diff --git a/src/cad/JOGL2NewtDemo.java b/src/cad/JOGL2NewtDemo.java new file mode 100644 index 00000000..2ddcd8b4 --- /dev/null +++ b/src/cad/JOGL2NewtDemo.java @@ -0,0 +1,60 @@ +package cad; + +import javax.media.opengl.GLCapabilities; +import javax.media.opengl.GLProfile; +import com.jogamp.newt.event.WindowAdapter; +import com.jogamp.newt.event.WindowEvent; +import com.jogamp.newt.opengl.GLWindow; +import com.jogamp.opengl.util.FPSAnimator; + +/** + * A program that draws with JOGL in a NEWT GLWindow. + * + */ +public class JOGL2NewtDemo { + private static String TITLE = "JOGL 2 with NEWT"; // window's title + private static final int WINDOW_WIDTH = 640; // width of the drawable + private static final int WINDOW_HEIGHT = 480; // height of the drawable + private static final int FPS = 60; // animator's target frames per second + + static { + GLProfile.initSingleton(); // The method allows JOGL to prepare some Linux-specific locking optimizations + } + + /** + * The entry main() method. + */ + public static void main(String[] args) { + // Get the default OpenGL profile, reflecting the best for your running platform + GLProfile glp = GLProfile.getDefault(); + // Specifies a set of OpenGL capabilities, based on your profile. + GLCapabilities caps = new GLCapabilities(glp); + // Create the OpenGL rendering canvas + GLWindow window = GLWindow.create(caps); + + // Create a animator that drives canvas' display() at the specified FPS. + final FPSAnimator animator = new FPSAnimator(window, FPS, true); + + window.addWindowListener(new WindowAdapter() { + @Override + public void windowDestroyNotify(WindowEvent arg0) { + // Use a dedicate thread to run the stop() to ensure that the + // animator stops before program exits. + new Thread() { + @Override + public void run() { + if (animator.isStarted()) + animator.stop(); // stop the animator loop + System.exit(0); + } + }.start(); + } + }); + + window.addGLEventListener(new JOGL2Renderer()); + window.setSize(WINDOW_WIDTH, WINDOW_HEIGHT); + window.setTitle(TITLE); + window.setVisible(true); + animator.start(); // start the animator loop + } +} \ No newline at end of file diff --git a/src/cad/JOGL2Renderer.java b/src/cad/JOGL2Renderer.java new file mode 100644 index 00000000..56f5eb07 --- /dev/null +++ b/src/cad/JOGL2Renderer.java @@ -0,0 +1,60 @@ +package cad; + +import javax.media.opengl.GL; +import javax.media.opengl.GL2; +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLEventListener; + +/** + * Class handles the OpenGL events to render graphics. + * + */ +public class JOGL2Renderer implements GLEventListener { + private double theta = 0.0f; // rotational angle + + @Override + public void init(GLAutoDrawable drawable) { + } + + @Override + public void dispose(GLAutoDrawable drawable) { + } + + /** + * Called back by the drawable to render OpenGL graphics + */ + @Override + public void display(GLAutoDrawable drawable) { + GL2 gl = drawable.getGL().getGL2(); // get the OpenGL graphics context + + gl.glClear(GL.GL_COLOR_BUFFER_BIT); // clear background + gl.glLoadIdentity(); // reset the model-view matrix + + // Rendering code - draw a triangle + float sine = (float)Math.sin(theta); + float cosine = (float)Math.cos(theta); + gl.glBegin(GL.GL_TRIANGLES); + gl.glColor3f(1, 0, 0); + gl.glVertex2d(-cosine, -cosine); + gl.glColor3f(0, 1, 0); + gl.glVertex2d(0, cosine); + gl.glColor3f(0, 0, 1); + gl.glVertex2d(sine, -sine); + gl.glEnd(); + + update(); + } + + @Override + public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { + } + + /** + * Update the rotation angle after each frame refresh + */ + private void update() { + theta += 0.01; + } + + /*... Other methods leave blank ...*/ +} \ No newline at end of file diff --git a/src/cad/fx/App.java b/src/cad/fx/App.java new file mode 100644 index 00000000..e9827077 --- /dev/null +++ b/src/cad/fx/App.java @@ -0,0 +1,22 @@ +package cad.fx; + +import javafx.application.Application; +import javafx.fxml.FXMLLoader; +import javafx.scene.Scene; +import javafx.stage.Stage; + +public class App extends Application { + + public static void main(String[] args) { + System.setProperty("prism.dirtyopts", "false"); + launch(args); + } + + @Override + public void start(Stage primaryStage) throws Exception { + Scene scene = new Scene(FXMLLoader.load(AppCtrl.class.getResource("app.fxml")), 1024, 1100); + primaryStage.setTitle("Solid CAD"); + primaryStage.setScene(scene); + primaryStage.show(); + } +} diff --git a/src/cad/fx/AppCtrl.java b/src/cad/fx/AppCtrl.java new file mode 100644 index 00000000..fa708921 --- /dev/null +++ b/src/cad/fx/AppCtrl.java @@ -0,0 +1,49 @@ +package cad.fx; + +import cad.fx.viewer.Viewer3D; +import javafx.fxml.Initializable; +import javafx.scene.Group; +import javafx.scene.Node; +import javafx.scene.control.Button; + +import java.net.URL; +import java.util.ResourceBundle; + +import static java.util.Collections.singletonList; + +public class AppCtrl implements Initializable { + + private final CadContext cadContext = new CadContext(); + + public Viewer3D viewer; + public Button beginSketching; + public Button endSketching; + public Button pad; + public Button cut; + + @Override + public void initialize(URL location, ResourceBundle resources) { + Group content = new Group(getInitObject()); + viewer.setContent(content); + beginSketching.setOnAction(event -> { + cadContext.beginSketching(); + }); + endSketching.setOnAction(event -> { + cadContext.endSketching(); + }); + pad.setOnAction(event -> { + cadContext.pad(50); + }); + } + + private Node getInitObject() { + + Surface square = Utils3D.createSquare(100); +// square = square.flip(); + return new CSGNode(Utils3D.getMesh(singletonList(square)), cadContext); + +// +// CSG init = new Cube(100).toCSG().difference(new Cylinder(30, 100, 10).toCSG()); +// return new CSGNode(Utils3D.getFXMesh(init), cadContext); + } +} diff --git a/src/cad/fx/CSGMesh.java b/src/cad/fx/CSGMesh.java new file mode 100644 index 00000000..6180fc7e --- /dev/null +++ b/src/cad/fx/CSGMesh.java @@ -0,0 +1,12 @@ +package cad.fx; + +import eu.mihosoft.vrl.v3d.Polygon; +import gnu.trove.map.TIntObjectMap; +import gnu.trove.map.hash.TIntObjectHashMap; +import javafx.scene.shape.TriangleMesh; + +public class CSGMesh extends TriangleMesh { + + public final TIntObjectMap polygons = new TIntObjectHashMap<>(); + +} diff --git a/src/cad/fx/CSGNode.java b/src/cad/fx/CSGNode.java new file mode 100644 index 00000000..4885916e --- /dev/null +++ b/src/cad/fx/CSGNode.java @@ -0,0 +1,41 @@ +package cad.fx; + +import eu.mihosoft.vrl.v3d.CSG; +import eu.mihosoft.vrl.v3d.Polygon; +import javafx.scene.Group; +import javafx.scene.Parent; +import javafx.scene.shape.MeshView; + +import java.util.HashMap; +import java.util.Map; + +public class CSGNode extends MeshView { + + private final CadContext context; + + public CSGNode(CSGMesh mesh, CadContext context) { + super(mesh); + this.context = context; + setMaterial(Utils3D.DEFAULT_MATERIAL); + setOnMouseClicked(e -> { + context.clickOnNode(this, e); + }); + } + + private void highlight(Polygon poly) { + System.out.println(poly); + } + + public final Map sketches = new HashMap<>(); + + public Sketch getSketch(Surface surface) { + Sketch sketch = sketches.get(surface); + if (sketch == null) { + sketch = new Sketch(surface); + ((Group) getParent()).getChildren().add(sketch.drawLayer); + sketches.put(surface, sketch); + } + return sketch; + } + +} diff --git a/src/cad/fx/CadContext.java b/src/cad/fx/CadContext.java new file mode 100644 index 00000000..5d26bafc --- /dev/null +++ b/src/cad/fx/CadContext.java @@ -0,0 +1,85 @@ +package cad.fx; + +import cad.math.Vector; +import javafx.scene.input.MouseEvent; +import javafx.scene.input.PickResult; + +import java.util.List; + +public class CadContext { + + public Sketcher sketcher; + public Selection selection; + + public void clickOnNode(CSGNode csgNode, MouseEvent e) { + PickResult pickResult = e.getPickResult(); + int face = pickResult.getIntersectedFace(); + CSGMesh csgMesh = (CSGMesh) csgNode.getMesh(); + Surface surface = csgMesh.polygons.get(face); + System.out.println(surface); + if (surface != null) { + if (selection != null) { + boolean isSameNode = selection.sameTo(csgNode, surface); + if (sketcher == null && !isSameNode) { + selection = new Selection(csgNode, surface); + } + if (sketcher != null && isSameNode) { + sketcher.addPoint(pickResult.getIntersectedPoint()); + } + } else { + if (sketcher == null) { + selection = new Selection(csgNode, surface); + } + } + } + } + + public void beginSketching() { + if (sketcher != null || selection == null) { + return; + } + sketcher = new Sketcher(selection.csgNode.getSketch(selection.surface)); + } + + public void endSketching() { + if (sketcher == null) { + return; + } + sketcher.commitOperation(); + sketcher = null; + } + + public void pad(double height) { + if (selection == null) { + return; + } + + Sketch sketch = selection.csgNode.getSketch(selection.surface); + for (List polygon : sketch.polygons) { + if (polygon.isEmpty()) { + continue; + } + Vector dir = sketch.owner.normal.scale(height); + + List extruded = Surface.extrude(sketch.owner, dir); + +// CSG pad = Extrude.points(dir, polygon); + sketch.drawLayer.getChildren().addAll(new CSGNode(Utils3D.getMesh(extruded), this)); // fixme + } + } + + public static class Selection { + + public final CSGNode csgNode; + public final Surface surface; + + public Selection(CSGNode csgNode, Surface surface) { + this.csgNode = csgNode; + this.surface = surface; + } + + public boolean sameTo(CSGNode csgNode, Surface surface) { + return this.csgNode.equals(csgNode) && this.surface.equals(surface); + } + } +} diff --git a/src/cad/fx/Sketch.java b/src/cad/fx/Sketch.java new file mode 100644 index 00000000..a0eba62a --- /dev/null +++ b/src/cad/fx/Sketch.java @@ -0,0 +1,18 @@ +package cad.fx; + +import cad.math.Vector; +import javafx.scene.Group; + +import java.util.ArrayList; +import java.util.List; + +public class Sketch { + + public final Surface owner; + public final List> polygons = new ArrayList<>(); + public final Group drawLayer = new Group(); + + public Sketch(Surface owner) { + this.owner = owner; + } +} diff --git a/src/cad/fx/Sketcher.java b/src/cad/fx/Sketcher.java new file mode 100644 index 00000000..a5d21ab6 --- /dev/null +++ b/src/cad/fx/Sketcher.java @@ -0,0 +1,33 @@ +package cad.fx; + +import cad.math.Vector; +import eu.mihosoft.vrl.v3d.Vector3d; +import javafx.geometry.Point3D; +import javafx.scene.shape.Sphere; + +import java.util.ArrayList; + +public class Sketcher { + + public final Sketch sketch; + + public Sketcher(Sketch sketch) { + this.sketch = sketch; + if (sketch.polygons.isEmpty()) { + sketch.polygons.add(new ArrayList<>()); + } + } + + public void addPoint(Point3D point) { + sketch.polygons.get(sketch.polygons.size() - 1).add(new Vector(point.getX(), point.getY(), point.getZ())); + Sphere pt = new Sphere(1); + pt.setTranslateX(point.getX()); + pt.setTranslateY(point.getY()); + pt.setTranslateZ(point.getZ()); + sketch.drawLayer.getChildren().addAll(pt); + } + + public void commitOperation() { + sketch.polygons.add(new ArrayList<>()); + } +} diff --git a/src/cad/fx/Surface.java b/src/cad/fx/Surface.java new file mode 100644 index 00000000..7ca13646 --- /dev/null +++ b/src/cad/fx/Surface.java @@ -0,0 +1,172 @@ +package cad.fx; + +import cad.math.HMath; +import cad.math.Matrix; +import cad.math.Vector; +import org.poly2tri.Poly2Tri; +import org.poly2tri.geometry.polygon.Polygon; +import org.poly2tri.geometry.polygon.PolygonPoint; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +import static java.util.stream.Collectors.toList; + +public class Surface { + + public final Vector normal; + public final List shell; + public final List> holes; + + private List triangles; + + public Surface(List shell) { + this(shell, Collections.emptyList()); + } + + public Surface(List shell, List> holes) { + this(normalOfCCWSeq(shell.get(0), shell.get(1), shell.get(2)), shell, holes); + } + + public Surface(Vector normal, List shell, List> holes) { + this.normal = normal.normalize(); + this.shell = shell; + this.holes = holes; + checkPolygon(shell); + for (List hole : holes) { + if (hole.size() < 3) { + checkPolygon(hole); + } + } + } + + public Surface fixCCW() { + if (!normal.slightlyEqualTo(normalOfCCWSeq(shell.get(0), shell.get(1), shell.get(2)))) { + List shell = new ArrayList<>(this.shell); + Collections.reverse(shell); + return new Surface(normal, shell, holes); + } + return this; + } + + + public Vector[] someBasis() { + Vector x = shell.get(1).minus(shell.get(0)).normalize(); + Vector y = normal.cross(x).normalize(); + return new Vector[] {x, y, normal}; + } + + private void checkPolygon(List shell) { + if (shell.size() < 3) { + throw new IllegalArgumentException("Polygon should contain at least 3 point"); + } + } + + public List getTriangles() { + if (triangles == null) { + triangulate(); + } + return triangles; + } + + private void triangulate() { + + Matrix _3dTransformation = new Matrix(someBasis()); + Matrix _2dTransformation = _3dTransformation.invert(); + + List shellPoints = shell.stream() + .map(vector -> HMath.cross(_2dTransformation, vector)) + .map(vector -> new PolygonPoint(vector.x, vector.y, vector.z)) + .collect(toList()); + + Polygon polygon = new Polygon(shellPoints); + + for (List hole : holes) { + + List holePoints = hole.stream() + .map(vector -> HMath.cross(_2dTransformation, vector)) + .map(vector -> new PolygonPoint(vector.x, vector.y, vector.z)) + .collect(toList()); + + polygon.addHole(new Polygon(holePoints)); + } + + Poly2Tri.triangulate(polygon); + + triangles = polygon.getTriangles().stream() + .map(tr -> + new Vector[]{ + HMath.cross(_3dTransformation, new Vector(tr.points[0].getX(), tr.points[0].getY(), tr.points[0].getZ())), + HMath.cross(_3dTransformation, new Vector(tr.points[1].getX(), tr.points[1].getY(), tr.points[1].getZ())), + HMath.cross(_3dTransformation, new Vector(tr.points[2].getX(), tr.points[2].getY(), tr.points[2].getZ())) + }) + .collect(Collectors.toList()); + + setupNormal(triangles, normal); + } + + public static void setupNormal(List triangles, Vector normal) { + + for (Vector[] triangle : triangles) { + if (!normalOfCCWSeq(triangle[0], triangle[1], triangle[2]).slightlyEqualTo(normal)) { + reverse(triangle); + System.out.println(""); + } + } + } + + public static Vector normalOfCCWSeq(Vector v0, Vector v1, Vector v2) { + return v1.minus(v0).cross(v2.minus(v0)).normalize(); + } + + private static void reverse(Vector[] triangle) { + Vector first = triangle[0]; + triangle[0] = triangle[2]; + triangle[2] = first; + } + + public Surface flip() { + return new Surface(normal.negate(), shell, holes); + } + + public static List extrude(Surface source, Vector target) { + + double dotProduct = target.normalize().dot(source.normal); + if (dotProduct == 0) { + return Collections.emptyList(); + } + if (dotProduct > 0) { + source = source.flip(); + } + source = source.fixCCW(); + + List surfaces = new ArrayList<>(); + surfaces.add(source); + + Surface lid = source.shift(target).flip(); + surfaces.add(lid); + + for (int i = 1; i < source.shell.size(); i++) { + Surface face = new Surface(Arrays.asList( + source.shell.get(i - 1), + source.shell.get(i), + lid.shell.get(i), + lid.shell.get(i - 1) + )); + surfaces.add(face); + } + return surfaces; + } + + public Surface shift(Vector target) { + List shell = this.shell.stream().map(vector -> vector.plus(target)).collect(toList()); + List> holes = new ArrayList<>(); + for (List hole : this.holes) { + holes.add(hole.stream().map(vector -> vector.plus(target)).collect(toList())); + } + return new Surface(normal, shell, holes); + } +} diff --git a/src/cad/fx/Test.java b/src/cad/fx/Test.java new file mode 100644 index 00000000..fc68a3e9 --- /dev/null +++ b/src/cad/fx/Test.java @@ -0,0 +1,82 @@ +package cad.fx; + +import javafx.application.Application; +import javafx.fxml.FXMLLoader; +import javafx.scene.Scene; +import javafx.scene.canvas.Canvas; +import javafx.scene.control.ScrollPane; +import javafx.scene.layout.Pane; +import javafx.scene.shape.Line; +import javafx.scene.transform.Translate; +import javafx.stage.Stage; +import org.poly2tri.Poly2Tri; +import org.poly2tri.geometry.polygon.Polygon; +import org.poly2tri.geometry.polygon.PolygonPoint; +import org.poly2tri.triangulation.TriangulationPoint; +import org.poly2tri.triangulation.delaunay.DelaunayTriangle; + +import java.util.Arrays; +import java.util.List; + +public class Test extends Application { + + public static void main(String[] args) { + System.setProperty("prism.dirtyopts", "false"); + launch(args); + } + + @Override + public void start(Stage primaryStage) throws Exception { + Pane pane = new Pane(); + Scene scene = new Scene(pane, 600, 600); + + + + + Polygon polygon = new Polygon(Arrays.asList( + new PolygonPoint(0, 0), +// new PolygonPoint(200, 100), + new PolygonPoint(400, 0), + new PolygonPoint(400, 400), + new PolygonPoint(0, 400) + )); + + Polygon hole = new Polygon(Arrays.asList( + new PolygonPoint(50, 50), + new PolygonPoint(50, 100), + new PolygonPoint(100, 100), + new PolygonPoint(100, 50) + )); + + polygon.addHole(hole); + + Poly2Tri.triangulate(polygon); + + + for (DelaunayTriangle triangle : polygon.getTriangles()) { + show(pane, Arrays.asList(triangle.points)); + } + + pane.getTransforms().add(new Translate(10, 10)); + show(pane, polygon); + + primaryStage.setScene(scene); + primaryStage.show(); + } + + private void show(Pane pane, Polygon polygon) { + show(pane, polygon.getPoints()); + } + + private void show(Pane pane, List points) { + + TriangulationPoint first = points.get(0); + TriangulationPoint prev = first; + for (TriangulationPoint point : points.subList(1, points.size())) { + pane.getChildren().addAll(new Line(prev.getX(), prev.getY(), point.getX(), point.getY())); + prev = point; + } + pane.getChildren().addAll(new Line(prev.getX(), prev.getY(), first.getX(), first.getY())); + } + +} diff --git a/src/cad/fx/Utils3D.java b/src/cad/fx/Utils3D.java new file mode 100644 index 00000000..281fe586 --- /dev/null +++ b/src/cad/fx/Utils3D.java @@ -0,0 +1,174 @@ +package cad.fx; + +import cad.math.Vector; +import eu.mihosoft.vrl.v3d.CSG; +import eu.mihosoft.vrl.v3d.MeshContainer; +import eu.mihosoft.vrl.v3d.Polygon; +import eu.mihosoft.vrl.v3d.Vector3d; +import eu.mihosoft.vrl.v3d.Vertex; +import eu.mihosoft.vrl.v3d.ext.org.poly2tri.PolygonUtil; +import javafx.scene.image.Image; +import javafx.scene.paint.Color; +import javafx.scene.paint.PhongMaterial; +import javafx.scene.shape.TriangleMesh; +import org.poly2tri.Poly2Tri; +import org.poly2tri.geometry.polygon.PolygonPoint; +import org.poly2tri.triangulation.TriangulationPoint; +import org.poly2tri.triangulation.delaunay.DelaunayTriangle; + +import java.util.Arrays; +import java.util.List; + +public class Utils3D { + + public static final PhongMaterial DEFAULT_MATERIAL = new PhongMaterial(); + + static { +// DEFAULT_MATERIAL.setDiffuseColor(Color.LIGHTBLUE); +// DEFAULT_MATERIAL.setSpecularColor(Color.WHITE); + + DEFAULT_MATERIAL.setDiffuseColor(Color.LIGHTSTEELBLUE); + DEFAULT_MATERIAL.setSpecularColor(Color.LIGHTBLUE); + +// DEFAULT_MATERIAL.setDiffuseMap(new Image(Utils3D.class.getResource("tex.png").toExternalForm())); + } + + public static CSGMesh getMesh(List surfaces) { + + CSGMesh mesh = new CSGMesh(); + + int faceCounter = 0; + + for (Surface surface : surfaces) { + + + for (Vector[] triangle : surface.getTriangles()) { + + + mesh.getPoints().addAll( + (float) triangle[0].x, + (float) triangle[0].y, + (float) triangle[0].z + ); + + mesh.getTexCoords().addAll(0); // texture (not covered) + mesh.getTexCoords().addAll(0); + + + mesh.getPoints().addAll( + (float) triangle[1].x, + (float) triangle[1].y, + (float) triangle[1].z + ); + + mesh.getTexCoords().addAll(0); // texture (not covered) + mesh.getTexCoords().addAll(0); + + mesh.getPoints().addAll( + (float) triangle[2].x, + (float) triangle[2].y, + (float) triangle[2].z + ); + + + mesh.getTexCoords().addAll(0); // texture (not covered) + mesh.getTexCoords().addAll(0); + + int counter = faceCounter * 3; + mesh.getFaces().addAll( + counter, // first vertex + 0, // texture (not covered) + counter + 1, // second vertex + 0, // texture (not covered) + counter + 2, // third vertex + 0 // texture (not covered) + ); + mesh.polygons.put(faceCounter, surface); + ++faceCounter; + + } // end if #verts >= 3 + + } // end for polygon + + return mesh; + } + + public static CSGMesh getFXMesh(List triangles) { + + CSGMesh mesh = new CSGMesh(); + + int faceCounter = 0; + + for (DelaunayTriangle p : triangles) { + + + TriangulationPoint firstVertex = p.points[0]; + + mesh.getPoints().addAll( + p.points[2].getXf(), + p.points[2].getYf(), + p.points[2].getZf() + ); + + mesh.getTexCoords().addAll(0); // texture (not covered) + mesh.getTexCoords().addAll(0); + + mesh.getPoints().addAll( + p.points[1].getXf(), + p.points[1].getYf(), + p.points[1].getZf() + ); + + mesh.getTexCoords().addAll(0); // texture (not covered) + mesh.getTexCoords().addAll(0); + + mesh.getPoints().addAll( + p.points[0].getXf(), + p.points[0].getYf(), + p.points[0].getZf() + ); + + mesh.getTexCoords().addAll(0); // texture (not covered) + mesh.getTexCoords().addAll(0); + + int counter = faceCounter * 3; + mesh.getFaces().addAll( + counter, // first vertex + 0, // texture (not covered) + counter + 1, // second vertex + 0, // texture (not covered) + counter + 2, // third vertex + 0 // texture (not covered) + ); +// mesh.polygons.put(faceCounter, p); + ++faceCounter; + + } // end for polygon + + return mesh; + } + + public static Surface createSquare(double width) { + + width /= 2; + + List shell = Arrays.asList( + new Vector(-width, -width), + new Vector(width, -width), + new Vector(width, width, 0), + new Vector(-width, width, 0) + ); + +// width /= 3; +// org.poly2tri.geometry.polygon.Polygon hole = new org.poly2tri.geometry.polygon.Polygon(Arrays.asList( +// new PolygonPoint(-width, -width), +// new PolygonPoint(width, -width), +// new PolygonPoint(width, width, 100), +// new PolygonPoint(-width, width, 100) +// )); +// +// polygon.addHole(hole); + + return new Surface(shell); + } +} diff --git a/src/cad/fx/app.fxml b/src/cad/fx/app.fxml new file mode 100644 index 00000000..b79c912c --- /dev/null +++ b/src/cad/fx/app.fxml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + +