public override bool GetLocalRealignmentWithTwoRotationCenters(LineTrackingCoordinateSystem axis1, Vector3 center1, LineTrackingCoordinateSystem axis2, Vector3 center2, out LineTrackingCoordinateSystem realignedAxis1, out LineTrackingCoordinateSystem realignedAxis2, out float errorDegrees) { Vector3 dir1 = axis1.Direction; Vector3 dir2 = axis2.Direction; if (VectorMath.GetAngleRadians(dir1, dir2) > Math.PI) { dir1 = -dir1; } Vector3 average = Vector3.Normalize(dir1 + dir2); Matrix mat1 = VectorMath.GetRotationMatrix(dir1, average); mat1.Translation += center1 - Vector3.Transform(center1, mat1); Matrix mat2 = VectorMath.GetRotationMatrix(dir2, average); mat2.Translation += center2 - Vector3.Transform(center2, mat2); realignedAxis1 = new LineTrackingCoordinateSystem(axis1); realignedAxis2 = new LineTrackingCoordinateSystem(axis2); realignedAxis1.ApplyTransform(mat1); realignedAxis2.ApplyTransform(mat2); errorDegrees = (float)VectorMath.GetAngleDegrees(dir1, average); return(true); }
private static void ValidateGeometryUtilities() { for (int i = 0; i < 10; i++) { // Check the proper quaternion is generated to go 'from'->'to' Random r = new Random(); Vector3 v1 = Vector3.Normalize(new Vector3(r.Next(), r.Next(), r.Next())); Vector3 v2 = Vector3.Normalize(new Vector3(r.Next(), r.Next(), r.Next())); float angle1 = r.Next() % 360; float angle2 = r.Next() % 360; if (angle1 == 0 || angle2 == 0) { continue; } Quaternion from = Quaternion.CreateFromAxisAngle(v1, (float)VectorMath.DegreesToRad(angle1)); Quaternion to = Quaternion.CreateFromAxisAngle(v2, (float)VectorMath.DegreesToRad(angle2)); Quaternion transformQuat = VectorMath.GetLocalRotation(from, to); Quaternion fromTo = from * transformQuat; //Quaternion fromTo = from; float newAngleBetween = VectorMath.GetAngleDegrees(to, fromTo); Debug.Assert(Math.Abs(newAngleBetween) < 5); float verifyAngle1; Vector3 verifyAxis1; VectorMath.GetRotationVectorAndAngleDegrees(from, out verifyAxis1, out verifyAngle1); Debug.Assert(Vector3.Dot(verifyAxis1, v1) > 0.99 && Math.Abs(verifyAngle1 - angle1) < 1 || Vector3.Dot(verifyAxis1, v1) < -0.99 && Math.Abs(360 - verifyAngle1 - angle1) < 1); //Debug.Assert( <= 2); Quaternion verifyFrom = VectorMath.GetLocalRotation(Quaternion.Identity, from); VectorMath.GetRotationVectorAndAngleDegrees(verifyFrom, out verifyAxis1, out verifyAngle1); Debug.Assert(Vector3.Dot(verifyAxis1, v1) > 0.99 && Math.Abs(verifyAngle1 - angle1) < 1 || Vector3.Dot(verifyAxis1, v1) < -0.99 && Math.Abs(360 - verifyAngle1 - angle1) < 1); } { Quaternion rotation1 = Quaternion.CreateFromAxisAngle(Vector3.UnitZ, (float)Math.PI / 2); CoordinateSystem cs = new CoordinateSystem(); cs.Translation = new Vector3(1, 2, 3); Console.WriteLine("PRE = " + cs.Translation.ToString()); cs.RotateLocal(new Vector3(-1, -2, -3), rotation1); Console.WriteLine("POST = " + cs.Translation.ToString()); } { for (int i = 0; i < 10; i++) { Random r = new Random(); Vector3 v1 = Vector3.Normalize(new Vector3(r.Next(), r.Next(), r.Next())); Vector3 v2 = Vector3.Normalize(new Vector3(r.Next(), r.Next(), r.Next())); Quaternion quat = VectorMath.GetRotationQuaternion(v1, v2); Debug.Assert(Vector3.Dot(Vector3.Transform(v1, quat), v2) > 0.99); } } }
public static TwoAxisRealignment Create(SymmetryBuilder builder, string axis1, string axis2) { CoordinateSystem coordinateSystem1 = builder.GetPrincipalCoordinateSystem(axis1); CoordinateSystem coordinateSystem2 = builder.GetPrincipalCoordinateSystem(axis2); Line principalAxis1 = Line.CreateFromPointAndDirection(coordinateSystem1.Translation, coordinateSystem1.UnitX); // The rosetta symdefs axes are along X of each transformed coordinate system, but something not requiring foreknowledge would be nice. Line principalAxis2 = Line.CreateFromPointAndDirection(coordinateSystem2.Translation, coordinateSystem2.UnitX); // The rosetta symdefs axes are along X of each transformed coordinate system, but something not requiring foreknowledge would be nice. Quaternion premultiply = Quaternion.Inverse(coordinateSystem1.Transform.Rotation); principalAxis1.Direction = Vector3.Transform(principalAxis1.Direction, premultiply); principalAxis2.Direction = Vector3.Transform(principalAxis2.Direction, premultiply); principalAxis1.Point = Vector3.Transform(principalAxis1.Point, premultiply); principalAxis2.Point = Vector3.Transform(principalAxis2.Point, premultiply); float angleDegrees = (float)VectorMath.GetAngleDegrees(principalAxis1.Direction, principalAxis2.Direction); TwoAxisRealignment result = TwoAxisRealignment.Create(angleDegrees, principalAxis1, principalAxis2); return(result); }
static void StartJobsAngleCXRCX(CxrcxFusionFlags options) { SymmetryBuilder symmetry = SymmetryBuilderFactory.CreateFromSymmetryName(options.Architecture); List <string> filesOligomer1 = GetPdbFiles(Directory.GetCurrentDirectory(), options.OligomerRegex1).ToList(); List <string> filesOligomer2 = GetPdbFiles(Directory.GetCurrentDirectory(), options.OligomerRegex2).ToList(); List <string> filesRepeat = GetPdbFiles(Directory.GetCurrentDirectory(), options.RepeatRegex).ToList(); ThreadSafeJobCounter sharedCounter = new ThreadSafeJobCounter(); sharedCounter.Total = filesOligomer1.Count * filesOligomer2.Count * filesRepeat.Count; Console.WriteLine("Using the following spacer repeat proteins:"); filesRepeat.ForEach(file => Console.WriteLine(file)); Console.WriteLine("\nUsing the following files for h**o-oligomer 1:"); filesOligomer1.ForEach(file => Console.WriteLine(file)); Console.WriteLine("\nUsing the following files for h**o-oligomer 2:"); filesOligomer2.ForEach(file => Console.WriteLine(file)); float angleDegrees = (float)VectorMath.GetAngleDegrees(symmetry.GetPrincipalCoordinateSystem(options.UnitId1).UnitX, Vector3.Zero, symmetry.GetPrincipalCoordinateSystem(options.UnitId2).UnitX); Console.WriteLine("Angle for {0}:{1}:{2} calculated to: {3:F4}", options.Architecture, options.UnitId1, options.UnitId2, angleDegrees); // Parse oligomer 1 and offset it to positive Z foreach (string fileOligomer1 in filesOligomer1) { string pdbCodeOligomer1 = PdbQuick.CodeFromFilePath(fileOligomer1); IChain peptide1 = PdbQuick.ChainFromFileOrCode(fileOligomer1); if (peptide1 == null) { Console.WriteLine("Pdb parsing failed for {0}", fileOligomer1); continue; } peptide1.Translate(new Vector3(0, 0, 20 - Rmsd.GetBackboneCentroid(peptide1).Z)); // Parse oligomer 2 and offset it to positive Z foreach (string fileOligomer2 in filesOligomer2) { string pdbCodeOligomer2 = PdbQuick.CodeFromFilePath(fileOligomer2); IChain peptide2 = PdbQuick.ChainFromFileOrCode(fileOligomer2); if (peptide2 == null) { Console.WriteLine("Pdb parsing failed for {0}", fileOligomer2); continue; } peptide2.Translate(new Vector3(0, 0, 20 - Rmsd.GetBackboneCentroid(peptide2).Z)); // Parse the repeat and offset it to positive Z foreach (string fileRepeat in filesRepeat) { IChain repeat = PdbQuick.ChainFromFileOrCode(fileRepeat); string pdbCodeRepeat = PdbQuick.CodeFromFilePath(fileRepeat); if (repeat == null) { Console.WriteLine("Pdb parsing failed for {0}", repeat); continue; } repeat.Translate(new Vector3(0, 0, 20 - Rmsd.GetBackboneCentroid(repeat).Z)); JobStartParamsCXRCX startParams = new JobStartParamsCXRCX(); // things taken directly from user options startParams.OutputPrefix = options.Architecture + "-" + options.UnitId1 + "-" + options.UnitId2; startParams.UnitId1 = options.UnitId1; startParams.UnitId2 = options.UnitId2; startParams.ChainCount1 = options.Oligomerization1; startParams.ChainCount2 = options.Oligomerization2; startParams.TopX = options.TopX; // everything else: startParams.Symmetry = SymmetryBuilderFactory.CreateFromSymmetryName(options.Architecture); startParams.PdbCodeBundle1 = pdbCodeOligomer1; startParams.PdbCodeBundle2 = pdbCodeOligomer2; startParams.PdbCodeRepeat = pdbCodeRepeat; startParams.Cx1 = peptide1; startParams.Cx2 = peptide2; startParams.Repeat = repeat; startParams.AngleDegrees = angleDegrees; startParams.Counter = sharedCounter; // Shared counter across queued jobs Debug.Assert(startParams.Validate(), "JobStartParamsCXRCX validation failure"); // Wait for free threads _threadCountSemaphore.WaitOne(); sharedCounter.IncrementQueued(); // Start the job Thread thread = new Thread(new ParameterizedThreadStart(RunJobAngleCXRCX)); thread.Start(startParams); Console.WriteLine("Queuing triplet [Bundle {0}]:[Repeat {1}]:[Bundle {2}], {3:F2} degrees, {4:F2} % ({5}/{6})", pdbCodeOligomer1, pdbCodeRepeat, pdbCodeOligomer2, angleDegrees, startParams.Counter.PercentQueued, startParams.Counter.Queued, startParams.Counter.Total); } } } }
public override bool GetLocalRealignmentWithTwoRotationCenters(LineTrackingCoordinateSystem axis1, Vector3 center1, LineTrackingCoordinateSystem axis2, Vector3 center2, out LineTrackingCoordinateSystem realignedAxis1, out LineTrackingCoordinateSystem realignedAxis2, out float errorDegrees) { // Set out parameter default values realignedAxis1 = new LineTrackingCoordinateSystem(axis1); realignedAxis2 = new LineTrackingCoordinateSystem(axis2); errorDegrees = float.NaN; // Find the points (p1 and p2) where axes (l1 and l2) are nearest and perpendicular to the rotation center // Find the plane normals (r1 and r2) defined by each center the new axis (p1->p2) that isn't yet tangent both spheres Vector3 p1 = Line.GetNearestPointOnLine(Line.CreateFrom(axis1), center1); Vector3 p2 = Line.GetNearestPointOnLine(Line.CreateFrom(axis2), center2); Line p12 = Line.CreateFromTwoPoints(p1, p2); Vector3 r1 = Vector3.Normalize(Vector3.Cross(p1 - center1, p12.Direction)); // Plane normals Vector3 r2 = Vector3.Normalize(Vector3.Cross(p2 - center2, p12.Direction)); Vector3 vC12 = center2 - center1; Vector3 uC12 = Vector3.Normalize(vC12); // Get the "average" plane that is created by rotating each plane about (p1->p2) until they are coincident Vector3 r = Vector3.Dot(r1, r2) > 0? Vector3.Normalize(r1 + r2) : Vector3.Normalize(r1 - r2); // This gives the normal for the nearest of two possible mid-planes Debug.Assert(Math.Abs(Vector3.Dot(r, p12.Direction)) < 0.001); // Project center1 and center2 onto the plane to determine circle centers and their radii Vector3 c1projection = center1 + Vector3.Dot(p1 - center1, r) * r; Vector3 c2projection = center2 + Vector3.Dot(p2 - center2, r) * r; float c1radius = Vector3.Distance(p1, c1projection); float c2radius = Vector3.Distance(p2, c2projection); Vector3 vProjectionC1C2 = c2projection - c1projection; Vector3 uProjectionC1C2 = Vector3.Normalize(vProjectionC1C2); float distanceProjectionC12 = vProjectionC1C2.Length(); Debug.Assert(Math.Abs(Vector3.Dot(p1 - c1projection, r)) < 0.001); Debug.Assert(Math.Abs(Vector3.Dot(p2 - c2projection, r)) < 0.001); // If one spherical surface fully contains the other, there are no solutions if (c1radius + distanceProjectionC12 < c2radius || c2radius + distanceProjectionC12 < c1radius) { return(false); } // Compute theta (angle between c1->c2 and the p1 on c1 or p2 on c2) //float originalTheta1 = (float) Math.Acos(p2 - c1proj) float solutionTheta1 = (float)Math.Acos((c1radius - c2radius) / distanceProjectionC12); float solutionTheta2 = c1radius + c2radius < distanceProjectionC12 ? (float)Math.Acos((c2radius + c2radius) / distanceProjectionC12) : float.NaN; // theta2 is only defined for spheres that don't overlap // Solutions exist at +/- theta1 Vector3 positiveTheta1P1 = c1projection + Vector3.Transform(uProjectionC1C2, Quaternion.CreateFromAxisAngle(r, solutionTheta1)); Vector3 positiveTheta1P2 = c2projection + Vector3.Transform(uProjectionC1C2, Quaternion.CreateFromAxisAngle(r, solutionTheta1)); Vector3 negativeTheta1P1 = c1projection + Vector3.Transform(uProjectionC1C2, Quaternion.CreateFromAxisAngle(r, -solutionTheta1)); Vector3 negativeTheta1P2 = c2projection + Vector3.Transform(uProjectionC1C2, Quaternion.CreateFromAxisAngle(r, -solutionTheta1)); Vector3 uPositiveTheta1 = positiveTheta1P2 - positiveTheta1P1; Vector3 uNegativeTheta1 = negativeTheta1P1 - negativeTheta1P2; float radErrorPositiveTheta1 = (float)(VectorMath.GetAngleDegrees(positiveTheta1P1 - c1projection, p1 - c1projection) + VectorMath.GetAngleDegrees(positiveTheta1P2 - c2projection, p2 - c2projection)); float radErrorNegativeTheta1 = (float)(VectorMath.GetAngleDegrees(negativeTheta1P1 - c1projection, p1 - c1projection) + VectorMath.GetAngleDegrees(negativeTheta1P2 - c2projection, p2 - c2projection)); if (radErrorPositiveTheta1 < radErrorNegativeTheta1) { errorDegrees = radErrorPositiveTheta1; // Move the realigned axes to reflect rotation onto the p1->p2 line segment Quaternion rotation1 = VectorMath.GetRotationQuaternion(p1 - center1, positiveTheta1P1 - center1); Quaternion rotation2 = VectorMath.GetRotationQuaternion(p2 - center2, positiveTheta1P2 - center2); realignedAxis1.Translation -= center1; realignedAxis1.ApplyRotation(rotation1); realignedAxis1.Translation += center1; realignedAxis2.Translation -= center2; realignedAxis2.ApplyRotation(rotation2); realignedAxis2.Translation += center2; rotation1 = Vector3.Dot(realignedAxis1.Direction, uPositiveTheta1) > 0 ? VectorMath.GetRotationQuaternion(realignedAxis1.Direction, uPositiveTheta1) : VectorMath.GetRotationQuaternion(realignedAxis1.Direction, -uPositiveTheta1); rotation2 = Vector3.Dot(realignedAxis2.Direction, uPositiveTheta1) > 0 ? VectorMath.GetRotationQuaternion(realignedAxis2.Direction, uPositiveTheta1) : VectorMath.GetRotationQuaternion(realignedAxis2.Direction, -uPositiveTheta1); realignedAxis1.Translation -= center1; realignedAxis1.ApplyRotation(rotation1); realignedAxis1.Translation += center1; realignedAxis2.Translation -= center2; realignedAxis2.ApplyRotation(rotation2); realignedAxis2.Translation += center2; } else { errorDegrees = radErrorNegativeTheta1; return(false); } // TODO: negative theta1, theta2 positive and negative return(true); }