From 971301d5b8c869622ab33050b97095487eff1aee Mon Sep 17 00:00:00 2001 From: Andy McFadden Date: Tue, 10 Mar 2020 11:23:18 -0700 Subject: [PATCH] Fix object timeout Remember how object references from plugins are proxy objects that time out if you don't access them for a while? I didn't either. This reshuffles the code to keep WireframeObject references rather than IVisualizationWireframe. --- PluginCommon/VisWireframe.cs | 7 +-- SourceGen/VisWireframeAnimation.cs | 14 +++--- SourceGen/Visualization.cs | 44 ++++++++++--------- SourceGen/WpfGui/EditVisualization.xaml.cs | 29 ++++++------ .../WpfGui/ShowWireframeAnimation.xaml.cs | 8 ++-- 5 files changed, 53 insertions(+), 49 deletions(-) diff --git a/PluginCommon/VisWireframe.cs b/PluginCommon/VisWireframe.cs index 9e56383..558d340 100644 --- a/PluginCommon/VisWireframe.cs +++ b/PluginCommon/VisWireframe.cs @@ -140,7 +140,8 @@ namespace PluginCommon { foreach (IntPair ip in mEdges) { if (ip.Val0 < 0 || ip.Val0 >= vertexCount || ip.Val1 < 0 || ip.Val1 >= vertexCount) { - msg = "invalid edge"; + msg = "invalid edge (vertices " + ip.Val0 + ", " + ip.Val1 + + "; count=" + vertexCount + ")"; return false; } } @@ -149,7 +150,7 @@ namespace PluginCommon { foreach (IntPair ip in mVertexFaces) { if (ip.Val0 < 0 || ip.Val0 >= vertexCount || ip.Val1 < 0 || ip.Val1 >= faceCount) { - msg = "invalid vertex-face"; + msg = "invalid vertex-face (v=" + ip.Val0 + ", f=" + ip.Val1 + ")"; return false; } } @@ -158,7 +159,7 @@ namespace PluginCommon { foreach (IntPair ip in mVertexFaces) { if (ip.Val0 < 0 || ip.Val0 >= edgeCount || ip.Val1 < 0 || ip.Val1 >= faceCount) { - msg = "invalid edge-face"; + msg = "invalid edge-face (e=" + ip.Val0 + ", f=" + ip.Val1 + ")"; return false; } } diff --git a/SourceGen/VisWireframeAnimation.cs b/SourceGen/VisWireframeAnimation.cs index 52242cf..b6d0a8b 100644 --- a/SourceGen/VisWireframeAnimation.cs +++ b/SourceGen/VisWireframeAnimation.cs @@ -51,7 +51,7 @@ namespace SourceGen { public const string P_DELTA_ROT_Y = "_deltaRotY"; public const string P_DELTA_ROT_Z = "_deltaRotZ"; - private IVisualizationWireframe mVisWire; + private WireframeObject mWireObj; /// @@ -59,10 +59,10 @@ namespace SourceGen { /// public VisWireframeAnimation(string tag, string visGenIdent, ReadOnlyDictionary visGenParams, Visualization oldObj, - IVisualizationWireframe visWire) + WireframeObject wireObj) : base(tag, visGenIdent, visGenParams, oldObj) { - // visWire may be null when loading from project file - mVisWire = visWire; + // wireObj may be null when loading from project file + mWireObj = wireObj; OverlayImage = ANIM_OVERLAY_IMAGE; } @@ -71,7 +71,7 @@ namespace SourceGen { /// /// /// We override it because this is our first opportunity to capture the - /// IVisualizationWireframe reference when the object was created during project + /// wireframe object reference if the object was created during project /// file loading. /// /// Reference to wireframe data generated by plugin. @@ -79,7 +79,7 @@ namespace SourceGen { public override void SetThumbnail(IVisualizationWireframe visWire, ReadOnlyDictionary parms) { base.SetThumbnail(visWire, parms); - mVisWire = visWire; + mWireObj = WireframeObject.Create(visWire); } /// @@ -100,7 +100,7 @@ namespace SourceGen { bool doBfc = Util.GetFromObjDict(VisGenParams, VisWireframe.P_IS_BFC_ENABLED, false); for (int frame = 0; frame < frameCount; frame++) { - BitmapSource bs = GenerateWireframeImage(mVisWire, dim, + BitmapSource bs = GenerateWireframeImage(mWireObj, dim, curX, curY, curZ, doPersp, doBfc); encoder.AddFrame(BitmapFrame.Create(bs), frameDelayMsec); diff --git a/SourceGen/Visualization.cs b/SourceGen/Visualization.cs index 9d51c5e..540e57f 100644 --- a/SourceGen/Visualization.cs +++ b/SourceGen/Visualization.cs @@ -196,7 +196,8 @@ namespace SourceGen { ReadOnlyDictionary parms) { Debug.Assert(visWire != null); Debug.Assert(parms != null); - CachedImage = GenerateWireframeImage(visWire, THUMBNAIL_DIM, parms); + WireframeObject wireObj = WireframeObject.Create(visWire); + CachedImage = GenerateWireframeImage(wireObj, THUMBNAIL_DIM, parms); } /// @@ -257,30 +258,30 @@ namespace SourceGen { /// Output bitmap dimension (width and height). /// Parameter set, for rotations and render options. /// Rendered bitmap. - public static BitmapSource GenerateWireframeImage(IVisualizationWireframe visWire, + public static BitmapSource GenerateWireframeImage(WireframeObject wireObj, double dim, ReadOnlyDictionary parms) { int eulerX = Util.GetFromObjDict(parms, VisWireframeAnimation.P_EULER_ROT_X, 0); int eulerY = Util.GetFromObjDict(parms, VisWireframeAnimation.P_EULER_ROT_Y, 0); int eulerZ = Util.GetFromObjDict(parms, VisWireframeAnimation.P_EULER_ROT_Z, 0); bool doPersp = Util.GetFromObjDict(parms, VisWireframe.P_IS_PERSPECTIVE, false); bool doBfc = Util.GetFromObjDict(parms, VisWireframe.P_IS_BFC_ENABLED, false); - return GenerateWireframeImage(visWire, dim, eulerX, eulerY, eulerZ, doPersp, doBfc); + return GenerateWireframeImage(wireObj, dim, eulerX, eulerY, eulerZ, doPersp, doBfc); } /// /// Generates a BitmapSource from IVisualizationWireframe data. Useful for thumbnails /// and GIF exports. /// - public static BitmapSource GenerateWireframeImage(IVisualizationWireframe visWire, + public static BitmapSource GenerateWireframeImage(WireframeObject wireObj, double dim, int eulerX, int eulerY, int eulerZ, bool doPersp, bool doBfc) { // Generate the path geometry. - GeometryGroup geo = GenerateWireframePath(visWire, dim, eulerX, eulerY, eulerZ, + GeometryGroup geo = GenerateWireframePath(wireObj, dim, eulerX, eulerY, eulerZ, doPersp, doBfc); // Render Path to bitmap -- https://stackoverflow.com/a/23582564/294248 Rect bounds = geo.GetRenderBounds(null); - Debug.WriteLine("RenderWF dim=" + dim + " bounds=" + bounds + ": " + visWire); + Debug.WriteLine("RenderWF dim=" + dim + " bounds=" + bounds + ": " + wireObj); // Create bitmap. RenderTargetBitmap bitmap = new RenderTargetBitmap( @@ -319,14 +320,14 @@ namespace SourceGen { /// Visualization data. /// Width/height to use for path area. /// Visualization parameters. - public static GeometryGroup GenerateWireframePath(IVisualizationWireframe visWire, + public static GeometryGroup GenerateWireframePath(WireframeObject wireObj, double dim, ReadOnlyDictionary parms) { int eulerX = Util.GetFromObjDict(parms, VisWireframeAnimation.P_EULER_ROT_X, 0); int eulerY = Util.GetFromObjDict(parms, VisWireframeAnimation.P_EULER_ROT_Y, 0); int eulerZ = Util.GetFromObjDict(parms, VisWireframeAnimation.P_EULER_ROT_Z, 0); bool doPersp = Util.GetFromObjDict(parms, VisWireframe.P_IS_PERSPECTIVE, false); bool doBfc = Util.GetFromObjDict(parms, VisWireframe.P_IS_BFC_ENABLED, false); - return GenerateWireframePath(visWire, dim, eulerX, eulerY, eulerZ, doPersp, doBfc); + return GenerateWireframePath(wireObj, dim, eulerX, eulerY, eulerZ, doPersp, doBfc); } /// @@ -334,20 +335,21 @@ namespace SourceGen { /// scaled if the output area is larger or smaller than the path bounds, so this scales /// coordinates so they fit within the box. /// - public static GeometryGroup GenerateWireframePath(IVisualizationWireframe visWire, + public static GeometryGroup GenerateWireframePath(WireframeObject wireObj, double dim, int eulerX, int eulerY, int eulerZ, bool doPersp, bool doBfc) { // WPF path drawing is based on a system where a pixel is drawn at the center - // of the coordinate, and integer coordinates start at the top left edge. If - // you draw a pixel at (0,0), most of the pixel will be outside the window - // (visible or not based on ClipToBounds). + // of its coordinates, and integer coordinates start at the top left edge of + // the drawing area. If you draw a pixel at (0,0), 3/4ths of the pixel will be + // outside the window (visible or not based on ClipToBounds). // // If you draw a line from (1,1 to 4,1), the line's length will appear to // be (4 - 1) = 3. It touches four pixels -- the end point is not exclusive -- - // but because the thickness doesn't extend past the endpoints, the filled - // area is only three. If you have a window of size 10x10, and you draw from - // 0,0 to 9,9, the line will extend for half a line-thickness off the top, - // but will not go past the right/left edges. (This becomes very obvious when - // you're working with an up-scaled 8x8 path.) + // but the filled area is only three, because the thickness doesn't extend the + // line's length, and the line stops at the coordinate at the center of the pixel. + // You're not drawing N pixels, you're drawing from one coordinate point to another. + // If you have a window of size 8x8, and you draw from 0,0 to 7,0, the line will + // extend for half a line-thickness off the top, but will not go past the right/left + // edges. (This becomes very obvious when you're working with an up-scaled 8x8 path.) // // Similarly, drawing a horizontal line two units long results in a square, and // drawing a line that starts and ends at the same point doesn't appear to @@ -357,12 +359,13 @@ namespace SourceGen { // This turns out to be important for another reason: a line from (1,1) to (9,1) // shows up as a double-wide half-bright line, while a line from (1.5,1.5) to // (9.5,1.5) is drawn as a single-wide full-brightness line. This is because of - // the anti-aliasing. + // the anti-aliasing. Anti-aliasing can be disabled, but the lines look much + // nicer with it enabled. // // The path has an axis-aligned bounding box that covers the pixel centers. If we - // want a path-drawn shape to animate smoothly we want to ensure that the bounds + // want a path-drawn mesh to animate smoothly we want to ensure that the bounds // are constant across all renderings of a shape (which could get thinner or wider - // as it rotates), so we draw an invisible pixel in our desired bottom-right corner. + // as it rotates), so we plot an invisible point in our desired bottom-right corner. // // If we want an 8x8 bitmap, we draw a line from (8,8) to (8,8) to establish the // bounds, then draw lines with coordinates from 0.5 to 7.5. @@ -377,7 +380,6 @@ namespace SourceGen { // Generate a list of clip-space line segments. Coordinate values are in the // range [-1,1], with +X to the right and +Y upward. - WireframeObject wireObj = WireframeObject.Create(visWire); List segs = wireObj.Generate(eulerX, eulerY, eulerZ, doPersp, doBfc); diff --git a/SourceGen/WpfGui/EditVisualization.xaml.cs b/SourceGen/WpfGui/EditVisualization.xaml.cs index f3802e3..5a1e9b4 100644 --- a/SourceGen/WpfGui/EditVisualization.xaml.cs +++ b/SourceGen/WpfGui/EditVisualization.xaml.cs @@ -52,8 +52,8 @@ namespace SourceGen.WpfGui { private SortedList mEditedList; private Visualization mOrigVis; - // IVisualization2d or IVisualizationWireframe - public object mVisObj; + private BitmapSource mThumbnail; + private WireframeObject mWireObj; /// /// Visualization generation identifier for the last visualizer we used, for the benefit @@ -384,19 +384,19 @@ namespace SourceGen.WpfGui { Debug.Assert(isTagValid); if (isWireframe && IsWireframeAnimated) { NewVis = new VisWireframeAnimation(trimTag, item.VisDescriptor.Ident, valueDict, - mOrigVis, (IVisualizationWireframe) mVisObj); + mOrigVis, mWireObj); } else { NewVis = new Visualization(trimTag, item.VisDescriptor.Ident, valueDict, mOrigVis); } + + // Set the thumbnail image. if (isWireframe) { - Debug.Assert(mVisObj is IVisualizationWireframe); - NewVis.CachedImage = - Visualization.GenerateWireframeImage((IVisualizationWireframe)mVisObj, - Visualization.THUMBNAIL_DIM, valueDict); + Debug.Assert(mWireObj != null); + NewVis.CachedImage = Visualization.GenerateWireframeImage(mWireObj, + Visualization.THUMBNAIL_DIM, valueDict); } else { - Debug.Assert(mVisObj is IVisualization2d); - NewVis.CachedImage = - Visualization.ConvertToBitmapSource((IVisualization2d)mVisObj); + Debug.Assert(mThumbnail != null); + NewVis.CachedImage = mThumbnail; } sLastVisIdent = NewVis.VisGenIdent; @@ -600,16 +600,17 @@ namespace SourceGen.WpfGui { BitmapDimensions = string.Format("{0}x{1}", previewImage.Source.Width, previewImage.Source.Height); - mVisObj = vis2d; + mThumbnail = (BitmapSource)previewImage.Source; } else { previewGrid.Background = Brushes.Black; previewImage.Source = Visualization.BLANK_IMAGE; double dim = Math.Floor( Math.Min(previewImage.ActualWidth, previewImage.ActualHeight)); - wireframePath.Data = Visualization.GenerateWireframePath(visWire, dim, parms); + WireframeObject wireObj = WireframeObject.Create(visWire); + wireframePath.Data = Visualization.GenerateWireframePath(wireObj, dim, parms); BitmapDimensions = "n/a"; - mVisObj = visWire; + mWireObj = wireObj; } } @@ -690,7 +691,7 @@ namespace SourceGen.WpfGui { } private void TestAnim_Click(object sender, RoutedEventArgs e) { - ShowWireframeAnimation dlg = new ShowWireframeAnimation(this, (IVisualizationWireframe)mVisObj, + ShowWireframeAnimation dlg = new ShowWireframeAnimation(this, mWireObj, CreateVisGenParams(true)); dlg.ShowDialog(); } diff --git a/SourceGen/WpfGui/ShowWireframeAnimation.xaml.cs b/SourceGen/WpfGui/ShowWireframeAnimation.xaml.cs index 4e706fa..eaf707f 100644 --- a/SourceGen/WpfGui/ShowWireframeAnimation.xaml.cs +++ b/SourceGen/WpfGui/ShowWireframeAnimation.xaml.cs @@ -33,7 +33,7 @@ namespace SourceGen.WpfGui { /// private DispatcherTimer mTimer; - IVisualizationWireframe mVisWire; + WireframeObject mWireObj; private int mFrameCount; private int mInitialX, mInitialY, mInitialZ; private int mDeltaX, mDeltaY, mDeltaZ; @@ -43,12 +43,12 @@ namespace SourceGen.WpfGui { private int mCurFrame; - public ShowWireframeAnimation(Window owner, IVisualizationWireframe visWire, + public ShowWireframeAnimation(Window owner, WireframeObject wireObj, ReadOnlyDictionary parms) { InitializeComponent(); Owner = owner; - mVisWire = visWire; + mWireObj = wireObj; mCurX = mInitialX = Util.GetFromObjDict(parms, VisWireframeAnimation.P_EULER_ROT_X, 0); mCurY = mInitialY = Util.GetFromObjDict(parms, VisWireframeAnimation.P_EULER_ROT_Y, 0); @@ -98,7 +98,7 @@ namespace SourceGen.WpfGui { // We use the dimensions of the Border surrounding the ViewBox, rather than the // ViewBox itself, because on the first iteration the ViewBox has a size of zero. double dim = Math.Floor(Math.Min(testBorder.ActualWidth, testBorder.ActualHeight)); - wireframePath.Data = Visualization.GenerateWireframePath(mVisWire, dim, + wireframePath.Data = Visualization.GenerateWireframePath(mWireObj, dim, mCurX, mCurY, mCurZ, mDoPersp, mDoBfc); } }