Package artofillusion.tools

Source Code of artofillusion.tools.ExtrudeDialog$EdgeInfo

/* Copyright (C) 2001-2005 by Peter Eastman

   This program is free software; you can redistribute it and/or modify it under the
   terms of the GNU General Public License as published by the Free Software
   Foundation; either version 2 of the License, or (at your option) any later version.

   This program is distributed in the hope that it will be useful, but WITHOUT ANY
   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
   PARTICULAR PURPOSE.  See the GNU General Public License for more details. */

package artofillusion.tools;

import artofillusion.*;
import artofillusion.math.*;
import artofillusion.object.*;
import artofillusion.object.TriangleMesh.*;
import artofillusion.texture.*;
import artofillusion.ui.*;
import buoy.event.*;
import buoy.widget.*;
import java.awt.*;
import java.util.*;

/** This dialog box allows the user to specify options for creating extruded objects. */

public class ExtrudeDialog extends BDialog
{
  private LayoutWindow window;
  private BComboBox objChoice, pathChoice;
  private RadioButtonGroup pathGroup;
  private BRadioButton pathBox, xBox, yBox, zBox, vectorBox;
  private BCheckBox orientBox;
  private ValueField distField, xField, yField, zField, segField, angleField, tolField;
  private BButton okButton, cancelButton;
  private ObjectPreviewCanvas preview;
  private Vector objects, paths;

  private static int counter = 1;

  public ExtrudeDialog(LayoutWindow window)
  {
    super(window, "Extrude", true);
    this.window = window;
    Scene scene = window.getScene();
    int selection[] = window.getSelectedIndices();
   
    // Identify the objects that can be extruded, and the paths along which they can be
    // extruded.

    objects = new Vector();
    paths = new Vector();
    for (int i = 0; i < selection.length; i++)
    {
      ObjectInfo obj = scene.getObject(selection[i]);
      if (obj.getObject() instanceof Curve)
      {
        objects.addElement(obj);
        paths.addElement(obj);
      }
      else if ((obj.getObject() instanceof TriangleMesh ||
          obj.getObject().canConvertToTriangleMesh() != Object3D.CANT_CONVERT) &&
          !obj.getObject().isClosed())
        objects.addElement(obj);
    }
    if (objects.size() == 1)
      paths.removeAllElements();

    // Layout the window.
   
    FormContainer content = new FormContainer(4, 10);
    setContent(BOutline.createEmptyBorder(content, UIUtilities.getStandardDialogInsets()));
    content.setDefaultLayout(new LayoutInfo(LayoutInfo.WEST, LayoutInfo.NONE, new Insets(0, 0, 0, 5), null));
    content.add(new BLabel("Object to Extrude:"), 0, 0, 2, 1);
    content.add(objChoice = new BComboBox(), 0, 1, 2, 1);
    for (int i = 0; i < objects.size(); i++)
      objChoice.add(((ObjectInfo) objects.elementAt(i)).getName());
    objChoice.addEventLink(ValueChangedEvent.class, this, "stateChanged");
    content.add(new BLabel("Extrude Direction:"), 0, 2, 2, 1);
    pathGroup = new RadioButtonGroup();
    content.add(xBox = new BRadioButton("X", true, pathGroup), 0, 3);
    content.add(yBox = new BRadioButton("Y", true, pathGroup), 0, 4);
    content.add(zBox = new BRadioButton("Z", true, pathGroup), 0, 5);
    content.add(pathBox = new BRadioButton("Curve", true, pathGroup), 0, 6);
    content.add(vectorBox = new BRadioButton("Vector", true, pathGroup), 0, 7);
    pathBox.setEnabled(paths.size() > 0);
    pathGroup.addEventLink(SelectionChangedEvent.class, this, "stateChanged");
    pathGroup.setSelection(zBox);
    RowContainer distanceRow = new RowContainer();
    content.add(distanceRow, 1, 4);
    distanceRow.add(new BLabel("Distance:"));
    distanceRow.add(distField = new ValueField(1.0, ValueField.POSITIVE, 5));
    distField.addEventLink(ValueChangedEvent.class, this, "makeObject");
    content.add(pathChoice = new BComboBox(), 1, 6);
    for (int i = 0; i < paths.size(); i++)
      pathChoice.add(((ObjectInfo) paths.elementAt(i)).getName());
    pathChoice.addEventLink(ValueChangedEvent.class, this, "stateChanged");
    RowContainer vectorRow = new RowContainer();
    content.add(vectorRow, 1, 7);
    vectorRow.add(new BLabel("X"));
    vectorRow.add(xField = new ValueField(0.0, ValueField.NONE, 4));
    xField.addEventLink(ValueChangedEvent.class, this, "makeObject");
    vectorRow.add(new BLabel("Y"));
    vectorRow.add(yField = new ValueField(0.0, ValueField.NONE, 4));
    yField.addEventLink(ValueChangedEvent.class, this, "makeObject");
    vectorRow.add(new BLabel("Z"));
    vectorRow.add(zField = new ValueField(1.0, ValueField.NONE, 4));
    zField.addEventLink(ValueChangedEvent.class, this, "makeObject");
    content.add(orientBox = new BCheckBox("Orientation Follows Curve", true), 0, 8, 2, 1);
    orientBox.addEventLink(ValueChangedEvent.class, this, "stateChanged");
    content.add(new BLabel("Number of Segments:"), 2, 0);
    content.add(new BLabel("Twist (degrees):"), 2, 1);
    content.add(new BLabel("Surface Accuracy:"), 2, 2);
    content.add(segField = new ValueField(1.0, ValueField.POSITIVE+ValueField.INTEGER, 5), 3, 0);
    content.add(angleField = new ValueField(0.0, ValueField.NONE, 5), 3, 1);
    content.add(tolField = new ValueField(0.1, ValueField.POSITIVE, 5), 3, 2);
    segField.addEventLink(ValueChangedEvent.class, this, "makeObject");
    angleField.addEventLink(ValueChangedEvent.class, this, "makeObject");
    tolField.addEventLink(ValueChangedEvent.class, this, "makeObject");
    if (paths.size() > 0)
      for (int i = 0; i < scene.getNumObjects(); i++)
        if (scene.getObject(i) != paths.elementAt(0))
        {
          objChoice.setSelectedIndex(i);
          break;
        }
    content.add(preview = new ObjectPreviewCanvas((ObjectInfo) objects.elementAt(0)), 2, 3, 2, 6,
        new LayoutInfo(LayoutInfo.CENTER, LayoutInfo.BOTH, null, null));
    RowContainer buttons = new RowContainer();
    content.add(buttons, 0, 9, 4, 1, new LayoutInfo());
    buttons.add(okButton = Translate.button("ok", this, "doOk"));
    buttons.add(cancelButton = Translate.button("cancel", this, "dispose"));
    makeObject();
    pack();
    UIUtilities.centerDialog(this, window);
    updateComponents();
    setVisible(true);
  }
 
  /** Deal with changes to the checkboxes and choices. */
 
  private void stateChanged()
  {
    makeObject();
    updateComponents();
  }
 
  /** Enable or disable components, based on the current selections. */
 
  private void updateComponents()
  {
    distField.setEnabled(xBox.getState() || yBox.getState() || zBox.getState());
    xField.setEnabled(vectorBox.getState());
    yField.setEnabled(vectorBox.getState());
    zField.setEnabled(vectorBox.getState());
    pathChoice.setEnabled(pathBox.getState());
    segField.setEnabled(!pathBox.getState());
    orientBox.setEnabled(pathBox.getState());
    Object3D profile = ((ObjectInfo) objects.elementAt(objChoice.getSelectedIndex())).getObject();
    tolField.setEnabled(!(profile instanceof Curve || profile instanceof TriangleMesh));
    if (pathBox.getState())
      okButton.setEnabled(objects.elementAt(objChoice.getSelectedIndex()) != paths.elementAt(pathChoice.getSelectedIndex()));
    else
      okButton.setEnabled(true);
  }
 
  private void doOk()
  {
    ObjectInfo profile = (ObjectInfo) objects.elementAt(objChoice.getSelectedIndex());
    CoordinateSystem coords = new CoordinateSystem(new Vec3(), Vec3.vz(), Vec3.vy());
    if (profile.getObject() instanceof Mesh)
    {
      Vec3 offset = profile.getCoords().fromLocal().times(((Mesh) profile.getObject()).getVertices()[0].r).minus(coords.fromLocal().times(((Mesh) preview.getObject().getObject()).getVertices()[0].r));
      coords.setOrigin(coords.getOrigin().plus(offset));
    }
    window.addObject(preview.getObject().getObject(), coords, "Extruded Object "+(counter++), null);
    window.setSelection(window.getScene().getNumObjects()-1);
    window.setUndoRecord(new UndoRecord(window, false, UndoRecord.DELETE_OBJECT, new Object [] {new Integer(window.getScene().getNumObjects()-1)}));
    window.updateImage();
    dispose();
  }
 
  // Create the extruded object.
 
  private void makeObject()
  {
    ObjectInfo profile = (ObjectInfo) objects.elementAt(objChoice.getSelectedIndex());
    Curve path;
    CoordinateSystem pathCoords;
   
    if (pathBox.getState())
      {
        ObjectInfo info = (ObjectInfo) paths.elementAt(pathChoice.getSelectedIndex());
        path = (Curve) info.getObject();
        pathCoords = info.getCoords();
      }
    else
      {
        Vec3 dir = new Vec3();
        if (xBox.getState())
          dir.x = distField.getValue();
        else if (yBox.getState())
          dir.y = distField.getValue();
        else if (zBox.getState())
          dir.z = distField.getValue();
        else
          dir.set(xField.getValue(), yField.getValue(), zField.getValue());
        Vec3 v[] = new Vec3 [(int) segField.getValue()+1];
        float smooth[] = new float [v.length];
        for (int i = 0; i < v.length; i++)
          {
            v[i] = new Vec3(dir);
            v[i].scale(i/segField.getValue());
            smooth[i] = 1.0f;
          }
        path = new Curve(v, smooth, Mesh.INTERPOLATING, false);
        pathCoords = new CoordinateSystem(new Vec3(), Vec3.vz(), Vec3.vy());
      }
    Object3D obj;
    if (profile.getObject() == path)
      obj = null;
    else if (profile.getObject() instanceof TriangleMesh)
      obj = extrudeMesh((TriangleMesh) profile.getObject(), path, profile.getCoords(), pathCoords, angleField.getValue()*Math.PI/180.0, orientBox.getState());
    else if (profile.getObject() instanceof Curve)
      obj = extrudeCurve((Curve) profile.getObject(), path, profile.getCoords(), pathCoords, angleField.getValue()*Math.PI/180.0, orientBox.getState());
    else
      obj = extrudeMesh(profile.getObject().convertToTriangleMesh(tolField.getValue()), path, profile.getCoords(), pathCoords, angleField.getValue()*Math.PI/180.0, orientBox.getState());
    Texture tex = window.getScene().getDefaultTexture();
    obj.setTexture(tex, tex.getDefaultMapping(obj));
    preview.setObject(obj);
    preview.repaint();
  }
 
  /** Extrude a curve into a spline mesh.
     
      @param profile     the curve to extrude
      @param profCoords  the coordinate system of the profile
      @param dir         the direction and distance along which to extrude it
      @param segments    the number of segments to create
      @param angle       the twist angle (in radians)
      @param orient      if true, the orientation of the profile will follow the curve
      @return the extruded object
  */
 
  public static Object3D extrudeCurve(Curve profile, CoordinateSystem profCoords, Vec3 dir, int segments, double angle, boolean orient)
  {
    Vec3 v[] = new Vec3 [segments+1];
    float smooth[] = new float [v.length];
    for (int i = 0; i < v.length; i++)
      {
        v[i] = new Vec3(dir);
        v[i].scale(i*segments);
        smooth[i] = 1.0f;
      }
    Curve path = new Curve(v, smooth, Mesh.INTERPOLATING, false);
    return extrudeCurve(profile, path, profCoords, new CoordinateSystem(), angle, orient);
  }
 
  /** Extrude a curve into a spline mesh.
     
      @param profile     the curve to extrude
      @param path        the path along which to extrude it
      @param profCoords  the coordinate system of the profile
      @param pathCoords  the coordinate system of the path
      @param angle       the twist angle (in radians)
      @param orient      if true, the orientation of the profile will follow the curve
      @return the extruded object
  */
 
  public static Object3D extrudeCurve(Curve profile, Curve path, CoordinateSystem profCoords, CoordinateSystem pathCoords, double angle, boolean orient)
  {
    MeshVertex profVert[] = profile.getVertices(), pathVert[] = path.getVertices();
    Vec3 profv[] = new Vec3 [profVert.length], pathv[] = new Vec3[pathVert.length];
    Vec3 subdiv[], center = new Vec3(), zdir[], updir[], t[], v[][];
    float usmooth[] = new float [pathVert.length], vsmooth[] = new float [profVert.length];
    float profSmooth[] = profile.getSmoothness(), pathSmooth[] = path.getSmoothness();
    CoordinateSystem localCoords = new CoordinateSystem(new Vec3(), Vec3.vz(), Vec3.vy());
    Mat4 rotate;
    int i, j;
   
    for (i = 0; i < profVert.length; i++)
      profv[i] = profCoords.fromLocal().timesDirection(profVert[i].r);
    for (i = 0; i < pathVert.length; i++)
      pathv[i] = pathCoords.fromLocal().timesDirection(pathVert[i].r);

    // Construct the Minimally Rotating Frame at every point along the path.  First,
    // subdivide the path and determine its direction at the starting point.
   
    subdiv = new Curve(pathv, pathSmooth, path.getSmoothingMethod(), path.isClosed()).subdivideCurve().getVertexPositions();
    t = new Vec3 [subdiv.length];
    zdir = new Vec3 [subdiv.length];
    updir = new Vec3 [subdiv.length];
    t[0] = subdiv[1].minus(subdiv[0]);
    t[0].normalize();
    zdir[0] = Vec3.vz();
    updir[0] = Vec3.vy();
   
    // Now find two vectors perpendicular to the path, and determine how much they
    // contribute to the z and up directions.
   
    Vec3 dir1, dir2;
    double zfrac1, zfrac2, upfrac1, upfrac2;
    zfrac1 = t[0].dot(zdir[0]);
    zfrac2 = Math.sqrt(1.0-zfrac1*zfrac1);
    dir1 = zdir[0].minus(t[0].times(zfrac1));
    dir1.normalize();
    upfrac1 = t[0].dot(updir[0]);
    upfrac2 = Math.sqrt(1.0-upfrac1*upfrac1);
    dir2 = updir[0].minus(t[0].times(upfrac1));
    dir2.normalize();
   
    // Propagate the vectors along the path.
   
    for (i = 1; i < subdiv.length; i++)
      {
        if (i == subdiv.length-1)
          {
            if (path.isClosed())
              t[i] = subdiv[0].minus(subdiv[subdiv.length-2]);
            else
              t[i] = subdiv[subdiv.length-1].minus(subdiv[subdiv.length-2]);
          }
        else
          t[i] = subdiv[i+1].minus(subdiv[i-1]);
        t[i].normalize();
        if (orient)
          {
            dir1 = dir1.minus(t[i].times(t[i].dot(dir1)));
            dir1.normalize();
            dir2 = dir2.minus(t[i].times(t[i].dot(dir2)));
            dir2.normalize();
            zdir[i] = t[i].times(zfrac1).plus(dir1.times(zfrac2));
            updir[i] = t[i].times(upfrac1).plus(dir2.times(upfrac2));
          }
        else
          {
            zdir[i] = zdir[i-1];
            updir[i] = updir[i-1];
          }
      }
   
    // Set the smoothness values.
   
    if (path.getSmoothingMethod() != Mesh.NO_SMOOTHING)
      for (i = 0; i < usmooth.length; i++)
        usmooth[i] = pathSmooth[i];
    if (profile.getSmoothingMethod() != Mesh.NO_SMOOTHING)
      for (i = 0; i < vsmooth.length; i++)
        vsmooth[i] = profSmooth[i];

    // If one curve is interpolating and the other is approximating, use a subdivided
    // version of the interpolating one to more accurately get its shape (since the final
    // mesh will be approximating).
   
    if (profile.getSmoothingMethod() == Mesh.APPROXIMATING && path.getSmoothingMethod() == Mesh.INTERPOLATING)
      {
        pathv = subdiv;
        usmooth = new float [pathv.length];
        for (i = 0; i < usmooth.length; i++)
          {
            if (i%2 == 0)
              usmooth[i] = Math.min(pathSmooth[i/2]*2.0f, 1.0f);
            else
              usmooth[i] = 1.0f;
          }
      }
    if (profile.getSmoothingMethod() == Mesh.INTERPOLATING && path.getSmoothingMethod() == Mesh.APPROXIMATING)
      {
        profv = new Curve(profv, profSmooth, profile.getSmoothingMethod(), profile.isClosed()).subdivideCurve().getVertexPositions();
        vsmooth = new float [profv.length];
        for (i = 0; i < vsmooth.length; i++)
          {
            if (i%2 == 0)
              vsmooth[i] = Math.min(profSmooth[i/2]*2.0f, 1.0f);
            else
              vsmooth[i] = 1.0f;
          }
      }
   
    // Create the extruded surface.
   
    v = new Vec3 [pathv.length][profv.length];
    for (i = 0; i < pathv.length; i++)
      {
        localCoords.setOrigin(pathv[i]);
        int k = (pathv.length == subdiv.length ? i : 2*i);
        localCoords.setOrientation(zdir[k], updir[k]);
        if (angle != 0.0)
          {
            rotate = Mat4.axisRotation(t[k], i*angle/(pathv.length-1));
            localCoords.transformAxes(rotate);
          }
        for (j = 0; j < profv.length; j++)
          {
            v[i][j] = localCoords.fromLocal().times(profv[j]);
            center.add(v[i][j]);
          }
      }
   
    // Center it.
   
    center.scale(1.0/(profv.length*pathv.length));
    for (i = 0; i < pathv.length; i++)
      for (j = 0; j < profv.length; j++)
        v[i][j].subtract(center);
    SplineMesh mesh = new SplineMesh(v, usmooth, vsmooth, Math.max(profile.getSmoothingMethod(),
        path.getSmoothingMethod()), path.isClosed(), profile.isClosed());
    mesh.makeRightSideOut();
    return mesh;
  }

  /** Extrude a triangle mesh into a solid object.
     
      @param profile     the TriangleMesh to extrude
      @param profCoords  the coordinate system of the profile
      @param dir         the direction and distance along which to extrude it
      @param segments    the number of segments to create
      @param angle       the twist angle (in radians)
      @param orient      if true, the orientation of the profile will follow the curve
      @return the extruded object
  */
 
  public static Object3D extrudeMesh(TriangleMesh profile, CoordinateSystem profCoords, Vec3 dir, int segments, double angle, boolean orient)
  {
    Vec3 v[] = new Vec3 [segments+1];
    float smooth[] = new float [v.length];
    for (int i = 0; i < v.length; i++)
      {
        v[i] = new Vec3(dir);
        v[i].scale(i*segments);
        smooth[i] = 1.0f;
      }
    Curve path = new Curve(v, smooth, Mesh.INTERPOLATING, false);
    return extrudeMesh(profile, path, profCoords, new CoordinateSystem(), angle, orient);
  }
 
  /** Extrude a triangle mesh into a solid object.
     
      @param profile     the TriangleMesh to extrude
      @param path        the path along which to extrude it
      @param profCoords  the coordinate system of the profile
      @param pathCoords  the coordinate system of the path
      @param angle       the twist angle (in radians)
      @param orient      if true, the orientation of the profile will follow the curve
      @return the extruded object
  */
 
  public static Object3D extrudeMesh(TriangleMesh profile, Curve path, CoordinateSystem profCoords, CoordinateSystem pathCoords, double angle, boolean orient)
  {
    Vertex profVert[] = (Vertex [] ) profile.getVertices();
    MeshVertex pathVert[] = path.getVertices();
    Edge profEdge[] = profile.getEdges();
    Face profFace[] = profile.getFaces();
    Vec3 profv[] = new Vec3 [profVert.length], pathv[] = new Vec3[pathVert.length];
    Vec3 subdiv[], center, zdir[], updir[], t[], v[];
    float pathSmooth[] = path.getSmoothness();
    CoordinateSystem localCoords = new CoordinateSystem(new Vec3(), Vec3.vz(), Vec3.vy());
    Mat4 rotate;
    int numBoundaryEdges = 0, numBoundaryPoints = 0, i, j, k;
    int boundaryEdge[], boundaryPoint[];
   
    for (i = 0; i < profVert.length; i++)
      profv[i] = profCoords.fromLocal().timesDirection(profVert[i].r);
    for (i = 0; i < pathVert.length; i++)
      pathv[i] = pathCoords.fromLocal().timesDirection(pathVert[i].r);
    if (path.getSmoothingMethod() == Mesh.NO_SMOOTHING)
      for (i = 0; i < pathSmooth.length; i++)
        pathSmooth[i] = 0.0f;
   
    // Make a list of the edges and vertices which are on the boundary of the mesh.
   
    boolean onBound[] = new boolean [profv.length];
    for (i = 0; i < profEdge.length; i++)
      if (profEdge[i].f2 == -1)
        {
          numBoundaryEdges++;
          onBound[profEdge[i].v1] = onBound[profEdge[i].v2] = true;
        }
    for (i = 0; i < onBound.length; i++)
      if (onBound[i])
        numBoundaryPoints++;
    boundaryEdge = new int [numBoundaryEdges];
    boundaryPoint = new int [numBoundaryPoints];
    for (i = 0, j = 0; i < profEdge.length; i++)
      if (profEdge[i].f2 == -1)
        boundaryEdge[j++] = i;
    for (i = 0, j = 0; i < onBound.length; i++)
      if (onBound[i])
        boundaryPoint[j++] = i;

    // Find which direction each boundary edge points in.
   
    boolean forward[] = new boolean [boundaryEdge.length];
    int edgeVertIndex[][] = new int [boundaryEdge.length][2];
    for (i = 0; i < boundaryEdge.length; i++)
      {
        Edge ed = profEdge[boundaryEdge[i]];
        Face fc = profFace[ed.f1];
        forward[i] = ((fc.v1 == ed.v1 && fc.v2 == ed.v2) ||
            (fc.v2 == ed.v1 && fc.v3 == ed.v2) || (fc.v3 == ed.v1 && fc.v1 == ed.v2));
        for (j = 0; j < boundaryPoint.length; j++)
          {
            if (boundaryPoint[j] == ed.v1)
              edgeVertIndex[i][0] = j;
            else if (boundaryPoint[j] == ed.v2)
              edgeVertIndex[i][1] = j;
          }
      }

    // Make up a list of the indices for every point on the side of the extruded object.
   
    int index[][];
    if (path.isClosed())
      {
        index = new int [pathv.length+1][boundaryPoint.length];
        for (i = 0; i < boundaryPoint.length; i++)
          {
            for (j = 0; j < pathv.length; j++)
              index[j][i] = j*boundaryPoint.length+i;
            index[j][i] = i;
          }
      }
    else
      {
        index = new int [pathv.length][boundaryPoint.length];
        for (i = 0; i < boundaryPoint.length; i++)
          {
            index[0][i] = boundaryPoint[i];
            index[pathv.length-1][i] = boundaryPoint[i]+profv.length;
            for (j = 1; j < pathv.length-1; j++)
              index[j][i] = (j-1)*boundaryPoint.length+i+2*profv.length;
          }
      }

    // Construct the Minimally Rotating Frame at every point along the path.  First,
    // subdivide the path and determine its direction at the starting point.
   
    subdiv = new Curve(pathv, pathSmooth, path.getSmoothingMethod(), path.isClosed()).subdivideCurve().getVertexPositions();
    t = new Vec3 [subdiv.length];
    zdir = new Vec3 [subdiv.length];
    updir = new Vec3 [subdiv.length];
    t[0] = subdiv[1].minus(subdiv[0]);
    t[0].normalize();
    zdir[0] = Vec3.vz();
    updir[0] = Vec3.vy();
   
    // Now find two vectors perpendicular to the path, and determine how much they
    // contribute to the z and up directions.
   
    Vec3 dir1, dir2;
    double zfrac1, zfrac2, upfrac1, upfrac2;
    zfrac1 = t[0].dot(zdir[0]);
    zfrac2 = Math.sqrt(1.0-zfrac1*zfrac1);
    dir1 = zdir[0].minus(t[0].times(zfrac1));
    dir1.normalize();
    upfrac1 = t[0].dot(updir[0]);
    upfrac2 = Math.sqrt(1.0-upfrac1*upfrac1);
    dir2 = updir[0].minus(t[0].times(upfrac1));
    dir2.normalize();
   
    // Propagate the vectors along the path.
   
    for (i = 1; i < subdiv.length; i++)
      {
        if (i == subdiv.length-1)
          {
            if (path.isClosed())
              t[i] = subdiv[0].minus(subdiv[subdiv.length-2]);
            else
              t[i] = subdiv[subdiv.length-1].minus(subdiv[subdiv.length-2]);
          }
        else
          t[i] = subdiv[i+1].minus(subdiv[i-1]);
        t[i].normalize();
        if (orient)
          {
            dir1 = dir1.minus(t[i].times(t[i].dot(dir1)));
            dir1.normalize();
            dir2 = dir2.minus(t[i].times(t[i].dot(dir2)));
            dir2.normalize();
            zdir[i] = t[i].times(zfrac1).plus(dir1.times(zfrac2));
            updir[i] = t[i].times(upfrac1).plus(dir2.times(upfrac2));
          }
        else
          {
            zdir[i] = zdir[i-1];
            updir[i] = updir[i-1];
          }
      }
   
    // Create the extruded surface.
   
    if (path.isClosed())
      v = new Vec3 [numBoundaryPoints*pathv.length];
    else
      v = new Vec3 [2*profv.length+numBoundaryPoints*(pathv.length-2)];
    Vector newEdge = new Vector(), newFace = new Vector();
    boolean angled = (profile.getSmoothingMethod() == Mesh.NO_SMOOTHING && path.getSmoothingMethod() != Mesh.NO_SMOOTHING);
    if (!path.isClosed())
      {
        // Add two copies of the profile mesh, to serve as the ends.
       
        localCoords.setOrigin(pathv[0]);
        localCoords.setOrientation(zdir[0], updir[0]);
        for (i = 0; i < profv.length; i++)
          v[i] = localCoords.fromLocal().times(profv[i]);
        k = (pathv.length == subdiv.length ? pathv.length-1 : 2*(pathv.length-1));
        localCoords.setOrigin(pathv[pathv.length-1]);
        localCoords.setOrientation(zdir[k], updir[k]);
        if (angle != 0.0)
          {
            rotate = Mat4.axisRotation(t[k], angle);
            localCoords.transformAxes(rotate);
          }
        for (i = 0; i < profv.length; i++)
          v[i+profv.length] = localCoords.fromLocal().times(profv[i]);
       
        // Add the edges and faces.
       
        for (i = 0; i < profEdge.length; i++)
          {
            float smoothness = profEdge[i].smoothness;
            if (angled || profEdge[i].f2 == -1)
              smoothness = 0.0f;
            newEdge.addElement(new EdgeInfo(profEdge[i].v1, profEdge[i].v2, smoothness));
            newEdge.addElement(new EdgeInfo(profEdge[i].v1+profv.length, profEdge[i].v2+profv.length, smoothness));
          }
        for (i = 0; i < profFace.length; i++)
          {
            Face f = profFace[i];
            newFace.addElement(new int [] {f.v1, f.v2, f.v3});
            newFace.addElement(new int [] {f.v1+profv.length, f.v3+profv.length, f.v2+profv.length});
          }
      }
    for (i = 0; i < pathv.length; i++)
      {
        if (!path.isClosed() && i == pathv.length-1)
          break;
       
        // Add each row of triangles and edges around the side.
       
        for (j = 0; j < boundaryEdge.length; j++)
          {
            int v1, v2;
            if (forward[j])
              {
                v1 = edgeVertIndex[j][0];
                v2 = edgeVertIndex[j][1];
              }
            else
              {
                v1 = edgeVertIndex[j][1];
                v2 = edgeVertIndex[j][0];
              }
            newFace.addElement(new int [] {index[i][v1], index[i+1][v1], index[i+1][v2]});
            newFace.addElement(new int [] {index[i][v2], index[i][v1], index[i+1][v2]});
            EdgeInfo ed1 = new EdgeInfo(index[i][v1], index[i+1][v1], angled ? 0.0f : profVert[boundaryPoint[v1]].smoothness);
            newEdge.addElement(ed1);
            ed1 = new EdgeInfo(index[i][v2], index[i+1][v2], angled ? 0.0f : profVert[boundaryPoint[v2]].smoothness);
            newEdge.addElement(ed1);
            ed1 = new EdgeInfo(index[i][v1], index[i+1][v2], 1.0f);
            newEdge.addElement(ed1);
            if (path.isClosed() || i > 0)
              {
                ed1 = new EdgeInfo(index[i][v1], index[i][v2], pathSmooth[i]);
                newEdge.addElement(ed1);
              }
          }
       
        // Add each row of points around the side.
       
        localCoords.setOrigin(pathv[i]);
        k = (pathv.length == subdiv.length ? i : 2*i);
        localCoords.setOrientation(zdir[k], updir[k]);
        if (angle != 0.0)
          {
            rotate = Mat4.axisRotation(t[k], i*angle/(pathv.length-1));
            localCoords.transformAxes(rotate);
          }
        for (j = 0; j < boundaryPoint.length; j++)
          v[index[i][j]] = localCoords.fromLocal().times(profv[boundaryPoint[j]]);
      }
   
    // Center it.
   
    center = new Vec3();
    for (i = 0; i < v.length; i++)
      center.add(v[i]);
    center.scale(1.0/v.length);
    for (i = 0; i < v.length; i++)
      v[i].subtract(center);
   
    // Build the final object.
   
    int faces[][] = new int [newFace.size()][];
    for (i = 0; i < faces.length; i++)
      faces[i] = (int []) newFace.elementAt(i);
    TriangleMesh mesh = new TriangleMesh(v, faces);
    Edge meshEdge[] = mesh.getEdges();
    for (i = 0; i < newEdge.size(); i++)
      {
        EdgeInfo info = (EdgeInfo) newEdge.elementAt(i);
        if (info.smoothness == 1.0f)
          continue;
        for (j = 0; j < meshEdge.length; j++)
          if ((meshEdge[j].v1 == info.v1 && meshEdge[j].v2 == info.v2) ||
              (meshEdge[j].v1 == info.v2 && meshEdge[j].v2 == info.v1))
            meshEdge[j].smoothness = info.smoothness;
      }
    mesh.setSmoothingMethod(Math.max(profile.getSmoothingMethod(), path.getSmoothingMethod()));
    mesh.makeRightSideOut();
    return mesh;
  }
 
  // Inner class used for storing information about new edges being created.
 
  private static class EdgeInfo
  {
    int v1, v2;
    float smoothness;
   
    public EdgeInfo(int vert1, int vert2, float smooth)
    {
      v1 = vert1;
      v2 = vert2;
      smoothness = smooth;
    }
  }
}
TOP

Related Classes of artofillusion.tools.ExtrudeDialog$EdgeInfo

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.