001/*
002 * Renderer Models. The MIT License.
003 * Copyright (c) 2022 rlkraft@pnw.edu
004 * See LICENSE for details.
005*/
006
007package renderer.models_L;
008
009import renderer.scene.*;
010import renderer.scene.primitives.*;
011import renderer.scene.util.MeshMaker;
012
013import java.util.function.DoubleFunction;
014import java.util.function.ToDoubleFunction;    // could use this instead
015import java.util.function.DoubleUnaryOperator; // could use this instead
016//https://docs.oracle.com/javase/8/docs/api/java/util/function/package-summary.html
017
018/**
019   Create a wireframe model of a parametric curve in space.
020<p>
021   See <a href="https://en.wikipedia.org/wiki/Parametric_equation" target="_top">
022                https://en.wikipedia.org/wiki/Parametric_equation</a>
023
024   @see ParametricSurface
025*/
026public class ParametricCurve extends Model implements MeshMaker
027{
028   public final DoubleFunction<Double> x;
029   public final DoubleFunction<Double> y;
030   public final DoubleFunction<Double> z;
031   public final double t1;
032   public final double t2;
033   public final int n;
034
035   /**
036      Create a trefoil knot as a parametric curve in space.
037   <p>
038      See <a href="https://en.wikipedia.org/wiki/Trefoil_knot#Descriptions" target="_top">
039                   https://en.wikipedia.org/wiki/Trefoil_knot#Descriptions</a>
040   */
041   public ParametricCurve()
042   {
043      this(t ->  0.5*Math.sin(t) + Math.sin(2*t),
044           t ->  0.5*Math.cos(t) - Math.cos(2*t),
045           t -> -0.5*Math.sin(3*t),
046           0, 2*Math.PI, 60);
047   }
048
049
050   /**
051      Create a parametric curve in the xy-plane,
052      <pre>{@code
053         x = x(t)
054         y = y(t)
055      }</pre>
056      with the parameter {@code  t} having the given parameter
057      range and the given number of line segments.
058
059      @param x   component function in the x-direction
060      @param y   component function in the y-direction
061      @param t1  beginning value of parameter range
062      @param t2  ending value of parameter range
063      @param n   number of line segments in the curve
064      @throws IllegalArgumentException if {@code n} is less than 1
065   */
066   public ParametricCurve(final DoubleFunction<Double> x,
067                          final DoubleFunction<Double> y,
068                          final double t1, final double t2,
069                          final int n)
070   {
071      this(x, y, t->0.0, t1, t2, n);
072   }
073
074
075   /**
076      Create a parametric curve in space,
077      <pre>{@code
078         x = x(t)
079         y = y(t)
080         z = z(t)
081      }</pre>
082      with the parameter {@code t} having the given parameter
083      range and the given number of line segments.
084
085      @param x   component function in the x-direction
086      @param y   component function in the y-direction
087      @param z   component function in the z-direction
088      @param t1  beginning value of parameter range
089      @param t2  ending value of parameter range
090      @param n   number of line segments in the curve
091      @throws IllegalArgumentException if {@code n} is less than 1
092   */
093   public ParametricCurve(final DoubleFunction<Double> x,
094                          final DoubleFunction<Double> y,
095                          final DoubleFunction<Double> z,
096                          final double t1, final double t2,
097                          final int n)
098   {
099      super(String.format("Parametric Curve(%d)", n));
100
101      if (n < 1)
102         throw new IllegalArgumentException("n must be greater than 0");
103
104      this.x = x;
105      this.y = y;
106      this.z = z;
107      this.t1 = t1;
108      this.t2 = t2;
109      this.n = n;
110
111      // Create the curve's geometry.
112      final double deltaT = (t2 - t1) / n;
113
114      for (int i = 0; i < n + 1; ++i)
115      {
116         addVertex( new Vertex( x.apply(t1 + i * deltaT),
117                                y.apply(t1 + i * deltaT),
118                                z.apply(t1 + i * deltaT) ) );
119      }
120
121      for (int i = 0; i < n; ++i)
122      {
123         addPrimitive(new LineSegment(i, i+1));
124      }
125   }
126
127
128
129   // Implement the MeshMaker interface (three methods).
130   @Override public int getHorzCount() {return n;}
131
132   @Override public int getVertCount() {return n;}
133
134   @Override
135   public ParametricCurve remake(final int n, final int k)
136   {
137      final int newN;
138      if (n != this.n)
139         newN = n;
140      else
141         newN = k;
142
143      return new ParametricCurve(this.x, this.y, this.z,
144                                 this.t1, this.t2,
145                                 newN);
146   }
147}//ParametricCurve