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.DoubleBinaryOperator; 014import java.util.function.ToDoubleBiFunction; // could use this instead 015//https://docs.oracle.com/javase/8/docs/api/java/util/function/package-summary.html 016 017/** 018 Create a wireframe model of a parametric surface in space. 019<p> 020 See <a href="https://en.wikipedia.org/wiki/Parametric_surface" target="_top"> 021 https://en.wikipedia.org/wiki/Parametric_surface</a> 022 023 @see ParametricCurve 024*/ 025public class ParametricSurface extends Model implements MeshMaker 026{ 027 public final DoubleBinaryOperator x; 028 public final DoubleBinaryOperator y; 029 public final DoubleBinaryOperator z; 030 public final double s1; 031 public final double s2; 032 public final double t1; 033 public final double t2; 034 public final int n; 035 public final int k; 036 037 /** 038 Create a graph of the function with the following formula, 039 <pre>{@code 040 f(x,z) = sin(PI*x) * sin(PI*z) 041 }</pre> 042 as a parametric surface. 043 */ 044 public ParametricSurface() 045 { 046 this((s,t) -> Math.sin(Math.PI*s) * Math.sin(Math.PI*t), 047 -1.0, 1.0, -1.0, 1.0, 048 49, 49); 049 } 050 051 052 /** 053 Create a graph of a function of two variables 054 {@code y = f(x, z)} as a parametric surface with 055 the given parameter ranges in the {@code x} and 056 {@code z} directions. 057 058 @param f function of x and z 059 @param x1 beginning value of x-parameter range 060 @param x2 ending value of x-parameter range 061 @param z1 beginning value of y-parameter range 062 @param z2 ending value of z-parameter range 063 @param n number of mesh lines in x-range 064 @param k number of mesh lines in y-range 065 @throws IllegalArgumentException if {@code n} is less than 2 066 @throws IllegalArgumentException if {@code k} is less than 2 067 */ 068 public ParametricSurface(final DoubleBinaryOperator f, 069 final double x1, final double x2, 070 final double z1, final double z2, 071 final int n, final int k) 072 { 073 this((x,z) -> x, f, (x,z) -> z, x1, x2, z1, z2, n, k); 074 } 075 076 077 /** 078 Create a parametric surface in space, 079 <pre>{@code 080 x = x(s,t) 081 y = y(s,t) 082 z = z(s,t) 083 }</pre> 084 with the parameters {@code s} and {@code t} having 085 the given parameter ranges and the given number of 086 mesh lines in each parametric direction. 087 088 @param x component function in the x-direction 089 @param y component function in the y-direction 090 @param z component function in the z-direction 091 @param s1 beginning value of first parameter range 092 @param s2 ending value of first parameter range 093 @param t1 beginning value of second parameter range 094 @param t2 ending value of second parameter range 095 @param n number of mesh lines in first range 096 @param k number of mesh lines in second range 097 @throws IllegalArgumentException if {@code n} is less than 2 098 @throws IllegalArgumentException if {@code k} is less than 2 099 */ 100 public ParametricSurface(final DoubleBinaryOperator x, 101 final DoubleBinaryOperator y, 102 final DoubleBinaryOperator z, 103 final double s1, final double s2, 104 final double t1, final double t2, 105 final int n, final int k) 106 { 107 this(x, y, z, s1, s2, t1, t2, n, k, 108 String.format("Parametric Surface(%d,%d)", n, k)); 109 } 110 111 112 /** 113 Create a parametric surface in space, 114 <pre>{@code 115 x = x(s,t) 116 y = y(s,t) 117 z = z(s,t) 118 }</pre> 119 with the parameters {@code s} and {@code t} having 120 the given parameter ranges and the given number of 121 mesh lines in each parametric direction. 122 123 @param x component function in the x-direction 124 @param y component function in the y-direction 125 @param z component function in the z-direction 126 @param s1 beginning value of first parameter range 127 @param s2 ending value of first parameter range 128 @param t1 beginning value of second parameter range 129 @param t2 ending value of second parameter range 130 @param n number of mesh lines in first range 131 @param k number of mesh lines in second range 132 @param name {@link String} name for this surface 133 @throws IllegalArgumentException if {@code n} is less than 2 134 @throws IllegalArgumentException if {@code k} is less than 2 135 */ 136 public ParametricSurface(final DoubleBinaryOperator x, 137 final DoubleBinaryOperator y, 138 final DoubleBinaryOperator z, 139 final double s1, final double s2, 140 final double t1, final double t2, 141 final int n, final int k, 142 String name) 143 { 144 super(name); 145 146 if (n < 2) 147 throw new IllegalArgumentException("n must be greater than 1"); 148 if (k < 2) 149 throw new IllegalArgumentException("k must be greater than 1"); 150 151 this.x = x; 152 this.y = y; 153 this.z = z; 154 this.s1 = s1; 155 this.s2 = s2; 156 this.t1 = t1; 157 this.t2 = t2; 158 this.n = n; 159 this.k = k; 160 161 // Create the surface's geometry. 162 163 final double deltaS = (s2 - s1) / (n - 1), // lines of latitude (dy) 164 deltaT = (t2 - t1) / (k - 1); // lines of longitude (dx) 165 166 // An array of vertices to be used to create the line segments. 167 final Vertex[][] v = new Vertex[n][k]; 168 169 // Create all the vertices. 170 for (int i = 0; i < n; ++i) // choose a line of latitude 171 { 172 for (int j = 0; j < k; ++j) // choose a line of longitude 173 { 174 v[i][j] = new Vertex(x.applyAsDouble(s1 + i*deltaS, t1 + j*deltaT), 175 y.applyAsDouble(s1 + i*deltaS, t1 + j*deltaT), 176 z.applyAsDouble(s1 + i*deltaS, t1 + j*deltaT)); 177 } 178 } 179 180 // Add all of the vertices to this model. 181 for (int i = 0; i < n; ++i) 182 { 183 for (int j = 0; j < k; ++j) 184 { 185 addVertex( v[i][j] ); 186 } 187 } 188 189 // Create the horizontal line segments. 190 for (int i = 0; i < n; ++i) // choose a line of latitude 191 { 192 for (int j = 0; j < k - 1; ++j) // choose a line of longitude 193 { // v[i][j] v[i][j+1] 194 addPrimitive(new LineSegment( (i * k) + j, (i * k) + (j+1) )); 195 } 196 } 197 198 // Create the vertical line segments. 199 for (int j = 0; j < k; ++j) // choose a line of longitude 200 { 201 for (int i = 0; i < n - 1; ++i) // choose a line of latitude 202 { // v[i][j] v[i+1][j] 203 addPrimitive(new LineSegment( (i * k) + j, ((i+1) * k) + j )); 204 } 205 } 206 } 207 208 209 210 // Implement the MeshMaker interface (three methods). 211 @Override public int getHorzCount() {return n;} 212 213 @Override public int getVertCount() {return k;} 214 215 @Override 216 public ParametricSurface remake(final int n, final int k) 217 { 218 return new ParametricSurface(this.x, this.y, this.z, 219 this.s1, this.s2, 220 this.t1, this.t2, 221 n, k); 222 } 223}//ParametricSurface