001/*
002 * Renderer 4. The MIT License.
003 * Copyright (c) 2022 rlkraft@pnw.edu
004 * See LICENSE for details.
005*/
006
007package renderer.scene.util;
008
009import renderer.scene.*;
010import renderer.scene.primitives.*;
011
012import java.util.ArrayList;
013import java.awt.Color;
014
015/**
016   Convert a {@link Model} object into a point cloud {@link Model}.
017<p>
018   See <a href="https://en.wikipedia.org/wiki/Point_cloud" target="_top">
019                https://en.wikipedia.org/wiki/Point_cloud</a>
020*/
021public class PointCloud
022{
023   /**
024      A static factory method that converts a given {@link Model}
025      into a {@link Model} made up of only {@link Point} primitives.
026
027      @param model  {@link Model} to convert into a point cloud
028      @return a {@link Model} that is a point cloud version of the input {@link Model}
029      @throws NullPointerException if {@code model} is {@code null}
030   */
031   public static Model make(final Model model)
032   {
033      return make(model, 0); // set the point size to 0
034   }
035
036
037   /**
038      A static factory method that converts a given {@link Model}
039      into a {@link Model} made up of only {@link Point} primitives.
040
041      @param model      {@link Model} to convert into a point cloud
042      @param pointSize  integer diameter of the rasterized points
043      @return a {@link Model} that is a point cloud version of the input {@link Model}
044      @throws NullPointerException if {@code model} is {@code null}
045      @throws IllegalArgumentException if {@code pointSize} is less than 0
046   */
047   public static Model make(final Model model, final int pointSize)
048   {
049      if (null == model)
050         throw new NullPointerException("model must not be null");
051      if (pointSize < 0)
052         throw new IllegalArgumentException("pointSize must be greater than or equal to 0");
053
054      final Model pointCloud = new Model(new ArrayList<Vertex>(model.vertexList),
055                                         new ArrayList<>(), // empty primitiveList
056                                         new ArrayList<Color>(model.colorList),
057                                         "PointCloud: " + model.name,
058                                         model.visible);
059
060      // Find the vertices that are being used by a primitive.
061      final boolean[] vIndices = new boolean[model.vertexList.size()];
062      final     int[] cIndices = new     int[model.vertexList.size()];
063
064      for (final Primitive p : model.primitiveList)
065      {
066         for (int i = 0; i < p.vIndexList.size(); ++i)
067         {
068            vIndices[p.vIndexList.get(i)] = true;
069            cIndices[p.vIndexList.get(i)] = p.cIndexList.get(i);
070         }
071      }
072
073      // Create a Point for each vertex that is used by some primitive.
074      for (int i = 0; i < vIndices.length; ++i)
075      {
076         if ( vIndices[i] )
077         {
078            pointCloud.addPrimitive( new Point(i, cIndices[i]) );
079         }
080      }
081
082      // Set the radius for each new Point primitive.
083      for (final Primitive p : pointCloud.primitiveList)
084      {
085         ((Point)p).radius = pointSize;;
086      }
087
088      return pointCloud;
089   }
090
091
092
093   // Private default constructor to enforce noninstantiable class.
094   // See Item 4 in "Effective Java", 3rd Ed, Joshua Bloch.
095   private PointCloud() {
096      throw new AssertionError();
097   }
098}