public void TestFailingRealIntersections() { Scientrace.Object3dEnvironment env = DummySources.dEnv(); Scientrace.CylindricalBorder cb = new Scientrace.CylindricalBorder(new Scientrace.Location(0, -0.07, 0), new Scientrace.NonzeroVector(0, -0.57, 0), 0.5); Scientrace.ParabolicMirror pm = new Scientrace.ParabolicMirror(env , Scientrace.PerfectMirror.Instance, new Scientrace.Location(0.354861881099294, -0.57, 0.276718350209505) , new Scientrace.UnitVector(-0.343710964996787, 0.900014703397015, -0.268022958363946), 2, cb); Scientrace.Line tl = new Scientrace.Line(-0.184140645666577, -0.0383229578843359, -0.3, 0.343710966184079, -0.900014705907235, 0.268022948412108); // pm.trf.transform(tl); // Scientrace.IntersectionPoint[] tips = pm.realIntersections(tl); //Assert.AreEqual(new Scientrace.Location(0,0,0), tips[0].loc); Scientrace.VectorTransform trf = pm.trf; Scientrace.Line trfline = trf.transform(tl - pm.loc); //transform location AND direction Scientrace.IntersectionPoint[] ips = pm.baseintersections(trfline); //Scientrace.IntersectionPoint tip; Scientrace.IntersectionPoint[] retips = new Scientrace.IntersectionPoint[2]; for (int ipi = 0; ipi < ips.GetLength(0); ipi++) { if (ips[ipi] == null) { retips[ipi] = null; continue; } //check below removed for performance reasons if (!trfline.throughLocation(ips[ipi].loc, 0.00001)) { string warning = @"ERROR: GOING DOWN! \n baseintersections " + trfline + " FAILED!"; throw new ArgumentOutOfRangeException(warning + ips[ipi].loc.trico() + " not in line " + trfline); } } }
public Scientrace.UnitVector getNorm() { //the VectorTransform this.trf object already produced a cross product over this.u and this.v, //resulting in normalized (unit-vector) norm on the surface. Scientrace.VectorTransform trf = this.getTransform(); return(trf.w.tryToUnitVector()); }
public Scientrace.Vector lineThroughPlaneTLoc(Scientrace.Line line) { Scientrace.VectorTransform trf = this.getTransform(); //coordinates after coordinate-transformation. u->e1, v->e2, loc->eloc //the parallelogram is in the (a*e1, b*e2, 0) plane Scientrace.Vector eloc; eloc = this.geteloc(); //coordinates after coordinate-transformation. line.direction->eld, line.loc->ell Scientrace.Vector eld, ell; eld = trf.transform(line.direction); ell = trf.transform(line.startingpoint); //nbase is the new base when the parallelogram "location" has been substracted from the line-location Scientrace.Vector nbase = ell - eloc; //catch direction eld.z = 0; line is parallel to plane! if (eld.z == 0) { return(null); } double nx, ny, nz; double dfac = (nbase.z / eld.z); nx = nbase.x - (dfac * eld.x); ny = nbase.y - (dfac * eld.y); nz = nbase.z - (dfac * eld.z); //shift plane back to eloc location and return return((new Scientrace.Vector(nx, ny, nz)) + eloc); }
public override Scientrace.VectorTransform getGridTransform(UnitVector nz) { //construction on request if (this.gridtrf == null) { Scientrace.Vector refvec; //vector has to differ at least 1% from the orthogonal orientation. if (Math.Abs(directionlength.toUnitVector().dotProduct(nz)) < 0.99) { refvec = nz; } else { refvec = (nz.rotateAboutX(Math.PI / 2).rotateAboutY(Math.PI / 2)).tryToUnitVector(); if (Math.Abs(directionlength.toUnitVector().dotProduct(nz)) < 0.99) { throw new Exception("WARNING: CrossProducts cannot produce orthogonal lines over two parallel " + "vectors, two vectors that are almost parallel will give a large significance error. FIX DIDN'T WORK, please contact the Scientrace developer."); } } Scientrace.NonzeroVector u, v; u = refvec.crossProduct(directionlength).tryToUnitVector() * this.radius; v = u.crossProduct(directionlength).tryToUnitVector() * this.radius; this.gridtrf = new Scientrace.VectorTransform(u, v, this.directionlength); } return(this.gridtrf); }
public override Scientrace.Location get2DLoc(Scientrace.Location loc) { //Scientrace.VectorTransformOrthonormal trf = new Scientrace.VectorTransformOrthonormal(this.surface.u, this.surface.v); //Scientrace.VectorTransform trf = new Scientrace.VectorTransform(this.surface.u, this.surface.v); Scientrace.VectorTransform trf = new Scientrace.VectorTransform(this.parallelogram.plane.u.normalized(), this.parallelogram.plane.v.normalized()); return(trf.transform(loc - this.parallelogram.plane.loc)); }
public virtual VectorTransform getTransform() { //construction on request if (this.trf == null) { this.trf = this.createNewTransform(); } return(this.trf); }
public Scientrace.VectorTransform getTransform() { //construction on request if (this.trf == null) { this.trf = new Scientrace.VectorTransform(this.u, this.v); } return(this.trf); }
public void TransformMatrixTest() { Scientrace.NonzeroVector u, v; u = new Scientrace.NonzeroVector(2, 3, 4); v = new Scientrace.NonzeroVector(1, 2, 3); Scientrace.VectorTransform foo = new Scientrace.VectorTransform(u, v); Scientrace.Vector tvec = foo.transform(new Scientrace.Vector(1, 1.5, 2)); Assert.AreEqual(tvec.x, 0.5); Assert.AreEqual(tvec.y, 0, 1E-15); Assert.AreEqual(tvec.z, 0, 1E-15); }
public void TransformMatrixAndBackToStringTest() { Scientrace.NonzeroVector u, v; u = new Scientrace.NonzeroVector(5, 2, 4); v = new Scientrace.NonzeroVector(3, 2, 3); Scientrace.VectorTransform foo = new Scientrace.VectorTransform(u, v); Scientrace.Vector orivec = new Scientrace.Vector(5, 1, 2); Scientrace.Vector tvec = foo.transform(orivec); Scientrace.Vector tbackvec = foo.transformback(tvec); Assert.AreEqual(orivec.ToCompactString(), tbackvec.ToCompactString()); }
public void CreateOrtho3rdVector() { Scientrace.UnitVector u, v; u = new Scientrace.UnitVector(2, 3, 4); v = new Scientrace.UnitVector(1, 2, 3); Scientrace.VectorTransform foo = new Scientrace.VectorTransform(u, v); Assert.AreNotEqual(0, u.dotProduct(v)); //, 1E-15); Assert.AreEqual(0, u.dotProduct(foo.w), 1E-15); Assert.AreEqual(0, v.dotProduct(foo.w), 1E-15); }
public void TransformMatrixAndBackTest2() { Scientrace.NonzeroVector u, v; u = new Scientrace.NonzeroVector(2.3, 3.2, 4.23); v = new Scientrace.NonzeroVector(3.14, 2.12, 3); Scientrace.VectorTransform foo = new Scientrace.VectorTransform(u, v); Scientrace.Vector tvec = foo.transform(new Scientrace.Vector(6, 3, 8)); Scientrace.Vector tbackvec = foo.transformback(tvec); Assert.AreEqual(tbackvec.x, 6, 1E-14); Assert.AreEqual(tbackvec.y, 3, 1E-14); Assert.AreEqual(tbackvec.z, 8, 1E-14); }
public void PGramIntersectionAtBaseVectors2() { Scientrace.Location loc = new Scientrace.Location(1, 2, 3); Scientrace.NonzeroVector v1 = new Scientrace.NonzeroVector(2, 5, 7); Scientrace.NonzeroVector v2 = new Scientrace.NonzeroVector(3, 2, 0); Scientrace.Parallelogram pg = new Scientrace.Parallelogram(loc, v1, v2); Scientrace.Vector intersection = pg.plane.lineThroughPlane(new Scientrace.Line(1, 1, -1, 2, 2, 2)); Scientrace.VectorTransform trf = pg.plane.getTransform(); Scientrace.Vector tintersection = trf.transform(intersection - loc); /* when the pgram-plane is shifted through 0,0,0, a transformation of any location in this plane to * the base vectors e1 and e2 (based on v1 and v2) should leave a 3rd coordinate set to zero. */ Assert.AreEqual(tintersection.z, 0, 1E-14); }
public override bool contains(Location tryloc) { Scientrace.VectorTransform trf = this.getTransform(); Scientrace.Vector trfdtryloc = trf.transform(tryloc - this.loc); if (!this.valuewithin(trfdtryloc.x, 0, 1)) { return(false); } if (!this.valuewithin(trfdtryloc.y, 0, 1)) { return(false); } if (!this.valuewithin(trfdtryloc.z, 0, 1)) { return(false); } return(true); }
public override bool contains(Location aLocation) { Scientrace.VectorTransform trf = this.getTransform(); Scientrace.Location tloc = trf.transform(aLocation - this.loc); double ufac, vfac; // ufac is the fraction of "a vector 1,0,0 transformedback.length" of the radius of the cylinder ufac = Vector.x1trbl(trf) / this.radius; vfac = Vector.y1trbl(trf) / this.radius; //checking for exceptions if ((tloc.z < 0) || (tloc.z > 1)) { return(false); //location lies below or above cylinder != within } if (Math.Pow(tloc.x * ufac, 2) + Math.Pow(tloc.y * vfac, 2) > 1) { return(false); //location lies outside the cylinder } return(true); }
/// <summary> /// If a vector 1,0,0 would be transformed back by vectortransform trf this would be its length /// </summary> /// <param name="trf"> /// A <see cref="Scientrace.VectorTransform"/> /// </param> /// <returns> /// A <see cref="System.Double"/> the length of the backtransform of 1,0,0 /// </returns> public static double x1trbl(Scientrace.VectorTransform trf) { return(trf.transformback(Scientrace.Vector.x1vector()).length); }
public override Scientrace.VectorTransform getGridTransform(UnitVector nz) { //construction on request if (this.gridtrf == null) { Scientrace.Vector refvec; //vector has to differ at least 1% from the orthogonal orientation. if (Math.Abs(directionlength.toUnitVector().dotProduct(nz))<0.99) { refvec = nz; } else { refvec = (nz.rotateAboutX(Math.PI/2).rotateAboutY(Math.PI/2)).tryToUnitVector(); if (Math.Abs(directionlength.toUnitVector().dotProduct(nz))<0.99) { throw new Exception("WARNING: CrossProducts cannot produce orthogonal lines over two parallel " + "vectors, two vectors that are almost parallel will give a large significance error. FIX DIDN'T WORK, please contact the Scientrace developer."); } } Scientrace.NonzeroVector u, v; u = refvec.crossProduct(directionlength).tryToUnitVector()*this.radius; v = u.crossProduct(directionlength).tryToUnitVector()*this.radius; this.gridtrf = new Scientrace.VectorTransform(u,v,this.directionlength); } return this.gridtrf; }
public override string exportX3D(Scientrace.Object3dEnvironment env) { int steps = this.exportX3Dgridsteps; Scientrace.AbstractGridBorder cborder = this.cborder; // Scientrace.VectorTransform cbordertrf = cborder.getTransform(); //Generate a rotating grid! Scientrace.VectorTransform cbordertrf = cborder.getGridTransform(this.zaxis); //Console.WriteLine("CBTRFcp: "+cbordertrf.ToCompactString()); /* * stloc: starting location actually representing the center of the border, * from there in the orthonormal direction of this border the collision points with * the parabolic mirror will be found (from -radius to +radius in both transform-directions */ Scientrace.Location stloc = cborder.getOrthoStartCenterLoc();// - cborder.getOthoDirection(); Scientrace.IntersectionPoint[] iparr, iparre, iparrs; Scientrace.Intersection cintr, eintr, sintr; /* * eloc is the location "east" of the current node, sloc "south" for drawing a grid * of course are terms east and south symbolic */ Scientrace.Location eloc, sloc; Scientrace.Line cline, eline, sline; //double r = cborder.getRadius(); double r1 = cborder.getURadius(); double r2 = cborder.getVRadius(); Scientrace.Vector v1 = cbordertrf.u.tryToUnitVector().toVector(); Scientrace.Vector v2 = cbordertrf.v.tryToUnitVector().toVector();; string retstr = ""; Scientrace.X3DGridPoint concentrationpoint = new Scientrace.X3DGridPoint(env, this.getConcentrationPoint(), null, null); retstr = concentrationpoint.x3DSphere(env.radius / 1000, "1 0 1", 0.5); retstr += concentrationpoint.x3DLineTo(this.loc, "1 0 1 1"); for (double ix = 0.5; ix < steps; ix++) { for (double iy = 0.5; iy <= steps; iy++) { /* Drawing a grid of lines in direction "border.orthodirection" to ParabolicMirror, * at every intersection a gridpoint is located. "cline" are those ortho-dir lines */ cline = new Scientrace.Line(((stloc - (v1 * r1) - (v2 * r2)) + (v1 * (r1 * 2 * (ix / steps))) + (v2 * (r2 * 2 * (iy / steps)))).toLocation(), cborder.getOrthoDirection()); // cborder.directionlength.toUnitVector()); /* USE THE "checkborder = false" function below to always * show the grid-points, also outside borders * iparr is the IntersectionPoint at the Parabolic Mirror for the current ix/iy iteration */ iparr = this.realIntersections(cline, true); /* DEBUG INFO foreach (IntersectionPoint ip in iparr) { * if (ip!=null) { * Console.WriteLine("IP AT: "+ip.ToString()); * } else { * Console.WriteLine("NO IP FROM: "+((stloc-(v1*r)-(v2*r))+(v1*(r*2*(ix/steps)))+(v2*(r*2*(iy/steps)))) +" AND "+cborder.getOrthoDirection()); * } * }*/ eline = new Scientrace.Line(((stloc - (v1 * r1) - (v2 * r2)) + (v1 * (r1 * 2 * ((ix + 1) / steps))) + (v2 * (r2 * 2 * (iy / steps)))).toLocation(), cborder.getOrthoDirection()); // cborder.directionlength.toUnitVector()); iparre = this.realIntersections(eline, true); //defining "east" neighbour eintr = new Intersection(eline, iparre, this); if (eintr.intersects) { eloc = eintr.enter.loc; } else { eloc = null; } sline = new Scientrace.Line(((stloc - (v1 * r1) - (v2 * r2)) + (v1 * (r1 * 2 * ((ix) / steps))) + (v2 * (r2 * 2 * ((iy + 1) / steps)))).toLocation(), cborder.getOrthoDirection()); // cborder.directionlength.toUnitVector()); iparrs = this.realIntersections(sline, true); //defining "south" neighbour sintr = new Intersection(sline, iparrs, this); if (sintr.intersects) { sloc = sintr.enter.loc; } else { sloc = null; } /* "central" point * where does line "cline" with intersections "iparr" intersect this object? */ cintr = new Intersection(cline, iparr, this, true); if (cintr.intersects) //add existing gridpoints { if (this.x3dgridspheres) { retstr = retstr + new X3DGridPoint(env, cintr.enter.loc, eloc, sloc).exportX3D(); } else { retstr = retstr + new X3DGridPoint(env, cintr.enter.loc, eloc, sloc).exportX3DnosphereRGBA("0 0 1 1"); } } } } //Console.WriteLine("!!!"+retstr); return(retstr + cborder.exportX3D(env)); }
public Scientrace.IntersectionPoint[] realIntersections(Scientrace.Line traceline, bool checkBorder) { // Scientrace.Line line = traceline-this.loc; //substract loc value from traceline location, //leave direction unchanged //Console.WriteLine("In untransformed world: traceline -> "+traceline.ToString()); //default value should be checkBorder = true; Scientrace.VectorTransform trf = this.trf; Scientrace.Line trfline = trf.transform(traceline - this.loc); //transform location AND direction Scientrace.IntersectionPoint[] ips = this.baseintersections(trfline); Scientrace.IntersectionPoint tip; Scientrace.IntersectionPoint[] retips = new Scientrace.IntersectionPoint[2]; for (int ipi = 0; ipi < ips.GetLength(0); ipi++) { if (ips[ipi] == null) { retips[ipi] = null; continue; } //Console.WriteLine("$$$$$$$$$$$$$$$$$"+ips[ipi].loc+"in?:"+trfline); //Console.WriteLine("___1"); //check below removed for performance reasons /*if (!trfline.throughLocation(ips[ipi].loc, 0.00001)) { * string warning =@"ERROR: GOING DOWN! \n baseintersections "+trfline+" FAILED!"; * throw new ArgumentOutOfRangeException(warning+ips[ipi].loc.trico() + " not in line " + trfline); * } *///This was removed at 20160222 /* else { * Scientrace.Line reflline = trfline.reflectAt(this.baseIntersectionPlane(ips[ipi].loc.x, ips[ipi].loc.y),0); * //Console.WriteLine("___2"); * /*if (!reflline.throughLocation(this.getBaseConcentrationPoint(), 0.000001)) { * //throw new ArgumentOutOfRangeException("CONCENTREER EENS!"); * }*/ // } end of removed check //throw new AccessViolationException("FOO"); //Console.WriteLine("ips["+ipi.ToString()+"]:"+ips[ipi].ToString()); //tip = trf.transformback(ips[ipi])+this.loc; tip = ips[ipi].copy(); tip.loc = trf.transformback(ips[ipi].loc) + this.loc; tip.flatshape.plane = trf.transformback(tip.flatshape.plane); //Console.WriteLine("___3"); /*if (!traceline.throughLocation(tip.loc, 0.0001)) { * throw new ArgumentOutOfRangeException(tip.loc.trico() + " (transformed) not in line " + traceline); * } else { * }*/ if (this.cborder.contains(tip.loc) || !checkBorder) //is this location within the borders between which the //parabolic mirror exists? { retips[ipi] = tip; /* //below for debug purposes only * retips[ipi] = new IntersectionPoint(traceline.startingpoint+ * traceline.direction.toLocation()*(traceline.direction.dotProduct(tip.loc-traceline.startingpoint)) * , tip.plane);*/ } else { //Console.WriteLine(tip.loc.ToString() + " is NOT within the cborder range-> "+this.cborder); retips[ipi] = null; continue; } //Console.WriteLine("trf: "+trf.ToString()); //Console.WriteLine("retips["+ipi.ToString()+"]:"+retips[ipi].ToString()); } return(retips); }
public virtual VectorTransform getTransform() { //construction on request if (this.trf == null) { this.trf = this.createNewTransform(); } return this.trf; }
private Scientrace.NonzeroVector calcTriangleHeightVector(ShadowObject3d sho3d) { Scientrace.Vector length = sho3d.getVector("length"); Scientrace.Vector width = sho3d.getVector("width"); Scientrace.Vector heightdir = sho3d.getVector("heightdir"); double angle = sho3d.getDouble("angle"); //create a vector orthogonal to length en width in the same binary direction as heightdir. Scientrace.UnitVector owl = (width.crossProduct(length) * Math.Sign(width.crossProduct(length).dotProduct(heightdir))).tryToUnitVector(); Scientrace.NonzeroVector bdir = ( //calculate the direction of the short side of the prism owl * Math.Sin(angle) + width.tryToUnitVector() * Math.Cos(angle) ).tryToNonzeroVector(); if ((bdir.length < 0.99999) || (bdir.length > 1.00001)) { throw new ArgumentOutOfRangeException("bdir.length", bdir.length, "!= 1"); } Scientrace.VectorTransform trf = new Scientrace.VectorTransform( width.tryToNonzeroVector(), owl.tryToNonzeroVector(), length.tryToNonzeroVector()); Scientrace.NonzeroVector hdirtrf = trf.transform(heightdir).tryToNonzeroVector(); Scientrace.Vector hprimetrf = new Scientrace.Vector(hdirtrf.x, hdirtrf.y, 0); //eliminate "length" component of heightdir in hprime //Console.WriteLine("HPRIMTRF:"+hprimetrf.trico()); Scientrace.NonzeroVector hprimedir = trf.transformback(hprimetrf).tryToNonzeroVector().normalized(); /* ^ * /C\ * / \ * h'/ \ b * / \ * /B_______A\ * width * angle = A; beta = B; gamma = C. */ //sine rule: hprimelen / sin A = width.length() / sin C = blen / sin B double beta, gamma; beta = Math.Acos(hprimedir.normalized().dotProduct(width.tryToNonzeroVector().normalized())); gamma = Math.PI - (angle + beta); double hprimelen; double sinruleconstant = width.length / Math.Sin(gamma); hprimelen = sinruleconstant * Math.Sin(angle); Scientrace.NonzeroVector hprime = hprimedir * hprimelen; // check: (trf.transform(hprime).x / hdirtrf.x) == (trf.transform(hprime).y / hdirtrf.y) double xycoeff = ((trf.transform(hprime).x / hdirtrf.x) / (trf.transform(hprime).y / hdirtrf.y)); if (Math.Abs(1 - xycoeff) > 0.00001) //doesn't do anything if .x/.x = NaN, but that's OK for now. { throw new ArgumentOutOfRangeException("xycoeff", xycoeff, "!=1"); } try { Scientrace.NonzeroVector h = ((Math.Abs(hdirtrf.x) > Math.Abs(hdirtrf.y)) ? // Preventing .x or .y denominator == 0 errors. trf.transformback(hdirtrf * (trf.transform(hprime).x / hdirtrf.x)) : trf.transformback(hdirtrf * (trf.transform(hprime).y / hdirtrf.y)) ).tryToNonzeroVector(); return(h); } catch (Scientrace.ZeroNonzeroVectorException zne) { Console.WriteLine("ERROR: calculated height for triangularprism has length zero!"); throw (zne); } } //end calcTriangleHeightVector
private Scientrace.NonzeroVector calcTriangleHeightVector(ShadowObject3d sho3d) { Scientrace.Vector length = sho3d.getVector("length"); Scientrace.Vector width = sho3d.getVector("width"); Scientrace.Vector heightdir = sho3d.getVector("heightdir"); double angle = sho3d.getDouble("angle"); //create a vector orthogonal to length en width in the same binary direction as heightdir. Scientrace.UnitVector owl = (width.crossProduct(length) * Math.Sign(width.crossProduct(length).dotProduct(heightdir))).tryToUnitVector(); Scientrace.NonzeroVector bdir = ( //calculate the direction of the short side of the prism owl*Math.Sin(angle) + width.tryToUnitVector()*Math.Cos(angle) ).tryToNonzeroVector(); if ((bdir.length < 0.99999) || (bdir.length > 1.00001)) { throw new ArgumentOutOfRangeException("bdir.length", bdir.length, "!= 1"); } Scientrace.VectorTransform trf = new Scientrace.VectorTransform( width.tryToNonzeroVector(), owl.tryToNonzeroVector(), length.tryToNonzeroVector()); Scientrace.NonzeroVector hdirtrf = trf.transform(heightdir).tryToNonzeroVector(); Scientrace.Vector hprimetrf = new Scientrace.Vector(hdirtrf.x, hdirtrf.y, 0); //eliminate "length" component of heightdir in hprime //Console.WriteLine("HPRIMTRF:"+hprimetrf.trico()); Scientrace.NonzeroVector hprimedir = trf.transformback(hprimetrf).tryToNonzeroVector().normalized(); /* ^ * /C\ * / \ * h'/ \ b * / \ * /B_______A\ * width * angle = A; beta = B; gamma = C. */ //sine rule: hprimelen / sin A = width.length() / sin C = blen / sin B double beta, gamma; beta = Math.Acos(hprimedir.normalized().dotProduct(width.tryToNonzeroVector().normalized())); gamma = Math.PI - (angle + beta); double hprimelen; double sinruleconstant = width.length / Math.Sin(gamma); hprimelen = sinruleconstant*Math.Sin(angle); Scientrace.NonzeroVector hprime = hprimedir*hprimelen; // check: (trf.transform(hprime).x / hdirtrf.x) == (trf.transform(hprime).y / hdirtrf.y) double xycoeff = ((trf.transform(hprime).x / hdirtrf.x) / (trf.transform(hprime).y / hdirtrf.y)); if (Math.Abs(1-xycoeff)>0.00001) { //doesn't do anything if .x/.x = NaN, but that's OK for now. throw new ArgumentOutOfRangeException("xycoeff", xycoeff, "!=1"); } try { Scientrace.NonzeroVector h = ((Math.Abs(hdirtrf.x)>Math.Abs(hdirtrf.y)) ? // Preventing .x or .y denominator == 0 errors. trf.transformback(hdirtrf*(trf.transform(hprime).x / hdirtrf.x)) : trf.transformback(hdirtrf*(trf.transform(hprime).y / hdirtrf.y)) ).tryToNonzeroVector(); return h; } catch (Scientrace.ZeroNonzeroVectorException zne) { Console.WriteLine("ERROR: calculated height for triangularprism has length zero!"); throw (zne); } }
public Scientrace.VectorTransform getTransform() { //construction on request if (this.trf == null) { this.trf = new Scientrace.VectorTransform(this.u, this.v); } return this.trf; }