CurveEditorWindow.java :  » Graphic-3D » Art-of-Illusion » ArtOfIllusion » Java Open Source

Java Open Source » Graphic 3D » Art of Illusion 
Art of Illusion » ArtOfIllusion » CurveEditorWindow.java
/* Copyright (C) 1999-2008 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;

import artofillusion.math.*;
import artofillusion.object.*;
import artofillusion.ui.*;
import buoy.event.*;
import buoy.widget.*;

import java.awt.*;

/** The CurveEditorWindow class represents the window for editing Curve objects. */

public class CurveEditorWindow extends MeshEditorWindow implements EditingWindow
{
  protected BMenu editMenu, meshMenu, smoothMenu;
  protected BMenuItem editMenuItem[], meshMenuItem[];
  protected BCheckBoxMenuItem smoothItem[];
  protected Runnable onClose;
  private int selectionDistance[], maxDistance;
  private boolean topology;
  boolean selected[];

  public CurveEditorWindow(EditingWindow parent, String title, ObjectInfo obj, Runnable onClose, boolean allowTopology)
  {
    super(parent, title, obj);
    this.onClose = onClose;
    topology = allowTopology;
    FormContainer content = new FormContainer(new double [] {0, 1}, new double [] {1, 0, 0});
    setContent(content);
    content.setDefaultLayout(new LayoutInfo(LayoutInfo.CENTER, LayoutInfo.BOTH, null, null));
    content.add(helpText = new BLabel(), 0, 1, 2, 1);
    content.add(viewsContainer, 1, 0);
    RowContainer buttons = new RowContainer();
    buttons.add(Translate.button("ok", this, "doOk"));
    buttons.add(Translate.button("cancel", this, "doCancel"));
    content.add(buttons, 0, 2, 2, 1, new LayoutInfo());
    content.add(tools = new ToolPalette(1, 7), 0, 0, new LayoutInfo(LayoutInfo.NORTH, LayoutInfo.NONE, null, null));
    EditingTool metaTool, altTool, compoundTool;
    tools.addTool(defaultTool = new ReshapeMeshTool(this, this));
    tools.addTool(new ScaleMeshTool(this, this));
    tools.addTool(new RotateMeshTool(this, this, false));
    tools.addTool(new SkewMeshTool(this, this));
    tools.addTool(new TaperMeshTool(this, this));
    tools.addTool(compoundTool = new MoveScaleRotateMeshTool(this, this));
    if (ArtOfIllusion.getPreferences().getUseCompoundMeshTool())
      defaultTool = compoundTool;
    tools.addTool(metaTool = new MoveViewTool(this));
    tools.addTool(altTool = new RotateViewTool(this));
    tools.setDefaultTool(defaultTool);
    tools.selectTool(defaultTool);
    for (int i = 0; i < theView.length; i++)
    {
      MeshViewer view = (MeshViewer) theView[i];
      view.setMetaTool(metaTool);
      view.setAltTool(altTool);
      view.setMeshVisible(true);
      view.setScene(parent.getScene(), obj);
    }
    createEditMenu();
    createMeshMenu((Curve) obj.getObject());
    createViewMenu();
    recursivelyAddListeners(this);
    UIUtilities.applyDefaultFont(content);
    UIUtilities.applyDefaultBackground(content);
    Rectangle screenBounds = GraphicsEnvironment.getLocalGraphicsEnvironment().getMaximumWindowBounds();
    Dimension windowDim = new Dimension((screenBounds.width*3)/4, (screenBounds.height*3)/4);
    setBounds(new Rectangle((screenBounds.width-windowDim.width)/2, (screenBounds.height-windowDim.height)/2, windowDim.width, windowDim.height));
    tools.requestFocus();
    selected = new boolean [((Curve) obj.getObject()).getVertices().length];
    findSelectionDistance();
    updateMenus();
  }

  /** This constructor is here to let TubeEditorWindow subclass CurveEditorWindow. */

  protected CurveEditorWindow(EditingWindow parent, String title, ObjectInfo obj)
  {
    super(parent, title, obj);
  }

  void createEditMenu()
  {
    editMenu = Translate.menu("edit");
    menubar.add(editMenu);
    editMenuItem = new BMenuItem [3];
    editMenu.add(undoItem = Translate.menuItem("undo", this, "undoCommand"));
    editMenu.add(redoItem = Translate.menuItem("redo", this, "redoCommand"));
    editMenu.add(Translate.menuItem("selectAll", this, "selectAllCommand"));
    editMenu.add(editMenuItem[0] = Translate.menuItem("extendSelection", this, "extendSelectionCommand"));
    editMenu.add(editMenuItem[1] = Translate.menuItem("invertSelection", this, "invertSelectionCommand"));
    editMenu.add(editMenuItem[2] = Translate.checkboxMenuItem("freehandSelection", this, "freehandModeChanged", false));
    editMenu.addSeparator();
    editMenu.add(Translate.menuItem("curveTension", this, "setTensionCommand"));
  }

  void createMeshMenu(Curve obj)
  {
    meshMenu = Translate.menu("curve");
    menubar.add(meshMenu);
    meshMenuItem = new BMenuItem [7];
    meshMenuItem[0] = Translate.menuItem("deletePoints", this, "deleteCommand");
    if (topology)
      meshMenu.add(meshMenuItem[0]);
    meshMenuItem[1] = Translate.menuItem("subdivide", this, "subdivideCommand");
    if (topology)
      meshMenu.add(meshMenuItem[1]);
    meshMenu.add(meshMenuItem[2] = Translate.menuItem("editPoints", this, "setPointsCommand"));
    meshMenu.add(meshMenuItem[3] = Translate.menuItem("transformPoints", this, "transformPointsCommand"));
    meshMenu.add(meshMenuItem[4] = Translate.menuItem("randomize", this, "randomizeCommand"));
    meshMenu.add(Translate.menuItem("centerCurve", this, "centerCommand"));
    meshMenu.addSeparator();
    meshMenu.add(meshMenuItem[5] = Translate.menuItem("smoothness", this, "setSmoothnessCommand"));
    meshMenu.add(smoothMenu = Translate.menu("smoothingMethod"));
    smoothItem = new BCheckBoxMenuItem [3];
    smoothMenu.add(smoothItem[0] = Translate.checkboxMenuItem("none", this, "smoothingChanged", obj.getSmoothingMethod() == Curve.NO_SMOOTHING));
    smoothMenu.add(smoothItem[1] = Translate.checkboxMenuItem("interpolating", this, "smoothingChanged", obj.getSmoothingMethod() == Curve.INTERPOLATING));
    smoothMenu.add(smoothItem[2] = Translate.checkboxMenuItem("approximating", this, "smoothingChanged", obj.getSmoothingMethod() == Curve.APPROXIMATING));
    meshMenu.add(meshMenuItem[6] = Translate.menuItem("closedEnds", this, "toggleClosedCommand"));
    if (obj.isClosed())
      meshMenuItem[6].setText(Translate.text("menu.openEnds"));
  }

  protected BMenu createShowMenu()
  {
    BMenu menu = Translate.menu("show");
    showItem = new BCheckBoxMenuItem [4];
    menu.add(showItem[0] = Translate.checkboxMenuItem("curve", this, "shownItemChanged", true));
    menu.add(showItem[3] = Translate.checkboxMenuItem("entireScene", this, "shownItemChanged", ((MeshViewer) theView[currentView]).getSceneVisible()));
    return menu;
  }
  
  /** Get the object being edited in this window. */
  
  public ObjectInfo getObject()
  {
    return objInfo;
  }
  
  /** Set the object being edited in this window. */
  
  public void setObject(Object3D obj)
  {
    objInfo.setObject(obj);
    objInfo.clearCachedMeshes();
  }
  
  public void setMesh(Mesh mesh)
  {
    Curve obj = (Curve) mesh;
    setObject(obj);
    if (selected.length != obj.getVertices().length)
      selected = new boolean [obj.getVertices().length];
    findSelectionDistance();
    currentTool.getWindow().updateMenus();
  }
  
  /** Get an array of flags telling which vertices are currently selected. */
  
  public boolean[] getSelection()
  {
    return selected;
  }
  
  public void setSelection(boolean sel[])
  {
    selected = sel;
    findSelectionDistance();
    updateMenus();
    updateImage();
  }

  public int[] getSelectionDistance()
  {
    if (maxDistance != getTensionDistance())
      findSelectionDistance();
    return selectionDistance;
  }
  
  /** The return value has no meaning, since there is only one selection mode in this window. */
  
  public int getSelectionMode()
  {
    return 0;
  }
  
  /** This is ignored, since there is only one selection mode in this window. */
  
  public void setSelectionMode(int mode)
  {
  }

  /** Calculate the distance (in edges) between each vertex and the nearest selected vertex. */

  void findSelectionDistance()
  {
    Curve theCurve = (Curve) getObject().getObject();
    int i, j, dist[] = new int [theCurve.getVertices().length];
    
    maxDistance = getTensionDistance();
    
    // First, set each distance to 0 or -1, depending on whether that vertex is part of the
    // current selection.
    
    for (i = 0; i < dist.length; i++)
      dist[i] = selected[i] ? 0 : -1;

    // Now extend this outward up to maxDistance.

    for (i = 0; i < maxDistance; i++)
      {
        for (j = 0; j < dist.length-1; j++)
          if (dist[j] == -1 && dist[j+1] == i)
            dist[j] = i+1;
        for (j = 1; j < dist.length; j++)
          if (dist[j] == -1 && dist[j-1] == i)
            dist[j] = i+1;
        if (theCurve.isClosed())
          {
            if (dist[0] == -1 && dist[dist.length-1] == i)
              dist[0] = i+1;
            if (dist[0] == i && dist[dist.length-1] == -1)
              dist[dist.length-1] = i+1;
          }
      }
    selectionDistance = dist;
  }

  /* EditingWindow methods. */

  public void updateMenus()
  {
    super.updateMenus();
    int i;
    for (i = 0; i < selected.length && !selected[i]; i++);
    if (i < selected.length)
    {
      editMenuItem[0].setEnabled(true);
      for (i = 0; i < 6; i++)
        meshMenuItem[i].setEnabled(true);
    }
    else
    {
      editMenuItem[0].setEnabled(false);
      for (i = 0; i < 6; i++)
        meshMenuItem[i].setEnabled(false);
    }
  }
  
  protected void doOk()
  {
    Curve theMesh = (Curve) objInfo.getObject();
    oldMesh.copyObject(theMesh);
    oldMesh = null;
    dispose();
    onClose.run();
  }
  
  protected void doCancel()
  {
    oldMesh = null;
    dispose();
  }
  
  protected void freehandModeChanged()
  {
    for (int i = 0; i < theView.length; i++)
      ((CurveViewer) theView[i]).setFreehandSelection(((BCheckBoxMenuItem) editMenuItem[2]).getState());
  }
  
  private void smoothingChanged(CommandEvent ev)
  {
    Widget source = ev.getWidget();
    if (source == smoothItem[0])
      setSmoothingMethod(Mesh.NO_SMOOTHING);
    else if (source == smoothItem[1])
      setSmoothingMethod(Mesh.INTERPOLATING);
    else if (source == smoothItem[2])
      setSmoothingMethod(Mesh.APPROXIMATING);
  }

  /** Select the entire curve. */
  
  public void selectAllCommand()
  {
    setUndoRecord(new UndoRecord(this, false, UndoRecord.SET_MESH_SELECTION, new Object [] {this, new Integer(0), selected.clone()}));
    for (int i = 0; i < selected.length; i++)
      selected[i] = true;
    setSelection(selected);
  }

  /** Extend the selection outward by one edge. */

  public void extendSelectionCommand()
  {
    int oldDist = tensionDistance;
    tensionDistance = 1;
    int dist[] = getSelectionDistance();
    boolean newSel[] = new boolean [dist.length];
    tensionDistance = oldDist;
    
    setUndoRecord(new UndoRecord(this, false, UndoRecord.SET_MESH_SELECTION, new Object [] {this, new Integer(0), selected.clone()}));
    for (int i = 0; i < dist.length; i++)
      newSel[i] = (dist[i] == 0 || dist[i] == 1);
    setSelection(newSel);
  }
  
  /** Invert the current selection. */
  
  public void invertSelectionCommand()
  {
    boolean newSel[] = new boolean [selected.length];
    for (int i = 0; i < newSel.length; i++)
      newSel[i] = !selected[i];
    setUndoRecord(new UndoRecord(this, false, UndoRecord.SET_MESH_SELECTION, new Object [] {this, new Integer(0), selected}));
    setSelection(newSel);
  }

  public void deleteCommand()
  {
    if (!topology)
      return;
    int i, j, num = 0;
    Curve theCurve = (Curve) objInfo.getObject();
    boolean newsel[];
    MeshVertex vt[] = theCurve.getVertices();
    float s[] = theCurve.getSmoothness(), news[];
    Vec3 v[];

    for (i = 0; i < selected.length; i++)
      if (selected[i])
        num++;
    if (num == 0)
      return;
    if (!theCurve.isClosed() && selected.length-num < 2)
      {
        new BStandardDialog("", Translate.text("curveNeeds2Points"), BStandardDialog.INFORMATION).showMessageDialog(this);
        return;
      }
    if (theCurve.isClosed() && selected.length-num < 3)
      {
        new BStandardDialog("", Translate.text("curveNeeds3Points"), BStandardDialog.INFORMATION).showMessageDialog(this);
        return;
      }
    setUndoRecord(new UndoRecord(this, false, UndoRecord.COPY_OBJECT, new Object [] {theCurve, theCurve.duplicate()}));
    v = new Vec3 [vt.length-num];
    news = new float [vt.length-num];
    newsel = new boolean [vt.length-num];
    for (i = 0, j = 0; i < vt.length; i++)
    {
      if (!selected[i])
      {
        newsel[j] = selected[i];
        news[j] = s[i];
        v[j++] = vt[i].r;
      }
    }
    theCurve.setShape(v, news);
    setSelection(newsel);
  }

  public void subdivideCommand()
  {
    Curve theCurve = (Curve) objInfo.getObject();
    MeshVertex vt[] = theCurve.getVertices();
    float s[] = theCurve.getSmoothness(), news[];
    boolean newsel[], split[];
    Vec3 v[], newpos[];
    int i, j, p1, p3, p4, splitcount = 0, method = theCurve.getSmoothingMethod();
    
    v = new Vec3 [vt.length];
    for (i = 0; i < vt.length; i++)
      v[i] = vt[i].r;
    
    // Determine which parts need to be subdivided.
    
    if (theCurve.isClosed())
      split = new boolean [vt.length];
    else
      split = new boolean [vt.length-1];
    for (i = 0; i < split.length; i++)
      if (selected[i] && selected[(i+1)%selected.length])
      {
        split[i] = true;
        splitcount++;
      }
    newpos = new Vec3 [vt.length+splitcount];
    news = new float [vt.length+splitcount];
    newsel = new boolean [vt.length+splitcount];
    
    // Do the subdivision.

    for (i = 0, j = 0; i < split.length; i++)
    {
      newsel[j] = selected[i];
      p1 = i-1;
      if (p1 < 0)
      {
        if (theCurve.isClosed())
          p1 = v.length-1;
        else
          p1 = 0;
      }
      if (i < v.length-1)
        p3 = i+1;
      else
      {
        if (theCurve.isClosed())
          p3 = 0;
        else
          p3 = v.length-1;
      }
      if (selected[i] && method == Mesh.APPROXIMATING)
        newpos[j] = Curve.calcApproxPoint(v, s, p1, i, p3);
      else
        newpos[j] = vt[i].r;
      if (selected[i])
        news[j] = Math.min(s[i]*2.0f, 1.0f);
      else
        news[j] = s[i];
      if (!split[i])
      {
        j++;
        continue;
      }
      if (method == Mesh.NO_SMOOTHING)
        newpos[j+1] = v[i].plus(v[p3]).times(0.5);
      else if (method == Mesh.INTERPOLATING)
      {
        if (i < v.length-2)
          p4 = i+2;
        else
        {
          if (theCurve.isClosed())
            p4 = (i+2)%v.length;
          else
            p4 = v.length-1;
        }
        newpos[j+1] = Curve.calcInterpPoint(v, s, p1, i, p3, p4);
      }
      else
        newpos[j+1] = v[i].plus(v[p3]).times(0.5);
      news[j+1] = 1.0f;
      newsel[j+1] = true;
      j += 2;
    }
    if (!theCurve.isClosed())
    {
      newpos[0] = v[0];
      newpos[j] = v[i];
      news[j] = s[i];
      newsel[j] = selected[i];
    }
    setUndoRecord(new UndoRecord(this, false, UndoRecord.COPY_OBJECT, new Object [] {theCurve, theCurve.duplicate()}));
    theCurve.setShape(newpos, news);
    setSelection(newsel);
  }

  public void setSmoothnessCommand()
  {
    final Curve theCurve = (Curve) objInfo.getObject();
    Curve oldCurve = (Curve) theCurve.duplicate();
    final MeshVertex vt[] = theCurve.getVertices();
    final float s[] = theCurve.getSmoothness();
    float value;
    final ValueSlider smoothness;
    int i;
    
    for (i = 0; i < selected.length && !selected[i]; i++);
    if (i == selected.length)
      return;
    value = 0.001f * (Math.round(s[i]*1000.0f));
    smoothness = new ValueSlider(0.0, 1.0, 100, (double) value);
    smoothness.addEventLink(ValueChangedEvent.class, new Object() {
      void processEvent()
      {
        float sm = (float) smoothness.getValue();
        float news[] = new float [vt.length];
        for (int i = 0; i < selected.length; i++)
          news[i] = selected[i] ? sm : s[i];
        theCurve.setSmoothness(news);
        objectChanged();
        for (int i = 0; i < theView.length; i++)
          theView[i].repaint();
      }
    } );
    ComponentsDialog dlg = new ComponentsDialog(this, Translate.text("setPointSmoothness"), new Widget [] {smoothness},
            new String [] {Translate.text("Smoothness")});
    if (dlg.clickedOk())
      setUndoRecord(new UndoRecord(this, false, UndoRecord.COPY_OBJECT, new Object [] {theCurve, oldCurve}));
    else
    {
      theCurve.copyObject(oldCurve);
      objectChanged();
      for (int j = 0; j < theView.length; j++)
        theView[j].repaint();
    }
  }

  void setSmoothingMethod(int method)
  {
    Curve theCurve = (Curve) objInfo.getObject();

    setUndoRecord(new UndoRecord(this, false, UndoRecord.COPY_OBJECT, new Object [] {theCurve, theCurve.duplicate()}));
    for (int i = 0; i < smoothItem.length; i++)
      smoothItem[i].setState(false);
    if (method == Mesh.NO_SMOOTHING)
      smoothItem[0].setState(true);
    else if (method == Mesh.INTERPOLATING)
      smoothItem[1].setState(true);
    else
      smoothItem[2].setState(true);
    theCurve.setSmoothingMethod(method);    
    objectChanged();
    for (int i = 0; i < theView.length; i++)
      theView[i].repaint();
  }

  public void toggleClosedCommand()
  {
    Curve theCurve = (Curve) objInfo.getObject();

    setUndoRecord(new UndoRecord(this, false, UndoRecord.COPY_OBJECT, new Object [] {theCurve, theCurve.duplicate()}));
    if (theCurve.isClosed())
    {
      theCurve.setClosed(false);
      meshMenuItem[6].setText(Translate.text("menu.closedEnds"));
    }
    else
    {
      theCurve.setClosed(true);
      meshMenuItem[6].setText(Translate.text("menu.openEnds"));
    }
    setMesh(theCurve);
    for (int i = 0; i < theView.length; i++)
      theView[i].repaint();
  }

  /** Given a list of deltas which will be added to the selected vertices, calculate the
      corresponding deltas for the unselected vertices according to the mesh tension. */
  
  public void adjustDeltas(Vec3 delta[])
  {
    int dist[] = getSelectionDistance(), count[] = new int [delta.length];
    Curve theCurve = (Curve) objInfo.getObject();
    int maxDistance = getTensionDistance();
    double tension = getMeshTension(), scale[] = new double [maxDistance+1];

    for (int i = 0; i < delta.length; i++)
      if (dist[i] != 0)
        delta[i].set(0.0, 0.0, 0.0);
    for (int i = 0; i < maxDistance; i++)
    {
      for (int j = 0; j < count.length; j++)
        count[j] = 0;
      for (int j = 0; j < dist.length-1; j++)
      {
        if (dist[j] == i && dist[j+1] == i+1)
        {
          count[j+1]++;
          delta[j+1].add(delta[j]);
        }
        else if (dist[j+1] == i && dist[j] == i+1)
        {
          count[j]++;
          delta[j].add(delta[j+1]);
        }
      }
      if (theCurve.isClosed())
      {
        if (dist[0] == i && dist[dist.length-1] == i+1)
        {
          count[dist.length-1]++;
          delta[dist.length-1].add(delta[0]);
        }
        else if (dist[dist.length-1] == i && dist[0] == i+1)
        {
          count[0]++;
          delta[0].add(delta[dist.length-1]);
        }
      }
      for (int j = 0; j < count.length; j++)
        if (count[j] > 1)
          delta[j].scale(1.0/count[j]);
    }
    for (int i = 0; i < scale.length; i++)
      scale[i] = Math.pow((maxDistance-i+1.0)/(maxDistance+1.0), tension);
    for (int i = 0; i < delta.length; i++)
      if (dist[i] > 0)
        delta[i].scale(scale[dist[i]]);
  }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.