1
0
mirror of https://github.com/fadden/6502bench.git synced 2024-11-25 14:34:27 +00:00

Progress toward wireframe animations

Moved X/Y/Z rotation out of the plugin, since it has nothing to do
with the plugin at all.  (Backface removal and perspective projection
are somewhat based on the data contents, as is the choice for
whether or not they should be options.)

Added sliders for X/Y/Z rotation.  Much more fun that way.

Renamed VisualizationAnimation to VisBitmapAnimation, as we're not
going to use it for wireframe animation.  Created a new class to
hold wireframe animation data, which is really just a reference to
the IVisualizationWireframe so we can generate an animated GIF
without having to pry open the plugin again.

Renamed the "frame-delay-msec" parameter, which should start with
an underscore to ensure it doesn't clash with plugin parameters.
If we don't find it with an underscore we check again without for
backward compatibility.
This commit is contained in:
Andy McFadden 2020-03-07 16:52:36 -08:00
parent b686d2d208
commit 7e92a86ffa
16 changed files with 355 additions and 139 deletions

View File

@ -181,7 +181,7 @@ namespace PluginCommon {
public class VisDescr {
/// <summary>
/// Unique identifier. This is stored in the project file. Names beginning with
/// underscores ('_') are reserved.
/// underscores ('_') are reserved and must not be defined by plugins.
/// </summary>
public string Ident { get; private set; }

View File

@ -27,9 +27,6 @@ namespace PluginCommon {
public class VisWireframe : IVisualizationWireframe {
public const string P_IS_PERSPECTIVE = "_isPerspective";
public const string P_IS_BFC_ENABLED = "_isBfcEnabled";
public const string P_EULER_ROT_X = "_eulerRotX";
public const string P_EULER_ROT_Y = "_eulerRotY";
public const string P_EULER_ROT_Z = "_eulerRotZ";
public static VisParamDescr Param_IsPerspective(string uiLabel, bool defaultVal) {
return new VisParamDescr(uiLabel, P_IS_PERSPECTIVE, typeof(bool), 0, 0, 0, defaultVal);
@ -37,15 +34,6 @@ namespace PluginCommon {
public static VisParamDescr Param_IsBfcEnabled(string uiLabel, bool defaultVal) {
return new VisParamDescr(uiLabel, P_IS_BFC_ENABLED, typeof(bool), 0, 0, 0, defaultVal);
}
public static VisParamDescr Param_EulerX(string uiLabel, int defaultVal) {
return new VisParamDescr(uiLabel, P_EULER_ROT_X, typeof(int), 0, 359, 0, defaultVal);
}
public static VisParamDescr Param_EulerY(string uiLabel, int defaultVal) {
return new VisParamDescr(uiLabel, P_EULER_ROT_Y, typeof(int), 0, 359, 0, defaultVal);
}
public static VisParamDescr Param_EulerZ(string uiLabel, int defaultVal) {
return new VisParamDescr(uiLabel, P_EULER_ROT_Z, typeof(int), 0, 359, 0, defaultVal);
}
private List<float> mVerticesX = new List<float>();
private List<float> mVerticesY = new List<float>();

View File

@ -791,11 +791,11 @@ namespace SourceGen {
int dispWidth, dispHeight;
Visualization vis = visSet[index];
if (vis is VisualizationAnimation) {
if (vis is VisBitmapAnimation) {
// Animated visualization.
VisualizationAnimation visAnim = (VisualizationAnimation)vis;
VisBitmapAnimation visAnim = (VisBitmapAnimation)vis;
int frameDelay = PluginCommon.Util.GetFromObjDict(visAnim.VisGenParams,
VisualizationAnimation.FRAME_DELAY_MSEC_PARAM, 330);
VisBitmapAnimation.P_FRAME_DELAY_MSEC_PARAM, 330);
AnimatedGifEncoder encoder = new AnimatedGifEncoder();
// Gather list of frames.

View File

@ -373,11 +373,11 @@ namespace SourceGen {
VisGenParams = new Dictionary<string, object>(vis.VisGenParams);
}
}
public class SerVisualizationAnimation : SerVisualization {
public class SerVisBitmapAnimation : SerVisualization {
public List<string> Tags { get; set; }
public SerVisualizationAnimation() { }
public SerVisualizationAnimation(VisualizationAnimation visAnim,
public SerVisBitmapAnimation() { }
public SerVisBitmapAnimation(VisBitmapAnimation visAnim,
SortedList<int, VisualizationSet> visSets)
: base(visAnim) {
Tags = new List<string>(visAnim.Count);
@ -400,7 +400,7 @@ namespace SourceGen {
}
}
// Fields are serialized to/from JSON. Do not change the field names.
// Fields are serialized to/from JSON. DO NOT change the field names.
public int _ContentVersion { get; set; }
public int FileDataLength { get; set; }
public int FileDataCrc32 { get; set; }
@ -415,7 +415,7 @@ namespace SourceGen {
public Dictionary<string, SerFormatDescriptor> OperandFormats { get; set; }
public Dictionary<string, SerLocalVariableTable> LvTables { get; set; }
public List<SerVisualization> Visualizations { get; set; }
public List<SerVisualizationAnimation> VisualizationAnimations { get; set; }
public List<SerVisBitmapAnimation> VisualizationAnimations { get; set; }
public Dictionary<string, SerVisualizationSet> VisualizationSets { get; set; }
/// <summary>
@ -506,15 +506,15 @@ namespace SourceGen {
spf.LvTables.Add(kvp.Key.ToString(), new SerLocalVariableTable(kvp.Value));
}
// Output Visualizations, VisualizationAnimations, and VisualizationSets
// Output Visualizations, VisBitmapAnimations, and VisualizationSets
spf.Visualizations = new List<SerVisualization>();
spf.VisualizationAnimations = new List<SerVisualizationAnimation>();
spf.VisualizationAnimations = new List<SerVisBitmapAnimation>();
spf.VisualizationSets = new Dictionary<string, SerVisualizationSet>();
foreach (KeyValuePair<int, VisualizationSet> kvp in proj.VisualizationSets) {
foreach (Visualization vis in kvp.Value) {
if (vis is VisualizationAnimation) {
VisualizationAnimation visAnim = (VisualizationAnimation)vis;
spf.VisualizationAnimations.Add(new SerVisualizationAnimation(visAnim,
if (vis is VisBitmapAnimation) {
VisBitmapAnimation visAnim = (VisBitmapAnimation)vis;
spf.VisualizationAnimations.Add(new SerVisBitmapAnimation(visAnim,
proj.VisualizationSets));
} else {
spf.Visualizations.Add(new SerVisualization(vis));
@ -793,10 +793,10 @@ namespace SourceGen {
}
}
// Extract the VisualizationAnimations, which link to Visualizations by tag.
foreach (SerVisualizationAnimation serVisAnim in spf.VisualizationAnimations) {
if (CreateVisualizationAnimation(serVisAnim, visDict, report,
out VisualizationAnimation visAnim)) {
// Extract the VisBitmapAnimations, which link to Visualizations by tag.
foreach (SerVisBitmapAnimation serVisAnim in spf.VisualizationAnimations) {
if (CreateVisBitmapAnimation(serVisAnim, visDict, report,
out VisBitmapAnimation visAnim)) {
try {
visDict.Add(visAnim.Tag, visAnim);
} catch (ArgumentException) {
@ -1057,11 +1057,11 @@ namespace SourceGen {
}
/// <summary>
/// Creates a VisualizationAnimation from its serialized form.
/// Creates a VisBitmapAnimation from its serialized form.
/// </summary>
private static bool CreateVisualizationAnimation(SerVisualizationAnimation serVisAnim,
private static bool CreateVisBitmapAnimation(SerVisBitmapAnimation serVisAnim,
Dictionary<string, Visualization> visList, FileLoadReport report,
out VisualizationAnimation visAnim) {
out VisBitmapAnimation visAnim) {
if (!CheckVis(serVisAnim, report, out Dictionary<string, object> parms)) {
visAnim = null;
return false;
@ -1074,7 +1074,7 @@ namespace SourceGen {
report.Add(FileLoadItem.Type.Warning, str);
continue;
}
if (vis is VisualizationAnimation) {
if (vis is VisBitmapAnimation) {
string str = string.Format(Res.Strings.ERR_BAD_VISUALIZATION_FMT,
"animation in animation: " + tag);
report.Add(FileLoadItem.Type.Warning, str);
@ -1082,8 +1082,8 @@ namespace SourceGen {
}
serialNumbers.Add(vis.SerialNumber);
}
visAnim = new VisualizationAnimation(serVisAnim.Tag, serVisAnim.VisGenIdent,
new ReadOnlyDictionary<string, object>(parms), serialNumbers, null);
visAnim = new VisBitmapAnimation(serVisAnim.Tag, serVisAnim.VisGenIdent,
new ReadOnlyDictionary<string, object>(parms), null, serialNumbers);
return true;
}

View File

@ -43,9 +43,6 @@ namespace WireframeTest {
P_OFFSET, typeof(int), 0, 0x00ffffff, VisParamDescr.SpecialMode.Offset, 0),
// These are interpreted by the main app.
VisWireframe.Param_EulerX("Rotation about X", 0),
VisWireframe.Param_EulerY("Rotation about Y", 0),
VisWireframe.Param_EulerZ("Rotation about Z", 0),
VisWireframe.Param_IsPerspective("Perspective projection", true),
VisWireframe.Param_IsBfcEnabled("Backface culling", true),
}),

View File

@ -245,11 +245,11 @@
"VisGenIdent":"wireframe-test",
"VisGenParams":{
"offset":10,
"_eulerRotX":15,
"_eulerRotY":30,
"_eulerRotZ":0,
"_isPerspective":true,
"_isBfcEnabled":true}},
"_isBfcEnabled":true,
"_eulerRotX":0,
"_eulerRotY":34,
"_eulerRotZ":65}},
{
"Tag":"bmp_data",
@ -263,8 +263,17 @@
"isColor":true,
"isFirstOdd":false,
"isHighBitFlipped":false}}],
"VisualizationAnimations":[],
"VisualizationAnimations":[{
"Tags":["wf_data",
"bmp_data"],
"Tag":"anim00002c",
"VisGenIdent":"(animation)",
"VisGenParams":{
"_frame-delay-msec":500}}],
"VisualizationSets":{
"10":{
"Tags":["wf_data",
"bmp_data"]}}}
"bmp_data"]},
"44":{
"Tags":["anim00002c"]}}}

View File

@ -103,8 +103,9 @@
</Compile>
<Compile Include="LocalVariableTable.cs" />
<Compile Include="Visualization.cs" />
<Compile Include="VisualizationAnimation.cs" />
<Compile Include="VisBitmapAnimation.cs" />
<Compile Include="VisualizationSet.cs" />
<Compile Include="VisWireframeAnimation.cs" />
<Compile Include="WireframeObject.cs" />
<Compile Include="WpfGui\AboutBox.xaml.cs">
<DependentUpon>AboutBox.xaml</DependentUpon>

View File

@ -27,17 +27,17 @@ namespace SourceGen {
/// A visualization with animated contents.
/// </summary>
/// <remarks>
/// References to Visualization objects (such as a 3D mesh or list of bitmaps) are held
/// here. The VisGenParams property holds animation properties, such as frame rate and
/// view angles.
/// This has a list of references to Visualization objects. The cached images are
/// displayed in sequence, at a rate determined by animation parameters also held here.
///
/// As with the base class, instances are generally immutable for the benefit of undo/redo.
/// </remarks>
public class VisualizationAnimation : Visualization {
public class VisBitmapAnimation : Visualization {
/// <summary>
/// Frame delay parameter.
/// </summary>
public const string FRAME_DELAY_MSEC_PARAM = "frame-delay-msec";
public const string P_FRAME_DELAY_MSEC_PARAM = "_frame-delay-msec";
public const string P_FRAME_DELAY_MSEC_PARAM_OLD = "frame-delay-msec";
/// <summary>
/// Fake visualization generation identifier.
@ -68,9 +68,9 @@ namespace SourceGen {
/// <param name="visGenIdent">Visualization generator identifier.</param>
/// <param name="visGenParams">Parameters for visualization generator.</param>
/// <param name="visSerialNumbers">Serial numbers of referenced Visualizations.</param>
public VisualizationAnimation(string tag, string visGenIdent,
ReadOnlyDictionary<string, object> visGenParams, List<int> visSerialNumbers,
VisualizationAnimation oldObj)
public VisBitmapAnimation(string tag, string visGenIdent,
ReadOnlyDictionary<string, object> visGenParams, VisBitmapAnimation oldObj,
List<int> visSerialNumbers)
: base(tag, visGenIdent, visGenParams, oldObj) {
Debug.Assert(visSerialNumbers != null);
@ -143,8 +143,8 @@ namespace SourceGen {
/// <param name="removedSerials">List of serial numbers to remove.</param>
/// <param name="newAnim">Object with changes, or null if nothing changed.</param>
/// <returns>True if something was actually removed.</returns>
public static bool StripEntries(VisualizationAnimation visAnim, List<int> removedSerials,
out VisualizationAnimation newAnim) {
public static bool StripEntries(VisBitmapAnimation visAnim, List<int> removedSerials,
out VisBitmapAnimation newAnim) {
bool somethingRemoved = false;
// Both sets should be small, so not worried about O(m*n).
@ -159,45 +159,16 @@ namespace SourceGen {
}
if (somethingRemoved) {
newAnim = new VisualizationAnimation(visAnim.Tag, visAnim.VisGenIdent,
visAnim.VisGenParams, newSerials, visAnim);
newAnim = new VisBitmapAnimation(visAnim.Tag, visAnim.VisGenIdent,
visAnim.VisGenParams, visAnim, newSerials);
} else {
newAnim = null;
}
return somethingRemoved;
}
public static BitmapSource GenerateAnimOverlayImage() {
const int IMAGE_SIZE = 128;
// Glowy "high tech" blue.
SolidColorBrush outlineBrush = new SolidColorBrush(Color.FromArgb(255, 0, 216, 255));
SolidColorBrush fillBrush = new SolidColorBrush(Color.FromArgb(128, 0, 182, 215));
DrawingVisual visual = new DrawingVisual();
using (DrawingContext dc = visual.RenderOpen()) {
// Thanks: https://stackoverflow.com/a/29249100/294248
Point p1 = new Point(IMAGE_SIZE * 5 / 8, IMAGE_SIZE / 2);
Point p2 = new Point(IMAGE_SIZE * 3 / 8, IMAGE_SIZE / 4);
Point p3 = new Point(IMAGE_SIZE * 3 / 8, IMAGE_SIZE * 3 / 4);
StreamGeometry sg = new StreamGeometry();
using (StreamGeometryContext sgc = sg.Open()) {
sgc.BeginFigure(p1, true, true);
PointCollection points = new PointCollection() { p2, p3 };
sgc.PolyLineTo(points, true, true);
}
sg.Freeze();
dc.DrawGeometry(fillBrush, new Pen(outlineBrush, 3), sg);
}
RenderTargetBitmap bmp = new RenderTargetBitmap(IMAGE_SIZE, IMAGE_SIZE, 96.0, 96.0,
PixelFormats.Pbgra32);
bmp.Render(visual);
return bmp;
}
public static bool operator ==(VisualizationAnimation a, VisualizationAnimation b) {
public static bool operator ==(VisBitmapAnimation a, VisBitmapAnimation b) {
if (ReferenceEquals(a, b)) {
return true; // same object, or both null
}
@ -206,18 +177,18 @@ namespace SourceGen {
}
return a.Equals(b);
}
public static bool operator !=(VisualizationAnimation a, VisualizationAnimation b) {
public static bool operator !=(VisBitmapAnimation a, VisBitmapAnimation b) {
return !(a == b);
}
public override bool Equals(object obj) {
if (!(obj is VisualizationAnimation)) {
if (!(obj is VisBitmapAnimation)) {
return false;
}
// Do base-class equality comparison and the ReferenceEquals check.
if (!base.Equals(obj)) {
return false;
}
VisualizationAnimation other = (VisualizationAnimation)obj;
VisBitmapAnimation other = (VisBitmapAnimation)obj;
if (other.mSerialNumbers.Count != mSerialNumbers.Count) {
return false;
}

View File

@ -0,0 +1,57 @@
/*
* Copyright 2020 faddenSoft
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using PluginCommon;
namespace SourceGen {
/// <summary>
/// A wireframe visualization with animation.
/// </summary>
/// <remarks>
/// All of the animation parameters get added to the Visualization parameter set, so this
/// is just a place to hold the IVisualizationWireframe reference and some constants.
/// </remarks>
public class VisWireframeAnimation : Visualization {
/// <summary>
/// Frame delay parameter.
/// </summary>
public const string P_FRAME_DELAY_MSEC = "_frame-delay-msec";
/// <summary>
/// Frame count parameter.
/// </summary>
public const string P_FRAME_COUNT = "_frame-count";
public const string P_EULER_ROT_X = "_eulerRotX";
public const string P_EULER_ROT_Y = "_eulerRotY";
public const string P_EULER_ROT_Z = "_eulerRotZ";
private IVisualizationWireframe mVisWire;
public VisWireframeAnimation(string tag, string visGenIdent,
ReadOnlyDictionary<string, object> visGenParams, Visualization oldObj,
IVisualizationWireframe visWire)
: base(tag, visGenIdent, visGenParams, oldObj) {
Debug.Assert(visWire != null);
mVisWire = visWire;
}
}
}

View File

@ -35,6 +35,20 @@ namespace SourceGen {
/// Immutability is useful here because the undo/redo mechanism operates at VisualizationSet
/// granularity. We want to know that the undo/redo operations are operating on objects
/// that weren't changed while sitting in the undo buffer.
///
/// At a basic level, bitmap and wireframe visualizations are the same: you take a
/// visualization generation identifier and a bunch of parameters, and generate Stuff.
/// The nature of the Stuff and what you do with it after are very different, however.
///
/// For a bitmap, we can generate the data once, and then scale or transform it as
/// necessary. Bitmap animations are a collection of bitmap visualizations.
///
/// For wireframes, we generate a WireframeObject using some of the parameters, and then
/// transform it with other parameters. The parameters are stored in a single dictionary,
/// but viewer-only parameters are prefixed with '_', which is not allowed in plugins.
///
/// This class represents the common ground between bitmaps and wireframes. It holds the
/// identifier and parameters, as well as the thumbnail data that we display in the list.
/// </remarks>
public class Visualization {
public const double THUMBNAIL_DIM = 64;
@ -98,8 +112,7 @@ namespace SourceGen {
/// <summary>
/// Image to overlay on animation visualizations.
/// </summary>
internal static readonly BitmapSource ANIM_OVERLAY_IMAGE =
VisualizationAnimation.GenerateAnimOverlayImage();
internal static readonly BitmapSource ANIM_OVERLAY_IMAGE = GenerateAnimOverlayImage();
internal static readonly BitmapSource BLANK_IMAGE = GenerateBlankImage();
//internal static readonly BitmapSource BLACK_IMAGE = GenerateBlackImage();
@ -246,6 +259,7 @@ namespace SourceGen {
/// <returns>Rendered bitmap.</returns>
public static BitmapSource GenerateWireframeImage(IVisualizationWireframe visWire,
ReadOnlyDictionary<string, object> parms, double dim) {
// Generate the path geometry.
GeometryGroup geo = GenerateWireframePath(visWire, parms, dim);
// Render Path to bitmap -- https://stackoverflow.com/a/23582564/294248
@ -283,15 +297,19 @@ namespace SourceGen {
}
/// <summary>
/// Generates a WPF path from IVisualizationWireframe data. Line widths get scaled
/// if the output area is larger or smaller than the path, so this scales coordinates
/// so they fit within the box.
/// Generates WPF Path geometry from IVisualizationWireframe data. Line widths get
/// scaled if the output area is larger or smaller than the path bounds, so this scales
/// coordinates so they fit within the box.
/// </summary>
/// <param name="visWire">Visualization data.</param>
/// <param name="parms">Visualization parameters.</param>
/// <param name="dim">Width/height to use for path area.</param>
public static GeometryGroup GenerateWireframePath(IVisualizationWireframe visWire,
ReadOnlyDictionary<string, object> parms, double dim) {
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);
// 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
@ -334,7 +352,7 @@ 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<WireframeObject.LineSeg> segs = wireObj.Generate(parms);
List<WireframeObject.LineSeg> segs = wireObj.Generate(parms, eulerX, eulerY, eulerZ);
// Convert clip-space coords to screen. We need to translate to [0,2] with +Y
// toward the bottom of the screen, scale up, round to the nearest whole pixel,
@ -361,6 +379,39 @@ namespace SourceGen {
return bmp;
}
/// <summary>
/// Generate an image to overlay on thumbnails of animations.
/// </summary>
/// <returns></returns>
private static BitmapSource GenerateAnimOverlayImage() {
const int IMAGE_SIZE = 128;
// Glowy "high tech" blue.
SolidColorBrush outlineBrush = new SolidColorBrush(Color.FromArgb(255, 0, 216, 255));
SolidColorBrush fillBrush = new SolidColorBrush(Color.FromArgb(128, 0, 182, 215));
DrawingVisual visual = new DrawingVisual();
using (DrawingContext dc = visual.RenderOpen()) {
// Thanks: https://stackoverflow.com/a/29249100/294248
Point p1 = new Point(IMAGE_SIZE * 5 / 8, IMAGE_SIZE / 2);
Point p2 = new Point(IMAGE_SIZE * 3 / 8, IMAGE_SIZE / 4);
Point p3 = new Point(IMAGE_SIZE * 3 / 8, IMAGE_SIZE * 3 / 4);
StreamGeometry sg = new StreamGeometry();
using (StreamGeometryContext sgc = sg.Open()) {
sgc.BeginFigure(p1, true, true);
PointCollection points = new PointCollection() { p2, p3 };
sgc.PolyLineTo(points, true, true);
}
sg.Freeze();
dc.DrawGeometry(fillBrush, new Pen(outlineBrush, 3), sg);
}
RenderTargetBitmap bmp = new RenderTargetBitmap(IMAGE_SIZE, IMAGE_SIZE, 96.0, 96.0,
PixelFormats.Pbgra32);
bmp.Render(visual);
return bmp;
}
/// <summary>
/// Returns a bitmap with a single black pixel.
/// </summary>

View File

@ -118,13 +118,13 @@ namespace SourceGen {
bool somethingRemoved = false;
newSet = new VisualizationSet(visSet.Count);
foreach (Visualization vis in visSet) {
if (!(vis is VisualizationAnimation)) {
if (!(vis is VisBitmapAnimation)) {
newSet.Add(vis);
continue;
}
if (VisualizationAnimation.StripEntries((VisualizationAnimation) vis,
removedSerials, out VisualizationAnimation newAnim)) {
if (VisBitmapAnimation.StripEntries((VisBitmapAnimation) vis,
removedSerials, out VisBitmapAnimation newAnim)) {
somethingRemoved = true;
if (newAnim.Count != 0) {
newSet.Add(newAnim);
@ -186,7 +186,7 @@ namespace SourceGen {
}
//Debug.WriteLine("Vis needs refresh: " + vis.Tag);
if (vis is VisualizationAnimation) {
if (vis is VisBitmapAnimation) {
continue;
}
@ -242,14 +242,14 @@ namespace SourceGen {
}
// Now that we've generated images for the Visualizations, update any
// VisualizationAnimation thumbnails that may have been affected.
// VisBitmapAnimation thumbnails that may have been affected.
foreach (KeyValuePair<int, VisualizationSet> kvp in visSets) {
VisualizationSet visSet = kvp.Value;
foreach (Visualization vis in visSet) {
if (!(vis is VisualizationAnimation)) {
if (!(vis is VisBitmapAnimation)) {
continue;
}
VisualizationAnimation visAnim = (VisualizationAnimation)vis;
VisBitmapAnimation visAnim = (VisBitmapAnimation)vis;
visAnim.GenerateImage(visSets);
}
}

View File

@ -209,7 +209,8 @@ namespace SourceGen {
/// <param name="parms">Visualization parameters.</param>
/// <returns>List of line segments, which could be empty if backface removal
/// was especially successful.</returns>
public List<LineSeg> Generate(ReadOnlyDictionary<string, object> parms) {
public List<LineSeg> Generate(ReadOnlyDictionary<string, object> parms,
int eulerX, int eulerY, int eulerZ) {
List<LineSeg> segs = new List<LineSeg>(mEdges.Count);
bool doPersp = Util.GetFromObjDict(parms, VisWireframe.P_IS_PERSPECTIVE, false);
bool doBfc = Util.GetFromObjDict(parms, VisWireframe.P_IS_BFC_ENABLED, false);
@ -226,9 +227,6 @@ namespace SourceGen {
scale = (scale * zadj) / (zadj + 0.5);
}
int eulerX = Util.GetFromObjDict(parms, VisWireframe.P_EULER_ROT_X, 0);
int eulerY = Util.GetFromObjDict(parms, VisWireframe.P_EULER_ROT_Y, 0);
int eulerZ = Util.GetFromObjDict(parms, VisWireframe.P_EULER_ROT_Z, 0);
Matrix44 rotMat = new Matrix44();
rotMat.SetRotationEuler(eulerX, eulerY, eulerZ);

View File

@ -36,11 +36,11 @@ namespace SourceGen.WpfGui {
/// <summary>
/// New/edited animation, only valid when dialog result is true.
/// </summary>
public VisualizationAnimation NewAnim { get; private set; }
public VisBitmapAnimation NewAnim { get; private set; }
private int mSetOffset;
private SortedList<int, VisualizationSet> mEditedList;
private VisualizationAnimation mOrigAnim;
private VisBitmapAnimation mOrigAnim;
private Brush mDefaultLabelColor = SystemColors.WindowTextBrush;
private Brush mErrorLabelColor = Brushes.Red;
@ -135,7 +135,7 @@ namespace SourceGen.WpfGui {
/// Constructor.
/// </summary>
public EditBitmapAnimation(Window owner, int setOffset,
SortedList<int, VisualizationSet> editedList, VisualizationAnimation origAnim) {
SortedList<int, VisualizationSet> editedList, VisBitmapAnimation origAnim) {
InitializeComponent();
Owner = owner;
DataContext = this;
@ -149,7 +149,12 @@ namespace SourceGen.WpfGui {
if (origAnim != null) {
TagString = origAnim.Tag;
mFrameDelayIntMsec = PluginCommon.Util.GetFromObjDict(origAnim.VisGenParams,
VisualizationAnimation.FRAME_DELAY_MSEC_PARAM, DEFAULT_FRAME_DELAY);
VisBitmapAnimation.P_FRAME_DELAY_MSEC_PARAM, DEFAULT_FRAME_DELAY);
if (mFrameDelayIntMsec == DEFAULT_FRAME_DELAY) {
// check for old-style
mFrameDelayIntMsec = PluginCommon.Util.GetFromObjDict(origAnim.VisGenParams,
VisBitmapAnimation.P_FRAME_DELAY_MSEC_PARAM_OLD, DEFAULT_FRAME_DELAY);
}
FrameDelayTimeMsec = mFrameDelayIntMsec.ToString();
} else {
TagString = "anim" + mSetOffset.ToString("x6");
@ -179,7 +184,7 @@ namespace SourceGen.WpfGui {
// Add all remaining non-animation Visualizations to the "source" set.
foreach (KeyValuePair<int, VisualizationSet> kvp in mEditedList) {
foreach (Visualization vis in kvp.Value) {
if (vis is VisualizationAnimation) {
if (vis is VisBitmapAnimation) {
// disallow using animations as animation frames
continue;
}
@ -210,15 +215,15 @@ namespace SourceGen.WpfGui {
private void OkButton_Click(object sender, RoutedEventArgs e) {
Dictionary<string, object> visGenParams = new Dictionary<string, object>(1);
visGenParams.Add(VisualizationAnimation.FRAME_DELAY_MSEC_PARAM, mFrameDelayIntMsec);
visGenParams.Add(VisBitmapAnimation.P_FRAME_DELAY_MSEC_PARAM, mFrameDelayIntMsec);
List<int> serials = new List<int>(VisAnimItems.Count);
foreach (Visualization vis in VisAnimItems) {
serials.Add(vis.SerialNumber);
}
NewAnim = new VisualizationAnimation(TagString, VisualizationAnimation.ANIM_VIS_GEN,
new ReadOnlyDictionary<string, object>(visGenParams), serials, mOrigAnim);
NewAnim = new VisBitmapAnimation(TagString, VisBitmapAnimation.ANIM_VIS_GEN,
new ReadOnlyDictionary<string, object>(visGenParams), mOrigAnim, serials);
NewAnim.GenerateImage(mEditedList);
DialogResult = true;

View File

@ -98,6 +98,7 @@ limitations under the License.
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0">
@ -135,7 +136,7 @@ limitations under the License.
BorderBrush="{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}"
Background="{StaticResource CheckerBackground}">
<!-- explicit width gets cleared after initial window layout -->
<Grid Name="previewGrid" Width="400" Height="400">
<Grid Name="previewGrid" Width="548" Height="400">
<!-- either the image or the path is shown, not both; background is set to
transparent for image, black for path -->
<Image Name="previewImage" Source="/Res/RedX.png"
@ -173,8 +174,78 @@ limitations under the License.
ItemTemplateSelector="{StaticResource ParameterTemplateSelector}">
</ItemsControl>
</Grid>
<Grid Grid.Row="4" HorizontalAlignment="Left" Visibility="{Binding WireframeCtrlVisibility}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Rectangle Grid.Row="0" Grid.ColumnSpan="3" Margin="4" HorizontalAlignment="Stretch"
Fill="LightGray" Height="2"/>
<TextBlock Grid.Column="0" Grid.Row="1" Text="Initial X rotation (deg):" Margin="0,5,0,0"/>
<TextBox Grid.Column="1" Grid.Row="1" Margin="4,4,0,0" Width="30" MaxLength="3"
Text="{Binding ElementName=initialXSlider, Path=Value, UpdateSourceTrigger=PropertyChanged}"/>
<Slider Name="initialXSlider" Grid.Column="2" Grid.Row="1" Margin="4,4,0,0" Width="360"
Minimum="0" Maximum="359" TickFrequency="1" IsSnapToTickEnabled="True"
ValueChanged="InitialRotSlider_ValueChanged"/>
<TextBlock Grid.Column="0" Grid.Row="2" Text="Initial Y rotation (deg):" Margin="0,5,0,0"/>
<TextBox Grid.Column="1" Grid.Row="2" Margin="4,4,0,0" Width="30" MaxLength="3"
Text="{Binding ElementName=initialYSlider, Path=Value, UpdateSourceTrigger=PropertyChanged}"/>
<Slider Name="initialYSlider" Grid.Column="2" Grid.Row="2" Margin="4,4,0,0" Width="360"
Minimum="0" Maximum="359" TickFrequency="1" IsSnapToTickEnabled="True"
ValueChanged="InitialRotSlider_ValueChanged"/>
<TextBlock Grid.Column="0" Grid.Row="3" Text="Initial Z rotation (deg):" Margin="0,5,0,0"/>
<TextBox Grid.Column="1" Grid.Row="3" Margin="4,4,0,0" Width="30" MaxLength="3"
Text="{Binding ElementName=initialZSlider, Path=Value, UpdateSourceTrigger=PropertyChanged}"/>
<Slider Name="initialZSlider" Grid.Column="2" Grid.Row="3" Margin="4,4,0,0" Width="360"
Minimum="0" Maximum="359" TickFrequency="1" IsSnapToTickEnabled="True"
ValueChanged="InitialRotSlider_ValueChanged"/>
<CheckBox Name="isAnimated" Grid.Column="0" Grid.Row="4" Content="Animated" Margin="0,4,0,0"
IsChecked="{Binding IsWireframeAnimated}"/>
<!-- remaining items are enabled when "isAnimated" checkbox is checked -->
<TextBlock Grid.Column="0" Grid.Row="5" Grid.ColumnSpan="2" Margin="0,5,0,0"
Text="Rotation per frame:"/>
<StackPanel Grid.Column="1" Grid.Row="5" Grid.ColumnSpan="2" Orientation="Horizontal" Margin="4,4,0,0"
IsEnabled="{Binding ElementName=isAnimated, Path=IsChecked}">
<TextBlock Text="X:" Margin="0,1,0,0"/>
<TextBox Width="40" MaxLength="4" Margin="4,0,0,0"/>
<TextBlock Text="Y:" Margin="8,1,0,0"/>
<TextBox Width="40" MaxLength="4" Margin="4,0,0,0"/>
<TextBlock Text="Z:" Margin="8,1,0,0"/>
<TextBox Width="40" MaxLength="4" Margin="4,0,0,0"/>
</StackPanel>
<TextBlock Grid.Column="0" Grid.Row="6" Text="Frame count:" Margin="0,5,0,0"/>
<TextBox Grid.Column="1" Grid.Row="6" Width="50" Height="18" Margin="4,4,0,0"/>
<Button Grid.Column="2" Grid.Row="6" Width="70" Margin="4,4,0,0" HorizontalAlignment="Left"
Content="Auto" />
<TextBlock Grid.Column="0" Grid.Row="7" Text="Frame delay (msec):" Margin="0,5,0,0"/>
<TextBox Grid.Column="1" Grid.Row="7" Width="50" Margin="4,4,0,0"/>
</Grid>
<DockPanel Grid.Column="0" Grid.Row="4" Margin="0,8,0,0" LastChildFill="False">
<DockPanel Grid.Column="0" Grid.Row="5" Margin="0,8,0,0" LastChildFill="False">
<Button DockPanel.Dock="Left" Content="Test Animation" Padding="4,0" Click="TestAnim_Click"
Visibility="{Binding WireframeCtrlVisibility}"
IsEnabled="{Binding ElementName=isAnimated, Path=IsChecked}"/>
<Button DockPanel.Dock="Right" Content="Cancel" Width="70" Margin="8,0,0,0" IsCancel="True"/>
<Button DockPanel.Dock="Right" Grid.Column="1" Content="OK" Width="70"
IsDefault="True" IsEnabled="{Binding IsValid}" Click="OkButton_Click"/>

View File

@ -31,6 +31,15 @@ namespace SourceGen.WpfGui {
/// <summary>
/// Visualization editor.
/// </summary>
/// <remarks>
/// This provides editing of bitmap and wireframe visualizations, which have some
/// significant differences. We deal with them both here to provide an illusion of
/// consistency, and because it's nice to have the plugin management and generated
/// parameter edit controls in one place.
///
/// The most significant difference is that, while bitmap animations are a collection
/// of visualizations, wireframe animations are just additional parameters.
/// </remarks>
public partial class EditVisualization : Window, INotifyPropertyChanged {
/// <summary>
/// New/edited visualization, only valid when dialog result is true.
@ -43,7 +52,7 @@ namespace SourceGen.WpfGui {
private SortedList<int, VisualizationSet> mEditedList;
private Visualization mOrigVis;
public BitmapSource mThumbnail;
public object mVisObj;
/// <summary>
/// Visualization generation identifier for the last visualizer we used, for the benefit
@ -72,6 +81,18 @@ namespace SourceGen.WpfGui {
}
private bool mIsValid;
public Visibility WireframeCtrlVisibility {
get { return mWireframeCtrlVisibility; }
set { mWireframeCtrlVisibility = value; OnPropertyChanged(); }
}
private Visibility mWireframeCtrlVisibility;
public bool IsWireframeAnimated {
get { return mIsWireframeAnimated; }
set { mIsWireframeAnimated = value; OnPropertyChanged(); }
}
private bool mIsWireframeAnimated;
/// <summary>
/// Visualization tag.
/// </summary>
@ -318,18 +339,34 @@ namespace SourceGen.WpfGui {
private void OkButton_Click(object sender, RoutedEventArgs e) {
VisualizationItem item = (VisualizationItem)visComboBox.SelectedItem;
Debug.Assert(item != null);
ReadOnlyDictionary<string, object> valueDict = sLastParams = CreateVisGenParams();
bool isWireframe = (item.VisDescriptor.VisualizationType == VisDescr.VisType.Wireframe);
ReadOnlyDictionary<string, object> valueDict = CreateVisGenParams(isWireframe);
sLastParams = valueDict;
string trimTag = Visualization.TrimAndValidateTag(TagString, out bool isTagValid);
Debug.Assert(isTagValid);
NewVis = new Visualization(trimTag, item.VisDescriptor.Ident, valueDict, mOrigVis);
NewVis.CachedImage = mThumbnail;
if (isWireframe && IsWireframeAnimated) {
NewVis = new VisWireframeAnimation(trimTag, item.VisDescriptor.Ident, valueDict,
mOrigVis, (IVisualizationWireframe) mVisObj);
} else {
NewVis = new Visualization(trimTag, item.VisDescriptor.Ident, valueDict, mOrigVis);
}
if (isWireframe) {
Debug.Assert(mVisObj is IVisualizationWireframe);
NewVis.CachedImage =
Visualization.GenerateWireframeImage((IVisualizationWireframe)mVisObj,
valueDict, Visualization.THUMBNAIL_DIM);
} else {
Debug.Assert(mVisObj is IVisualization2d);
NewVis.CachedImage =
Visualization.ConvertToBitmapSource((IVisualization2d)mVisObj);
}
sLastVisIdent = NewVis.VisGenIdent;
DialogResult = true;
}
private ReadOnlyDictionary<string, object> CreateVisGenParams() {
private ReadOnlyDictionary<string, object> CreateVisGenParams(bool includeWire) {
// Generate value dictionary.
Dictionary<string, object> valueDict =
new Dictionary<string, object>(ParameterList.Count);
@ -351,6 +388,17 @@ namespace SourceGen.WpfGui {
}
}
WireframeCtrlVisibility = includeWire ? Visibility.Visible : Visibility.Collapsed;
if (includeWire) {
int rotX = (int)initialXSlider.Value;
int rotY = (int)initialYSlider.Value;
int rotZ = (int)initialZSlider.Value;
valueDict.Add(VisWireframeAnimation.P_EULER_ROT_X, rotX);
valueDict.Add(VisWireframeAnimation.P_EULER_ROT_Y, rotY);
valueDict.Add(VisWireframeAnimation.P_EULER_ROT_Z, rotZ);
}
return new ReadOnlyDictionary<string, object>(valueDict);
}
@ -446,26 +494,28 @@ namespace SourceGen.WpfGui {
VisualizationItem item = (VisualizationItem)visComboBox.SelectedItem;
BitmapDimensions = "?";
previewGrid.Background = null;
wireframePath.Data = new GeometryGroup();
if (!IsValid || item == null) {
previewImage.Source = sBadParamsImage;
previewGrid.Background = null;
wireframePath.Data = new GeometryGroup();
} else {
// Invoke the plugin.
PluginErrMessage = string.Empty;
IVisualization2d vis2d = null;
IVisualizationWireframe visWire = null;
ReadOnlyDictionary<string, object> parms = CreateVisGenParams();
ReadOnlyDictionary<string, object> parms = null;
try {
IPlugin_Visualizer plugin =
(IPlugin_Visualizer)mProject.GetPlugin(item.ScriptIdent);
if (item.VisDescriptor.VisualizationType == VisDescr.VisType.Bitmap) {
parms = CreateVisGenParams(false);
vis2d = plugin.Generate2d(item.VisDescriptor, parms);
if (vis2d == null) {
Debug.WriteLine("Vis2d generator returned null");
}
} else if (item.VisDescriptor.VisualizationType == VisDescr.VisType.Wireframe) {
parms = CreateVisGenParams(true);
IPlugin_Visualizer_v2 plugin2 = (IPlugin_Visualizer_v2)plugin;
visWire = plugin2.GenerateWireframe(item.VisDescriptor, parms);
if (visWire == null) {
@ -496,16 +546,15 @@ namespace SourceGen.WpfGui {
BitmapDimensions = string.Format("{0}x{1}",
previewImage.Source.Width, previewImage.Source.Height);
mThumbnail = (BitmapSource)previewImage.Source;
mVisObj = vis2d;
} else {
previewGrid.Background = Brushes.Black;
previewImage.Source = Visualization.BLANK_IMAGE;
wireframePath.Data = Visualization.GenerateWireframePath(visWire, parms,
previewImage.ActualWidth / 2);
Math.Min(previewImage.ActualWidth, previewImage.ActualHeight) / 2);
BitmapDimensions = "n/a";
mThumbnail = Visualization.GenerateWireframeImage(visWire, parms,
Visualization.THUMBNAIL_DIM);
mVisObj = visWire;
}
}
@ -533,6 +582,16 @@ namespace SourceGen.WpfGui {
}
Debug.WriteLine("VisComboBox sel change: " + item.VisDescriptor.Ident);
GenerateParamControls(item.VisDescriptor);
if (mOrigVis != null) {
initialXSlider.Value = Util.GetFromObjDict(mOrigVis.VisGenParams,
VisWireframeAnimation.P_EULER_ROT_X, 0);
initialYSlider.Value = Util.GetFromObjDict(mOrigVis.VisGenParams,
VisWireframeAnimation.P_EULER_ROT_Y, 0);
initialZSlider.Value = Util.GetFromObjDict(mOrigVis.VisGenParams,
VisWireframeAnimation.P_EULER_ROT_Z, 0);
}
UpdateControls();
}
@ -549,6 +608,15 @@ namespace SourceGen.WpfGui {
//Debug.WriteLine("CHECK CHANGE" + pv);
UpdateControls();
}
private void InitialRotSlider_ValueChanged(object sender,
RoutedPropertyChangedEventArgs<double> e) {
UpdateControls();
}
private void TestAnim_Click(object sender, RoutedEventArgs e) {
Debug.WriteLine("TEST!");
}
}
/// <summary>

View File

@ -221,9 +221,9 @@ namespace SourceGen.WpfGui {
Visualization item = (Visualization)visualizationGrid.SelectedItem;
Visualization newVis;
if (item is VisualizationAnimation) {
if (item is VisBitmapAnimation) {
EditBitmapAnimation dlg = new EditBitmapAnimation(this, mOffset,
CreateEditedSetList(), (VisualizationAnimation)item);
CreateEditedSetList(), (VisBitmapAnimation)item);
if (dlg.ShowDialog() != true) {
return;
}
@ -264,18 +264,18 @@ namespace SourceGen.WpfGui {
// (This is a bit awkward because we can't modify VisualizationList while iterating
// through it, and there's no simple "replace entry" operation on an observable
// collection. Fortunately we don't do this often and the data sets are small.)
List<VisualizationAnimation> needsUpdate = new List<VisualizationAnimation>();
List<VisBitmapAnimation> needsUpdate = new List<VisBitmapAnimation>();
foreach (Visualization vis in VisualizationList) {
if (vis is VisualizationAnimation) {
VisualizationAnimation visAnim = (VisualizationAnimation)vis;
if (vis is VisBitmapAnimation) {
VisBitmapAnimation visAnim = (VisBitmapAnimation)vis;
if (visAnim.ContainsSerial(item.SerialNumber)) {
needsUpdate.Add(visAnim);
}
}
}
foreach (VisualizationAnimation visAnim in needsUpdate) {
VisualizationAnimation newAnim;
if (VisualizationAnimation.StripEntries(visAnim,
foreach (VisBitmapAnimation visAnim in needsUpdate) {
VisBitmapAnimation newAnim;
if (VisBitmapAnimation.StripEntries(visAnim,
new List<int>(1) { item.SerialNumber }, out newAnim)) {
if (newAnim.Count == 0) {
VisualizationList.Remove(visAnim);