Package ca.eandb.jmist.framework.scene

Source Code of ca.eandb.jmist.framework.scene.BrilliantCut

/**
* Java Modular Image Synthesis Toolkit (JMIST)
* Copyright (C) 2008-2013 Bradley W. Kimmel
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
package ca.eandb.jmist.framework.scene;

import ca.eandb.jmist.framework.SceneElement;
import ca.eandb.jmist.framework.geometry.MeshBuilder;
import ca.eandb.jmist.math.Basis3;
import ca.eandb.jmist.math.MathUtil;
import ca.eandb.jmist.math.Plane3;
import ca.eandb.jmist.math.Point3;
import ca.eandb.jmist.math.Ray3;
import ca.eandb.jmist.math.Vector3;

/**
* Static methods for creating the geometry of a brilliant cut diamond, as
* defined by the Gemological Institute of America (GIA).
* @see <a href="http://www.gia.edu/diamondcut/05_diamond_anatomy.html">Diamond anatomy</a>
* @author Brad Kimmel
*/
public final class BrilliantCut {

  /**
   * Specification of the cut geometry, as described by the Gemological
   * Institute of America (GIA).  See
   * <a href="http://www.gia.edu/diamondcut/05_diamond_anatomy.html">
   * http://www.gia.edu/diamondcut/05_diamond_anatomy.html</a>.
   */
  public static final class Info {

    /**
     * Distance measured between two opposing points along the girdle's
     * outline.<br>
     * <br>
     * This field is required.
     */
    public double diameter = 1.0;

    /**
     * Total depth (measured from table plane to culet) relative to the
     * average diameter.<br>
     * <br>
     * This field is required if {@link #relMaxGirdleThickness} is not
     * specified.  If specified, this value must be at least the sum of
     * {@link #relCrownHeight} and {@link #relPavilionDepth}.
     * @see #relMaxGirdleThickness
     * @see #relCrownHeight
     * @see #relPavilionDepth
     */
    public double relTotalDepth = Double.NaN; // out

    /**
     * Avg table size relative to the avg diameter.  Table size is measured
     * from bezel point to bezel point.<br>
     * <br>
     * This field is required.
     */
    public double relTableSize = 0.573;

    /**
     * The angle of the bezel facet plane relative to the table plane.<br>
     * <br>
     * This field is required if {@link #relCrownHeight} is not specified.
     * @see #relCrownHeight
     */
    public double crownAngle = Math.toRadians(34.78);

    /**
     * Average crown height relative to the average diameter. Crown height
     * is measured from the table plane to the intersection of the bezel
     * facet with the girdle.<br>
     * <br>
     * This field is required if {@link #crownAngle} is not specified.
     * @see #crownAngle
     */
    public double relCrownHeight = Double.NaN; // out

    /**
     * The angle of the main facet plane relative to the table plane.<br>
     * <br>
     * This field is required if {@link #relPavilionDepth} is not
     * specified.
     * @see #relPavilionDepth
     */
    public double pavilionAngle = Math.toRadians(40.7);

    /**
     * Average pavilion depth relative to the average diameter. Pavilion
     * depth is measured from the culet to the intersection of the pavilion
     * main facet with the girdle.<br>
     * <br>
     * This field is required if {@link #pavilionAngle} is not specified.
     * @see #pavilionAngle
     */
    public double relPavilionDepth = Double.NaN; // out

    /**
     * The horizontally projected distance from the point of the star facet
     * to the edge of the table, relative to the distance between the table
     * edge and the girdle edge.<br>
     * <br>
     * This field is required.
     */
    public double relStarLength = 0.523;

    /**
     * The horizontally projected distance from the point where two
     * pavilion mains meet to the closest edge of the girdle, relative to
     * the distance between the girdle edge and the center of the
     * culet.<br>
     * <br>
     * This field is required.
     */
    public double relLowerHalfLength = 0.761;

    /**
     * The thickness of the girdle at its thickest point (between the top
     * of the pavilion main and the bottom of the crown bezel) relative to
     * the average diameter.<br>
     * <br>
     * This field is required if {@link #relTotalDepth} is not specified.
     * @see #relTotalDepth
     */
    public double relMaxGirdleThickness = 0.021;

    /**
     * Avg culet size relative to the avg diameter.<br>
     * <br>
     * This field is required.
     */
    public double relCuletSize = 0.004;

  };

  /**
   * Creates a <code>SceneElement</code> representing the geometry of a
   * brilliant cut diamond using the default specifications.
   * @return A <code>SceneElement</code> representing the geometry of a
   *     brilliant cut diamond.
   */
  public static SceneElement createGeometry() {
    return createMeshBuilder().createGeometry();
  }

  /**
   * Creates a <code>SceneElement</code> representing the geometry of a
   * brilliant cut diamond using the provided specifications.
   * @param cut The <code>BrilliantCut.Info</code> describing the cut
   *     geometry.  Information not provided will be computed and the
   *     corresponding fields will be populated.
   * @return A <code>SceneElement</code> representing the geometry of a
   *     brilliant cut diamond.
   * @throws IllegalArgumentException If insufficient information is
   *     provided to determine the cut geometry.
   */
  public static SceneElement createGeometry(Info cut) {
    return createMeshBuilder(cut).createGeometry();
  }

  /**
   * Creates a <code>MeshBuilder</code> prepared with the geometry of a
   * brilliant cut diamond using the default specifications.
   * @return A <code>SceneElement</code> representing the geometry of a
   *     brilliant cut diamond.
   */
  public static MeshBuilder createMeshBuilder() {
    return createMeshBuilder(new Info());
  }

  /**
   * Creates a <code>MeshBuilder</code> prepared with the geometry of a
   * brilliant cut diamond using the provided specifications.
   * @param cut The <code>BrilliantCut.Info</code> describing the cut
   *     geometry.  Information not provided will be computed and the
   *     corresponding fields will be populated.
   * @return A <code>SceneElement</code> representing the geometry of a
   *     brilliant cut diamond.
   * @throws IllegalArgumentException If insufficient information is
   *     provided to determine the cut geometry.
   */
  public static MeshBuilder createMeshBuilder(Info cut) {

    validateInfo(cut);

    double radius = 0.5 * cut.diameter;
    double crownHeight = cut.relCrownHeight * cut.diameter;

    double halfTableDiag = radius * cut.relTableSize;
    double halfTableWidth = halfTableDiag * Math.cos(Math.PI / 8.0);

    double kiteBottom = -cut.relCrownHeight * cut.diameter;
    double lowerMainTop = kiteBottom - cut.relMaxGirdleThickness
        * cut.diameter;

    double pavilionPointDepth = radius * Math.tan(cut.pavilionAngle);

    double culetBottom = -cut.diameter * cut.relTotalDepth;

    double starPointRadius = halfTableWidth * (1.0 - cut.relStarLength)
        + radius * cut.relStarLength;
    double lowerGirdleInner = radius * (1.0 - cut.relLowerHalfLength);

    MeshBuilder b = MeshBuilder.fromCylinder(radius, -culetBottom, 64);
    b.translate(new Vector3(0, 0, culetBottom));

    for (int i = 0; i < 8; i++) {

      double theta0 = 2.0 * Math.PI * ((double) i) / 8.0;
      double theta1 = 2.0 * Math.PI * (((double) i) + 0.5) / 8.0;
      double theta2 = 2.0 * Math.PI * (((double) i) + 1.0) / 8.0;
      double theta3 = 2.0 * Math.PI * (((double) i) + 1.5) / 8.0;
      double cost0 = Math.cos(theta0);
      double sint0 = Math.sin(theta0);
      double cost1 = Math.cos(theta1);
      double sint1 = Math.sin(theta1);
      double cost2 = Math.cos(theta2);
      double sint2 = Math.sin(theta2);
      double cost3 = Math.cos(theta3);
      double sint3 = Math.sin(theta3);

      Point3 p = new Point3(halfTableDiag * cost1, halfTableDiag * sint1, 0.0);
      Point3 q = new Point3(radius * cost1, radius * sint1, -crownHeight);
      Point3 r;

      Basis3 basis = Basis3.fromUW(p.vectorTo(q), Vector3.K);
      Plane3 planeKite = Plane3.throughPoint(p, basis);

      b.slice(planeKite, true);

      q = new Point3(halfTableDiag * cost3, halfTableDiag * sint3, 0.0);
      r = new Point3(starPointRadius * cost2, starPointRadius * sint2, 0.0);

      Ray3 ray = new Ray3(r, Vector3.K);
      r = ray.pointAt(planeKite.intersect(ray));

      Plane3 planeStar = Plane3.throughPoints(q, p, r);
      b.slice(planeStar, true);

      p = new Point3(radius * cost2, radius * sint2, -crownHeight);
      q = new Point3(radius * cost1, radius * sint1, -crownHeight);

      Plane3 planeGirdle = Plane3.throughPoints(q, p, r);
      b.slice(planeGirdle, true);

      q = new Point3(radius * cost3, radius * sint3, -crownHeight);
      planeGirdle = Plane3.throughPoints(p, q, r);
      b.slice(planeGirdle, true);

      p = new Point3(radius * cost1, radius * sint1, lowerMainTop);
      q = new Point3(0.0, 0.0, lowerMainTop - pavilionPointDepth);
      basis = Basis3.fromUW(p.vectorTo(q), Vector3.NEGATIVE_K);

      Plane3 planeMain = Plane3.throughPoint(p, basis);
      b.slice(planeMain, true);

      q = new Point3(radius * cost0, radius * sint0, lowerMainTop);
      r = new Point3(lowerGirdleInner * cost0, lowerGirdleInner * sint0, 0.0);
      ray = new Ray3(r, Vector3.NEGATIVE_K);
      r = ray.pointAt(planeMain.intersect(ray));

      planeGirdle = Plane3.throughPoints(p, q, r);
      b.slice(planeGirdle, true);

      q = new Point3(radius * cost2, radius * sint2, lowerMainTop);
      r = new Point3(lowerGirdleInner * cost2, lowerGirdleInner * sint2, 0.0);
      ray = new Ray3(r, Vector3.NEGATIVE_K);
      r = ray.pointAt(planeMain.intersect(ray));
      planeGirdle = Plane3.throughPoints(q, p, r);
      b.slice(planeGirdle, true);

    }

    b.mergeVertices(MathUtil.EPSILON);
    return b;

  }

  /**
   * Validates the provided <code>BrilliantCut.Info</code> and populates
   * missing fields.
   * @param cut The <code>BrilliantCut.Info</code> describing the cut
   *     geometry.
   */
  private static void validateInfo(Info cut) {
    if (Double.isNaN(cut.diameter)) {
      throw new IllegalArgumentException("cut.diameter is required");
    }
    if (Double.isNaN(cut.relTableSize)) {
      throw new IllegalArgumentException("cut.relTableSize is required");
    }
    if (Double.isNaN(cut.relStarLength)) {
      throw new IllegalArgumentException("cut.relStarLength is required");
    }
    if (Double.isNaN(cut.relLowerHalfLength)) {
      throw new IllegalArgumentException("cut.relLowerHalfLength is required");
    }
    if (Double.isNaN(cut.relCuletSize)) {
      throw new IllegalArgumentException("cut.relCuletSize is required");
    }
    if (Double.isNaN(cut.pavilionAngle)) {
      if (Double.isNaN(cut.relPavilionDepth)) {
        throw new IllegalArgumentException("cut is underspecified, require cut.relPavilionDepth or cut.pavilionAngle");
      }
      cut.pavilionAngle = Math.atan2(2.0 * cut.relPavilionDepth,
          1.0 - cut.relCuletSize);
    } else if (Double.isNaN(cut.relPavilionDepth)) {
      cut.relPavilionDepth = 0.5 * (1.0 - cut.relCuletSize)
          * Math.tan(cut.pavilionAngle);
    }
    if (Double.isNaN(cut.crownAngle)) {
      if (Double.isNaN(cut.relCrownHeight)) {
        throw new IllegalArgumentException("cut is underspecified, require cut.relCrownHeight or cut.crownAngle");
      }
      cut.crownAngle = Math.atan2(2.0 * cut.relCrownHeight,
          1.0 - cut.relTableSize);
    } else if (Double.isNaN(cut.relCrownHeight)) {
      cut.relCrownHeight = 0.5 * (1.0 - cut.relTableSize)
          * Math.tan(cut.crownAngle);
    }
    if (Double.isNaN(cut.relTotalDepth)) {
      if (Double.isNaN(cut.relMaxGirdleThickness)) {
        throw new IllegalArgumentException("cut is underspecified, require cut.relTotalDepth or cut.relMaxGirdleThickness");
      }
      cut.relTotalDepth = cut.relCrownHeight + cut.relMaxGirdleThickness
          + cut.relPavilionDepth;
    } else if (Double.isNaN(cut.relMaxGirdleThickness)) {
      cut.relMaxGirdleThickness = cut.relTotalDepth - cut.relCrownHeight
          - cut.relPavilionDepth;
      if (cut.relMaxGirdleThickness < 0.0) {
        throw new IllegalArgumentException("cut.relTotalDepth is to small");
      }
    }
  }

}
TOP

Related Classes of ca.eandb.jmist.framework.scene.BrilliantCut

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.