-
- using System;
- using System.Threading;
- using System.Drawing;
- using Autodesk.AutoCAD.ApplicationServices;
- using Autodesk.AutoCAD.EditorInput;
- using Autodesk.AutoCAD.DatabaseServices;
- using Autodesk.AutoCAD.Runtime;
- using Autodesk.AutoCAD.Geometry;
- using Autodesk.AutoCAD.GraphicsInterface;
- using Autodesk.AutoCAD.GraphicsSystem;
- using Autodesk.AutoCAD.Interop;
-
- namespace ViewTransitionsSlerp
- {
- public class MyView
- {
- public Point3d position;
- public Point3d target;
- public Vector3d upVector;
- public double fieldWidth;
- public double fieldHeight;
-
-
-
- public MyView(){}
-
-
-
- public MyView(
- double x1, double y1, double z1,
- double x2, double y2, double z2,
- double x3, double y3, double z3,
- double x4, double y4
- )
- {
- position = new Point3d(x1, y1, z1)
- target = new Point3d(x2, y2, z2)
- upVector = new Vector3d(x3, y3, z3)
- fieldWidth = x4
- fieldHeight= y4
- }
-
- public MyView(
- Point3d position, Point3d target, Vector3d upVector,
- double fieldWidth, double fieldHeight
- )
- {
- this.position = position
- this.target = target
- this.upVector = upVector
- this.fieldWidth = fieldWidth
- this.fieldHeight = fieldHeight
- }
- };
-
- public class Commands
- {
- static MyView defaultView =
- new MyView(
- 1930.1,1339.3,4399.3, 1930.1,1339.3,0.0,
- 0.0,1.0,0.0, 3279.8, 1702.6
- );
- static MyView topView =
- new MyView(
- 1778.1,1108.2,635.7, 1778.1,1108.2,0.0,
- 0.0,1.0,0.0, 474.0, 246.0
- );
- static MyView bottomView =
- new MyView(
- 1778.1,1108.2,-635.7, 1778.1,1108.2,0.0,
- 0.0,1.0,0.0, 474.0, 246.0
- );
- static MyView leftView =
- new MyView(
- -344.1,1108.2,66.1, 0.0,1108.2,66.1,
- 0.0,0.0,1.0, 256.5, 133.2
- );
- static MyView rightView =
- new MyView(
- 344.1,1108.2,66.1, 0.0,1108.2,66.1,
- 0.0,0.0,1.0, 256.5, 133.2
- );
- static MyView SWIso =
- new MyView(
- 265.1,-404.7,1579.0, 838.0,168.2,1006.2,
- 0.4,0.4,0.8, 739.7, 384.0
- );
- static MyView SEIso =
- new MyView(
- 2105.6,780.7,393.7, 1532.7,1353.5,-179.2,
- -0.4,0.4,0.8, 739.7, 384.0
- );
- static MyView NEIso =
- new MyView(
- 1366.8,697.0,-345.2, 793.9,124.1,-918.0,
- -0.4,-0.4,0.8, 739.7, 384.0
- );
- static MyView NWIso =
- new MyView(
- 1003.9, 1882.3, 840.2, 1576.8, 1309.5,
- 267.3, 0.4, -0.4, 0.8, 739.7, 384.0
- );
-
- // Enacts a smooth transition from the current view to a
- // new view using spherical linear interpolation (Slerp)
-
- static Matrix3d SmoothViewToSlerp(
- MyView nv, double timeToTake
- )
- {
- Matrix3d newViewMatrix = Matrix3d.Identity;
-
- Document doc =
- Application.DocumentManager.MdiActiveDocument;
- Database db =
- doc.Database;
- Manager gsm =
- doc.GraphicsManager;
-
-
-
- int vpn =
- Convert.ToInt32(
- Application.GetSystemVariable("CVPORT")
- );
-
- View view = gsm.GetGsView(vpn, true);
- using (view)
- {
-
-
- view.BeginInteractivity(24);
-
- Matrix3d viewMatrix = view.ViewingMatrix;
-
- // Get the current view settings
-
- MyView cv =
- new MyView(
- view.Position, view.Target, view.UpVector,
- view.FieldWidth, view.FieldHeight
- );
-
- // Set up the start positions
-
- Point3d intPos = cv.position;
- Point3d intTgt = cv.target;
- Vector3d intUpVec = cv.upVector;
- double intWid = cv.fieldWidth;
- double intHgt = cv.fieldHeight;
-
-
-
-
- for (float mu = 0; mu 50 ? 0 : sleepTime));
- }
-
- view.EndInteractivity();
-
- // Finally set the new view
-
- gsm.SetViewportFromView(vpn, view, true, true, false);
-
- System.Windows.Forms.Application.DoEvents();
- }
-
- return newViewMatrix;
- }
-
-
-
- static double CosInterp(double y1, double y2, double mu)
- {
- double mu2;
-
- mu2 = (1-Math.Cos(mu*Math.PI))/2
- return(y1*(1-mu2)+y2*mu2);
- }
-
-
-
- static Vector3d Slerp(Vector3d from, Vector3d to, float step)
- {
- if (step == 0)
- return from;
-
- if (from == to || step == 1)
- return to;
-
-
-
- Vector3d unitfrom = from.GetNormal(),
- unitto = to.GetNormal()
-
-
-
- double theta =
- Math.Acos(unitfrom.DotProduct(unitto));
-
- if (theta == 0)
- return to;
-
-
-
- double st =
- Math.Sin(theta);
-
-
-
- return
- from * (Math.Sin((1 - step) * theta) / st) +
- to * Math.Sin(step * theta) / st;
- }
-
- // Function to create a solid background of the same
-
-
-
-
- private static ObjectId CreateBackground()
- {
- const string bgKey = "TTIF_BG";
-
- Document doc =
- Application.DocumentManager.MdiActiveDocument;
- Database db =
- doc.Database;
-
- ObjectId vtId = ObjectId.Null;
-
-
-
- int vpn =
- Convert.ToInt32(
- Application.GetSystemVariable("CVPORT")
- );
-
-
- // 3D view already exists
-
- View view =
- doc.GraphicsManager.GetGsView(vpn, false);
- if (view == null)
- {
- Transaction tr =
- db.TransactionManager.StartTransaction();
- using (tr)
- {
- ObjectId bgId = ObjectId.Null;
-
-
-
- ObjectId bgdId =
- Background.GetBackgroundDictionaryId(db, true);
-
- DBDictionary bgd =
- (DBDictionary)tr.GetObject(
- bgdId,
- OpenMode.ForRead
- );
-
- if (bgd.Contains(bgKey))
- {
- bgId = bgd.GetAt(bgKey)
- }
- else
- {
-
-
-
-
- AcadPreferences prefs =
- (AcadPreferences)Application.Preferences;
- int rawCol =
- (int)prefs.Display.GraphicsWinModelBackgrndColor;
-
- // Create a background with the corresponding RGB
-
- SolidBackground sb = new SolidBackground();
- sb.Color =
- new Autodesk.AutoCAD.Colors.EntityColor(
- (byte)(rawCol & 0x000000FF),
- (byte)((rawCol & 0x0000FF00) >> 8),
- (byte)((rawCol & 0x00FF0000) >> 16)
- );
-
-
-
- bgd.UpgradeOpen();
- bgId = bgd.SetAt(bgKey, sb)
- tr.AddNewlyCreatedDBObject(sb, true);
- }
-
-
-
- ViewportTable vt =
- (ViewportTable)tr.GetObject(
- db.ViewportTableId,
- OpenMode.ForRead
- );
- foreach (ObjectId id in vt)
- {
- ViewportTableRecord vtr =
- (ViewportTableRecord)tr.GetObject(
- id,
- OpenMode.ForRead
- );
- if (vtr.Name == "*Active")
- {
- vtId = id
- vtr.UpgradeOpen();
- vtr.Background = bgId
- }
- }
- tr.Commit();
- }
- }
- else
- view.Dispose();
-
- return vtId;
- }
-
- private static void RemoveBackground(ObjectId vtId)
- {
- Document doc =
- Application.DocumentManager.MdiActiveDocument;
- Database db =
- doc.Database;
-
- Transaction tr =
- db.TransactionManager.StartTransaction();
- using (tr)
- {
-
-
- ViewportTableRecord vtr =
- (ViewportTableRecord)tr.GetObject(
- vtId,
- OpenMode.ForWrite
- );
-
-
-
- ObjectId obgId =
- vtr.GetPreviousBackground(
- DrawableType.SolidBackground
- );
- vtr.Background = obgId
-
- tr.Commit();
- }
- }
-
- [CommandMethod("TVS")]
- static public void TransitionViewSlerp()
- {
- Document doc =
- Application.DocumentManager.MdiActiveDocument;
- Database db =
- doc.Database;
-
- ObjectId vtId = CreateBackground();
-
- SmoothViewToSlerp(defaultView, 10);
- SmoothViewToSlerp(SWIso, 10);
- SmoothViewToSlerp(topView, 10);
- SmoothViewToSlerp(SEIso, 10);
- SmoothViewToSlerp(bottomView, 10);
- SmoothViewToSlerp(NEIso, 10);
- SmoothViewToSlerp(leftView, 10);
- SmoothViewToSlerp(NWIso, 10);
- SmoothViewToSlerp(rightView, 10);
- SmoothViewToSlerp(defaultView, 10);
-
- if (vtId != ObjectId.Null)
- RemoveBackground(vtId);
- }
- }
- }
What's especially notable about this implementation is actually how similarly it works to the previous one. Fenton came up with a pretty nice interpolation technique without knowing about Slerp which produces very similar - possibly identical, although I haven't verified them - results. Very cool.
From my side I hadn't heard of Slerp and only had the vaguest idea of what a quaternion was - even now I wouldn't know a quaternion if it bit me on the nose, so it's a good thing the wikipedia article contains a geometric alternative to the quaternion Slerp formula.
So why use one technique over the other? There are a couple of possible differentiators that may make a difference to people.
The first is the possibility - and this is not something I've verified through performance benchmarking - that the Slerp implementation is more efficient. We need fewer calls to Slerp() than we used for CosInterp(), simply because we're interpolating multiple values at the same time. But this isn't likely to be a noticeable difference in any real-world application, so isn't something that would concern me, either way.
The second differentiator is a potential deployment issue: a bug was introduced with Service Pack 1 of the .NET Framework 3.5 that can cause problems with vector arithmetic in AutoCAD .NET applications. Jimmy Bergmark reported on this, back in August, and a hotfix was posted by Microsoft in early December. I hit this issue after having installed a pre-release version of AutoCAD 2010 (which installed the .NET Framework 3.5 SP1) but I hit the problem when executing code in AutoCAD 2009. Installing the hotfix solved the problem, but in this case the more fundamental implementation not relying on Vector3d objects proved to be more reliable.
In reality, though, avoiding vector arithmetic isn't really an option for most developers, so this is being addressed on a few fronts: AutoCAD 2009 Update 2 apparently works around it (not sure how I missed that update, but there you go) as does AutoCAD 2010... and it also seems that any day now Microsoft will be pushing out a fixed version of the .NET Framework 3.5 SP1 in a general distribution release via Windows Update (which means that any Windows user running .NET Framework 2.0 or higher will get it). So I'm a little less worried about the impact of this issue than I was when I first saw it manifest itself. For ADN members who would like additional, detailed information on this issue, please visit this DevNote on the ADN site (login required).
All this to say that the two versions of the code are much the same, when all is said and done. I've provided both mainly for the purposes of intellectual curiosity and in case the techniques shown are relevant for other scenarios.