private void getStateID(SB sb) { V3 v1 = new V3(); for (Ellipsoid ellipsoid : simpleEllipsoids.values()) { Tensor t = ellipsoid.tensor; if (!ellipsoid.isValid || t == null) continue; sb.append(" Ellipsoid ID ") .append(ellipsoid.id) .append(" modelIndex ") .appendI(t.modelIndex) .append(" center ") .append(Escape.eP(ellipsoid.center)) .append(" axes"); for (int i = 0; i < 3; i++) { v1.setT(t.eigenVectors[i]); v1.scale(ellipsoid.lengths[i]); sb.append(" ").append(Escape.eP(v1)); } sb.append(" " + getColorCommandUnk("", ellipsoid.colix, translucentAllowed)); if (ellipsoid.options != null) sb.append(" options ").append(PT.esc(ellipsoid.options)); if (!ellipsoid.isOn) sb.append(" off"); sb.append(";\n"); } }
/** * Cartoon meshes are triangulated objects. * * @param i * @param madBeg * @param madMid * @param madEnd * @param aspectRatio * @param tension * @return true if deferred rendering is required due to normals averaging */ private boolean createMesh( int i, int madBeg, int madMid, int madEnd, float aspectRatio, int tension) { setNeighbors(i); if (controlPoints[i].distanceSquared(controlPoints[iNext]) == 0) return false; // options: // isEccentric == not just a tube boolean isEccentric = (aspectRatio != 1 && wingVectors != null); // isFlatMesh == using mesh even for hermiteLevel = 0 (for exporters) boolean isFlatMesh = (aspectRatio == 0); // isElliptical == newer cartoonFancy business boolean isElliptical = (cartoonsFancy || hermiteLevel >= 6); // parameters: int nHermites = (hermiteLevel + 1) * 2 + 1; // 5 for hermiteLevel = 1; 13 for hermitelevel 5 int nPer = (isFlatMesh ? 4 : (hermiteLevel + 1) * 4 - 2); // 6 for hermiteLevel 1; 22 for hermiteLevel 5 float angle = (float) ((isFlatMesh ? Math.PI / (nPer - 1) : 2 * Math.PI / nPer)); Mesh mesh = meshes[i] = new Mesh().mesh1("mesh_" + shapeID + "_" + i, (short) 0, i); boolean variableRadius = (madBeg != madMid || madMid != madEnd); // control points and vectors: if (controlHermites == null || controlHermites.length < nHermites + 1) { controlHermites = new P3[nHermites + 1]; } GData.getHermiteList( tension, controlPoints[iPrev], controlPoints[i], controlPoints[iNext], controlPoints[iNext2], controlPoints[iNext3], controlHermites, 0, nHermites, true); // wing hermites determine the orientation of the cartoon if (wingHermites == null || wingHermites.length < nHermites + 1) { wingHermites = new V3[nHermites + 1]; } wing.setT(wingVectors[iPrev]); if (madEnd == 0) wing.scale(2.0f); // adds a flair to an arrow GData.getHermiteList( tension, wing, wingVectors[i], wingVectors[iNext], wingVectors[iNext2], wingVectors[iNext3], wingHermites, 0, nHermites, false); // } // radius hermites determine the thickness of the cartoon float radius1 = madBeg / 2000f; float radius2 = madMid / 2000f; float radius3 = madEnd / 2000f; if (variableRadius) { if (radiusHermites == null || radiusHermites.length < ((nHermites + 1) >> 1) + 1) { radiusHermites = new P3[((nHermites + 1) >> 1) + 1]; } ptPrev.set(radius1, radius1, 0); pt.set(radius1, radius2, 0); pt1.set(radius2, radius3, 0); ptNext.set(radius3, radius3, 0); // two for the price of one! GData.getHermiteList( 4, ptPrev, pt, pt1, ptNext, ptNext, radiusHermites, 0, (nHermites + 1) >> 1, true); } // now create the cartoon polygon int nPoints = 0; int iMid = nHermites >> 1; int kpt1 = (nPer + 2) / 4; int kpt2 = (3 * nPer + 2) / 4; int mode = (!isEccentric ? MODE_TUBE : isFlatMesh ? MODE_FLAT : isElliptical ? MODE_ELLIPTICAL : MODE_NONELLIPTICAL); boolean useMat = (mode == MODE_TUBE || mode == MODE_NONELLIPTICAL); for (int p = 0; p < nHermites; p++) { norm.sub2(controlHermites[p + 1], controlHermites[p]); float scale = (!variableRadius ? radius1 : p < iMid ? radiusHermites[p].x : radiusHermites[p - iMid].y); wing.setT(wingHermites[p]); wing1.setT(wing); switch (mode) { case MODE_FLAT: // hermiteLevel = 0 and not exporting break; case MODE_ELLIPTICAL: // cartoonFancy wing1.cross(norm, wing); wing1.normalize(); wing1.scale(wing.length() / aspectRatio); break; case MODE_NONELLIPTICAL: // older nonelliptical hermiteLevel > 0 wing.scale(2 / aspectRatio); wing1.sub(wing); break; case MODE_TUBE: // not helix or sheet wing.cross(wing, norm); wing.normalize(); break; } wing.scale(scale); wing1.scale(scale); if (useMat) { aa.setVA(norm, angle); mat.setAA(aa); } pt1.setT(controlHermites[p]); float theta = (isFlatMesh ? 0 : angle); for (int k = 0; k < nPer; k++, theta += angle) { if (useMat && k > 0) mat.rotate(wing); switch (mode) { case MODE_FLAT: wingT.setT(wing1); wingT.scale((float) Math.cos(theta)); break; case MODE_ELLIPTICAL: wingT.setT(wing1); wingT.scale((float) Math.sin(theta)); wingT.scaleAdd2((float) Math.cos(theta), wing, wingT); break; case MODE_NONELLIPTICAL: wingT.setT(wing); if (k == kpt1 || k == kpt2) wing1.scale(-1); wingT.add(wing1); break; case MODE_TUBE: wingT.setT(wing); break; } pt.add2(pt1, wingT); mesh.addV(pt, true); } if (p > 0) { int nLast = (isFlatMesh ? nPer - 1 : nPer); for (int k = 0; k < nLast; k++) { // draw the triangles of opposing quads congruent, so they won't clip // esp. for high ribbonAspectRatio values int a = nPoints - nPer + k; int b = nPoints - nPer + ((k + 1) % nPer); int c = nPoints + ((k + 1) % nPer); int d = nPoints + k; if (k < nLast / 2) mesh.addQuad(a, b, c, d); else mesh.addQuad(b, c, d, a); } } nPoints += nPer; } if (!isFlatMesh) { int nPointsPreCap = nPoints; // copying vertices here so that the caps are not connected to the rest of // the mesh preventing light leaking around the sharp edges if (doCap0) { for (int l = 0; l < nPer; l++) mesh.addV(mesh.vs[l], true); nPoints += nPer; for (int k = hermiteLevel * 2; --k >= 0; ) mesh.addQuad( nPoints - nPer + k + 2, nPoints - nPer + k + 1, nPoints - nPer + (nPer - k) % nPer, nPoints - k - 1); } if (doCap1) { for (int l = 0; l < nPer; l++) mesh.addV(mesh.vs[nPointsPreCap - nPer + l], true); nPoints += nPer; for (int k = hermiteLevel * 2; --k >= 0; ) mesh.addQuad( nPoints - k - 1, nPoints - nPer + (nPer - k) % nPer, nPoints - nPer + k + 1, nPoints - nPer + k + 2); } } meshReady[i] = true; adjustCartoonSeamNormals(i, nPer); mesh.setVisibilityFlags(1); return true; }