Package org.nlogo.gl.render

Source Code of org.nlogo.gl.render.ShapeManager

// (C) Uri Wilensky. https://github.com/NetLogo/NetLogo

package org.nlogo.gl.render;

import org.nlogo.api.Shape;
import org.nlogo.api.ShapeList;
import org.nlogo.shape.InvalidShapeDescriptionException;
import org.nlogo.shape.LinkShape;
import org.nlogo.shape.VectorShape;

import javax.media.opengl.GL;
import javax.media.opengl.glu.GLU;
import javax.media.opengl.glu.GLUquadric;
import javax.media.opengl.glu.GLUtessellator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

// the use of "@@@" in here is ugly. maybe put the patch shape and
// wireframe code in a diff. class? - ST 3/3/05

class ShapeManager {
  static final int SMOOTHNESS = 16;

  private final GLUquadric quadric;
  private int lastList;
  private final Map<String, GLShape> shapes = new HashMap<String, GLShape>();
  private final List<AddShapeRequest> queue = new LinkedList<AddShapeRequest>();
  private final Map<String, String> shapeMap = new HashMap<String, String>();
  final Map<String, List<String>> customShapes = new HashMap<String, List<String>>();
  private final ShapeList turtleShapeList;
  private final ShapeList linkShapeList;
  private final Map<String, GLShape> modelShapes = new HashMap<String, GLShape>();
  private final Map<String, GLLinkShape> linkShapes = new HashMap<String, GLLinkShape>();
  private final Tessellator tessellator = new Tessellator();
  private final GLUtessellator tess;
  private final boolean is3D;

  ShapeManager(GL gl, GLU glu, ShapeList turtleShapeList, ShapeList linkShapeList,
               Map<String, List<String>> customShapes, boolean is3D)

  {
    this.is3D = is3D;
    // tessellation for concave polygons in model shapes
    tess = glu.gluNewTess();
    glu.gluTessCallback(tess, GLU.GLU_TESS_BEGIN_DATA, tessellator);
    glu.gluTessCallback(tess, GLU.GLU_TESS_EDGE_FLAG_DATA, tessellator);
    glu.gluTessCallback(tess, GLU.GLU_TESS_VERTEX_DATA, tessellator);
    glu.gluTessCallback(tess, GLU.GLU_TESS_END_DATA, tessellator);
    glu.gluTessCallback(tess, GLU.GLU_TESS_COMBINE_DATA, tessellator);
    glu.gluTessCallback(tess, GLU.GLU_TESS_ERROR_DATA, tessellator);
    glu.gluTessProperty
        (tess, GLU.GLU_TESS_WINDING_RULE, GLU.GLU_TESS_WINDING_ODD);

    quadric = glu.gluNewQuadric();
    glu.gluQuadricNormals(quadric, GLU.GLU_SMOOTH);
    lastList = new Builtins(gl, glu, quadric).add(shapes, shapeMap);
    this.turtleShapeList = turtleShapeList;
    this.linkShapeList = linkShapeList;
    addModelShapes(gl, glu);
    addLinkShapes(gl, glu);
    if (customShapes != null) {
      lastList = CustomShapes.updateShapes(gl, lastList, shapes, customShapes);
    }
  }

  GLShape getShape(String name) {
    if (shapes.containsKey(name)) {
      return shapes.get(name);
    } else if (shapeMap.containsKey(name)) {
      return shapes.get(shapeMap.get(name));
    } else if (modelShapes.containsKey(name)) {
      return modelShapes.get(name);
    } else {
      return shapes.get("default");
    }
  }

  GLLinkShape getLinkShape(String name) {
    return linkShapes.get(name);
  }

  // a model library shape that doesn't have a built-in 3D shape
  boolean modelLibraryShape(String name) {
    return modelShapes.containsKey(name)
        && (!shapes.containsKey(name))
        && (!shapeMap.containsKey(name));
  }

  private static enum AddShapeRequestType {IMPORT, LIBRARY_TURTLE, LIBRARY_LINK}

  private class AddShapeRequest {
    final AddShapeRequestType type;
    final Object data;

    AddShapeRequest(AddShapeRequestType type, Object data) {
      this.type = type;
      this.data = data;
    }
  }

  public void invalidateTurtleShape(String shape) {
    queue.add(new AddShapeRequest(AddShapeRequestType.LIBRARY_TURTLE, shape));
  }

  public void invalidateLinkShape(String shape) {
    queue.add(new AddShapeRequest(AddShapeRequestType.LIBRARY_LINK, shape));
  }

  public void addCustomShapes(String filename)
      throws java.io.IOException,
      InvalidShapeDescriptionException {
    for(CustomShapes.Description shape : CustomShapes.readShapes(filename)) {
      queue.add(new AddShapeRequest(AddShapeRequestType.IMPORT, shape));
    }
  }

  // Need to do it this way because we need the GL
  void checkQueue(GL gl, GLU glu) {
    for (AddShapeRequest req : queue) {
      if (req.type == AddShapeRequestType.IMPORT) {
        CustomShapes.Description shape = (CustomShapes.Description) req.data;
        lastList = CustomShapes.addNewShape(gl, shapes, shape);
        customShapes.put(shape.name(), shape.javaLines());
      } else if (req.type == AddShapeRequestType.LIBRARY_TURTLE) {
        List<Shape> modelShapeList = turtleShapeList.getShapes();

        if (modelShapes.containsKey(req.data)) {
          GLShape shape = modelShapes.get(req.data);
          gl.glDeleteLists(shape.displayListIndex(), 1);
          modelShapes.remove(req.data);
        }

        lastList = gl.glGenLists(1);

        // locate new/changed shape in model library
        for (int j = 0; j < modelShapeList.size(); j++) {
          VectorShape vShape =
              (VectorShape) modelShapeList.get(j);
          if (vShape.getName().equals(req.data)) {
            // compile shape
            addModelShape(gl, glu, vShape, lastList);
            break;
          }
        }
      } else if (req.type == AddShapeRequestType.LIBRARY_LINK) {
        if (modelShapes.containsKey(req.data)) {
          GLLinkShape shape = linkShapes.get(req.data);
          gl.glDeleteLists(shape.directionIndicator().displayListIndex(), 1);
          linkShapes.remove(req.data);
        }
        addLinkShape(gl, glu,
            (org.nlogo.shape.LinkShape) linkShapeList.shape((String) req.data),
            gl.glGenLists(1));
      } else {
        throw new IllegalStateException();
      }
    }
    queue.clear();
  }

  private void addLinkShapes(GL gl, GLU glu) {
    List<Shape> lols = linkShapeList.getShapes();
    int nextIndex = gl.glGenLists(lols.size());
    Iterator<Shape> iter = lols.iterator();
    for (int i = nextIndex; iter.hasNext(); i++) {
      addLinkShape(gl, glu, (LinkShape) iter.next(), i);
    }
  }

  private void addLinkShape(GL gl, GLU glu, LinkShape shape, int index) {
    VectorShape vShape = (VectorShape) shape.getDirectionIndicator();
    GLLinkShape gShape =
      is3D
      ? new GLLinkShape3D(shape, new GLShape(vShape.getName(), index, vShape.isRotatable()))
      : new GLLinkShape  (shape, new GLShape(vShape.getName(), index, vShape.isRotatable()));
    linkShapes.put(shape.getName(), gShape);
    compileShape(gl, glu, vShape, index, vShape.isRotatable());
  }

  private void addModelShapes(GL gl, GLU glu) {
    List<Shape> modelShapeList = turtleShapeList.getShapes();
    lastList = gl.glGenLists(modelShapeList.size());

    // compile each shape in the model
    for (int i = 0; i < modelShapeList.size(); i++) {
      VectorShape vShape =
          (VectorShape) modelShapeList.get(i);
      addModelShape(gl, glu, vShape, (lastList + i));
    }
  }

  private void addModelShape(GL gl, GLU glu,
                             VectorShape vShape,
                             int index) {
    boolean rotatable = vShape.isRotatable();
    modelShapes.put(vShape.getName(),
        new GLShape(vShape.getName(), index, rotatable));

    compileShape(gl, glu, vShape, index, rotatable);
  }

  void compileShape(GL gl, GLU glu,
                    VectorShape vShape,
                    int index, boolean rotatable) {
    gl.glNewList(index, GL.GL_COMPILE);

    if (!rotatable) {
      gl.glDisable(GL.GL_LIGHTING);
    }

    // render each element in this shape
    List<org.nlogo.shape.Element> elements = vShape.getElements();
    for (int i = 0; i < elements.size(); i++) {
      org.nlogo.shape.Element element = elements.get(i);

      if (element instanceof org.nlogo.shape.Rectangle) {
        renderRectangle
            (gl, i,
                (org.nlogo.shape.Rectangle) element, rotatable);
      } else if (element instanceof org.nlogo.shape.Polygon) {
        Polygons.renderPolygon(gl, glu, tessellator, tess, i,
          (org.nlogo.shape.Polygon) element, rotatable, is3D);
      } else if (element instanceof org.nlogo.shape.Circle) {
        renderCircle(gl, glu, i,
            (org.nlogo.shape.Circle) element, rotatable);
      } else if (element instanceof org.nlogo.shape.Line) {
        renderLine(gl, i,
            (org.nlogo.shape.Line) element);
      } else if (element instanceof org.nlogo.shape.Curve) {
        throw new IllegalStateException();
      }
    }

    if (!rotatable) {
      gl.glEnable(GL.GL_LIGHTING);
    }

    gl.glEndList();
  }

  private void renderRectangle(GL gl, int offset,
                               org.nlogo.shape.Rectangle rect,
                               boolean rotatable) {
    float zDepth = 0.01f + offset * 0.0001f;

    if (!rect.marked()) {
      float[] rgb = rect.getColor().getRGBColorComponents(null);
      gl.glPushAttrib(GL.GL_CURRENT_BIT);
      gl.glColor3fv(java.nio.FloatBuffer.wrap(rgb));
    }

    java.awt.Point[] corners = rect.getCorners();

    float coords[] =
        {corners[0].x * .001f - 0.15f,
            (300 - corners[0].y) * .001f - 0.15f,
            corners[1].x * .001f - 0.15f,
            (300 - corners[1].y) * .001f - 0.15f};

    // no need to "pancake" if it is always facing the user
    renderRectangle
        (gl, coords[0], coords[2], coords[3], coords[1],
            -zDepth, zDepth, !rect.filled(), rotatable);

    if (!rect.marked()) {
      gl.glPopAttrib();
    }
  }

  void renderRectangle(GL gl, float x0, float x1, float y0, float y1,
                       float z0, float z1, boolean filled, boolean rotatable) {
    Rectangle.renderRectangularPrism(gl, x0, x1, y0, y1,
        z0, z1, filled, is3D && rotatable, rotatable);
  }

  private void renderCircle(GL gl, GLU glu, int offset,
                            org.nlogo.shape.Circle circle,
                            boolean rotatable) {
    float zDepth = 0.01f + offset * 0.0001f;

    if (!circle.marked()) {
      float[] rgb = circle.getColor().getRGBColorComponents(null);
      gl.glPushAttrib(GL.GL_CURRENT_BIT);
      gl.glColor3fv(java.nio.FloatBuffer.wrap(rgb));
    }

    // for now assume it is a circle

    float radius = (float) circle.getBounds().getWidth() * .0005f;
    float origin[] =
        {(float) circle.getOrigin().getX() * .001f - 0.15f,
            (300 - (float) circle.getOrigin().getY()) * .001f - 0.15f};

    gl.glPushMatrix();

    if (!circle.filled()) {
      glu.gluQuadricDrawStyle(quadric, GLU.GLU_SILHOUETTE);
    }

    // no need to "pancake" if it is always facing the user
    if (rotatable) {
      if (!circle.filled()) {
        glu.gluQuadricDrawStyle(quadric, GLU.GLU_FILL);
        gl.glDisable(GL.GL_CULL_FACE);
      }

      gl.glTranslatef(origin[0], origin[1], -zDepth);
      glu.gluCylinder(quadric, radius, radius, (2 * zDepth),
          SMOOTHNESS, 1);

      if (!circle.filled()) {
        gl.glEnable(GL.GL_CULL_FACE);
        glu.gluQuadricDrawStyle(quadric, GLU.GLU_SILHOUETTE);
      }

      gl.glTranslatef(0.0f, 0.0f, (2 * zDepth));
    } else {
      gl.glTranslatef(origin[0], origin[1], zDepth);
    }

    renderCircle(gl, glu, 0.0f, radius, zDepth, rotatable);

    if (!circle.filled()) {
      glu.gluQuadricDrawStyle(quadric, GLU.GLU_FILL);
    }

    gl.glPopMatrix();

    if (!circle.marked()) {
      gl.glPopAttrib();
    }
  }

  private void renderCircle(GL gl, GLU glu,
                            float innerRadius, float outerRadius, float zDepth,
                            boolean rotatable) {
    glu.gluDisk(quadric, innerRadius, outerRadius, SMOOTHNESS, 1);
    if(is3D && rotatable) {
      gl.glRotatef(180f, 1f, 0f, 0f);
      gl.glTranslatef(0f, 0f, zDepth * 2);
      glu.gluDisk(quadric, innerRadius, outerRadius, SMOOTHNESS, 1);
    }
  }

  private void renderLine(GL gl, int offset, org.nlogo.shape.Line line) {
    float zDepth = offset * 0.0001f;

    if (!line.marked()) {
      float[] rgb = line.getColor().getRGBColorComponents(null);
      gl.glPushAttrib(GL.GL_CURRENT_BIT);
      gl.glColor3fv(java.nio.FloatBuffer.wrap(rgb));
    }

    java.awt.Point start = line.getStart();
    java.awt.Point end = line.getEnd();
    float coords[] =
        {start.x * .001f - 0.15f, (300 - start.y) * .001f - 0.15f,
            end.x * .001f - 0.15f, (300 - end.y) * .001f - 0.15f};

    gl.glBegin(GL.GL_LINES);
    gl.glNormal3f(0.0f, 0.0f, -1.0f);

    // top line
    gl.glVertex3f(coords[0], coords[1], zDepth);
    gl.glVertex3f(coords[2], coords[3], zDepth);
    gl.glEnd();

    // we don't "pancake" line because we don't really need to,
    // by default the line tilts to always show the user
    // a flat face, and this way we can scale all lines
    // by line thickness and it will look good and it will be fast. ev 4/5/06

    if (!line.marked()) {
      gl.glPopAttrib();
    }
  }

}
TOP

Related Classes of org.nlogo.gl.render.ShapeManager

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.