Serialize animations to project file

We now store Visualizations, VisualizationAnimations, and
VisualizationSets as three separate lists linked by tag strings.

WARNING: this breaks existing projects with visualizations.  The
test projects have been updated.
This commit is contained in:
Andy McFadden 2019-12-22 16:56:57 -08:00
parent 4b19a029a9
commit 226ba7c9a3
11 changed files with 284 additions and 116 deletions

View File

@ -90,6 +90,10 @@ namespace CommonWPF {
}
public void Start() {
if (mBitmaps == null) {
throw new InvalidOperationException("Must set bitmaps before starting");
}
Tick(null, null); // show something immediately
mTimer.Start();
}
@ -98,9 +102,6 @@ namespace CommonWPF {
}
private void Tick(object sender, EventArgs e) {
if (mBitmaps == null) {
throw new InvalidOperationException("Must set bitmaps before starting");
}
if (mNext >= mBitmaps.Count) {
mNext = 0;
}

View File

@ -372,14 +372,29 @@ namespace SourceGen {
VisGenParams = new Dictionary<string, object>(vis.VisGenParams);
}
}
public class SerVisualizationAnimation : SerVisualization {
public List<string> Tags { get; set; }
public SerVisualizationAnimation() { }
public SerVisualizationAnimation(VisualizationAnimation visAnim,
SortedList<int, VisualizationSet> visSets)
: base(visAnim) {
Tags = new List<string>(visAnim.Count);
for (int i = 0; i < visAnim.Count; i++) {
Visualization vis =
VisualizationSet.FindVisualizationBySerial(visSets, visAnim[i]);
Tags.Add(vis.Tag);
}
}
}
public class SerVisualizationSet {
public List<SerVisualization> Items { get; set; }
public List<string> Tags { get; set; }
public SerVisualizationSet() { }
public SerVisualizationSet(VisualizationSet visSet) {
Items = new List<SerVisualization>(visSet.Count);
Tags = new List<string>(visSet.Count);
foreach (Visualization vis in visSet) {
Items.Add(new SerVisualization(vis));
Tags.Add(vis.Tag);
}
}
}
@ -398,6 +413,8 @@ namespace SourceGen {
public Dictionary<string, SerSymbol> UserLabels { get; set; }
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 Dictionary<string, SerVisualizationSet> VisualizationSets { get; set; }
/// <summary>
@ -488,8 +505,20 @@ namespace SourceGen {
spf.LvTables.Add(kvp.Key.ToString(), new SerLocalVariableTable(kvp.Value));
}
// Output Visualizations, VisualizationAnimations, and VisualizationSets
spf.Visualizations = new List<SerVisualization>();
spf.VisualizationAnimations = new List<SerVisualizationAnimation>();
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,
proj.VisualizationSets));
} else {
spf.Visualizations.Add(new SerVisualization(vis));
}
}
spf.VisualizationSets.Add(kvp.Key.ToString(), new SerVisualizationSet(kvp.Value));
}
@ -745,21 +774,60 @@ namespace SourceGen {
}
// Deserialize visualization sets. These were added in v1.5.
if (spf.VisualizationSets != null) {
if (spf.VisualizationSets != null && spf.Visualizations != null) {
Dictionary<string, Visualization> visDict =
new Dictionary<string, Visualization>(spf.Visualizations.Count);
// Extract the Visualizations.
foreach (SerVisualization serVis in spf.Visualizations) {
if (CreateVisualization(serVis, report, out Visualization vis)) {
try {
visDict.Add(vis.Tag, vis);
} catch (ArgumentException) {
string str = string.Format(Res.Strings.ERR_BAD_VISUALIZATION_FMT,
"duplicate tag " + vis.Tag);
report.Add(FileLoadItem.Type.Warning, str);
continue;
}
}
}
// Extract the VisualizationAnimations, which link to Visualizations by tag.
foreach (SerVisualizationAnimation serVisAnim in spf.VisualizationAnimations) {
if (CreateVisualizationAnimation(serVisAnim, visDict, report,
out VisualizationAnimation visAnim)) {
try {
visDict.Add(visAnim.Tag, visAnim);
} catch (ArgumentException) {
string str = string.Format(Res.Strings.ERR_BAD_VISUALIZATION_FMT,
"duplicate tag " + visAnim.Tag);
report.Add(FileLoadItem.Type.Warning, str);
continue;
}
}
}
// Extract the VisualizationSets, which link to Visualizations of all types by tag.
foreach (KeyValuePair<string, SerVisualizationSet> kvp in spf.VisualizationSets) {
if (!ParseValidateKey(kvp.Key, spf.FileDataLength,
Res.Strings.PROJECT_FIELD_LV_TABLE, report, out int intKey)) {
continue;
}
if (!CreateVisualizationSet(kvp.Value, report, out VisualizationSet visSet)) {
if (!CreateVisualizationSet(kvp.Value, visDict, report,
out VisualizationSet visSet)) {
report.Add(FileLoadItem.Type.Warning,
string.Format(Res.Strings.ERR_BAD_VISUALIZATION_SET_FMT, intKey));
continue;
}
proj.VisualizationSets[intKey] = visSet;
}
if (visDict.Count != 0) {
// We remove visualizations as we add them to sets, so this indicates a
// problem.
Debug.WriteLine("WARNING: visDict still has " + visDict.Count + " entries");
}
}
@ -973,39 +1041,115 @@ namespace SourceGen {
return true;
}
private static bool CreateVisualizationSet(SerVisualizationSet serVisSet,
FileLoadReport report, out VisualizationSet outVisSet) {
outVisSet = new VisualizationSet();
foreach (SerVisualization serVis in serVisSet.Items) {
string trimTag = Visualization.TrimAndValidateTag(serVis.Tag, out bool isTagValid);
if (!isTagValid) {
Debug.WriteLine("Visualization with invalid tag: " + serVis.Tag);
string str = string.Format(Res.Strings.ERR_BAD_VISUALIZATION_FMT, trimTag);
report.Add(FileLoadItem.Type.Warning, str);
continue;
}
if (string.IsNullOrEmpty(serVis.VisGenIdent) || serVis.VisGenParams == null) {
/// <summary>
/// Creates a Visualization from its serialized form.
/// </summary>
private static bool CreateVisualization(SerVisualization serVis, FileLoadReport report,
out Visualization vis) {
if (!CheckVis(serVis, report, out Dictionary<string, object> parms)) {
vis = null;
return false;
}
vis = new Visualization(serVis.Tag, serVis.VisGenIdent,
new ReadOnlyDictionary<string, object>(parms));
return true;
}
/// <summary>
/// Creates a VisualizationAnimation from its serialized form.
/// </summary>
private static bool CreateVisualizationAnimation(SerVisualizationAnimation serVisAnim,
Dictionary<string, Visualization> visList, FileLoadReport report,
out VisualizationAnimation visAnim) {
if (!CheckVis(serVisAnim, report, out Dictionary<string, object> parms)) {
visAnim = null;
return false;
}
List<int> serialNumbers = new List<int>(serVisAnim.Tags.Count);
foreach (string tag in serVisAnim.Tags) {
if (!visList.TryGetValue(tag, out Visualization vis)) {
string str = string.Format(Res.Strings.ERR_BAD_VISUALIZATION_FMT,
"ident/params");
"unknown tag in animation: " + tag);
report.Add(FileLoadItem.Type.Warning, str);
continue;
}
if (vis is VisualizationAnimation) {
string str = string.Format(Res.Strings.ERR_BAD_VISUALIZATION_FMT,
"animation in animation: " + tag);
report.Add(FileLoadItem.Type.Warning, str);
continue;
}
serialNumbers.Add(vis.SerialNumber);
}
visAnim = new VisualizationAnimation(serVisAnim.Tag, serVisAnim.VisGenIdent,
new ReadOnlyDictionary<string, object>(parms), serialNumbers, null);
return true;
}
/// <summary>
/// Checks for errors common to Visualization objects. Generates a replacement
/// parameter object to work around JavaScript type conversion.
/// </summary>
private static bool CheckVis(SerVisualization serVis, FileLoadReport report,
out Dictionary<string, object> parms) {
parms = null;
string unused = Visualization.TrimAndValidateTag(serVis.Tag, out bool isTagValid);
if (!isTagValid) {
Debug.WriteLine("Visualization with invalid tag: " + serVis.Tag);
string str = string.Format(Res.Strings.ERR_BAD_VISUALIZATION_FMT, serVis.Tag);
report.Add(FileLoadItem.Type.Warning, str);
return false;
}
if (string.IsNullOrEmpty(serVis.VisGenIdent) || serVis.VisGenParams == null) {
string str = string.Format(Res.Strings.ERR_BAD_VISUALIZATION_FMT,
"ident/params");
report.Add(FileLoadItem.Type.Warning, str);
return false;
}
// The JavaScript deserialization turns floats into Decimal. Change it back
// so we don't have to deal with it later.
parms = new Dictionary<string, object>(serVis.VisGenParams.Count);
foreach (KeyValuePair<string, object> kvp in serVis.VisGenParams) {
object val = kvp.Value;
if (val is decimal) {
val = (double)((decimal)val);
}
parms.Add(kvp.Key, val);
}
return true;
}
/// <summary>
/// Creates a VisualizationSet from its serialized form.
/// </summary>
private static bool CreateVisualizationSet(SerVisualizationSet serVisSet,
Dictionary<string, Visualization> visList, FileLoadReport report,
out VisualizationSet outVisSet) {
outVisSet = new VisualizationSet();
foreach (string rawTag in serVisSet.Tags) {
string trimTag = Visualization.TrimAndValidateTag(rawTag, out bool isTagValid);
if (!isTagValid) {
Debug.WriteLine("VisualizationSet with invalid tag: " + rawTag);
string str = string.Format(Res.Strings.ERR_BAD_VISUALIZATION_FMT, rawTag);
report.Add(FileLoadItem.Type.Warning, str);
continue;
}
if (!visList.TryGetValue(trimTag, out Visualization vis)) {
Debug.WriteLine("VisSet ref to unknown tag: " + trimTag);
string str = string.Format(Res.Strings.ERR_BAD_VISUALIZATION_FMT,
"unknown tag: " + trimTag);
report.Add(FileLoadItem.Type.Warning, str);
continue;
}
// The JavaScript deserialization turns floats into Decimal. Change it back
// so we don't have to deal with it later.
Dictionary<string, object> parms =
new Dictionary<string, object>(serVis.VisGenParams.Count);
foreach (KeyValuePair<string, object> kvp in serVis.VisGenParams) {
object val = kvp.Value;
if (val is decimal) {
val = (double)((decimal)val);
}
parms.Add(kvp.Key, val);
}
Visualization vis = new Visualization(serVis.Tag, serVis.VisGenIdent,
new ReadOnlyDictionary<string, object>(parms));
outVisSet.Add(vis);
// Each Visualization should only appear in one VisualizationSet. Things
// might get weird when we remove one if this isn't true. So we remove
// it from the dictionary.
visList.Remove(trimTag);
}
return true;
}

View File

@ -20,9 +20,7 @@
},
"LongComments":{
"222":{
"Text":"\r\nPurple/green diagonal\r\n\r\n","BoxMode":false,"MaxWidth":80,"BackgroundColor":0},
"0":{
"Text":"Some images:","BoxMode":false,"MaxWidth":80,"BackgroundColor":0}},
"Text":"\r\nPurple/green diagonal\r\n\r\n","BoxMode":false,"MaxWidth":80,"BackgroundColor":0}},
"Notes":{
"222":{
"Text":"Bitmaps here","BoxMode":false,"MaxWidth":80,"BackgroundColor":-256}},
@ -45,9 +43,7 @@
"Length":64,"Format":"Dense","SubFormat":"None","SymbolRef":null}},
"LvTables":{
},
"VisualizationSets":{
"0":{
"Items":[{
"Visualizations":[{
"Tag":"multi1","VisGenIdent":"apple2-hi-res-bitmap","VisGenParams":{
"offset":222,"byteWidth":1,"height":8,"colStride":0,"rowStride":0,"isColor":true,"isFirstOdd":false}},
{
@ -55,31 +51,38 @@
"offset":230,"byteWidth":1,"height":8,"colStride":0,"rowStride":0,"isColor":true,"isFirstOdd":false}},
{
"Tag":"multi3","VisGenIdent":"apple2-hi-res-bitmap","VisGenParams":{
"offset":238,"byteWidth":12,"height":1,"colStride":0,"rowStride":0,"isColor":false,"isFirstOdd":false}},
"offset":238,"byteWidth":3,"height":2,"colStride":0,"rowStride":0,"isColor":false,"isFirstOdd":false}},
{
"Tag":"multi4","VisGenIdent":"apple2-hi-res-bitmap","VisGenParams":{
"offset":270,"byteWidth":3,"height":8,"colStride":0,"rowStride":0,"isColor":true,"isFirstOdd":false}}]},
"222":{
"Items":[{
"Tag":"vis0000de","VisGenIdent":"apple2-hi-res-bitmap","VisGenParams":{
"offset":222,"byteWidth":1,"height":8,"colStride":0,"rowStride":0,"isColor":true,"isFirstOdd":false}}]},
"230":{
"Items":[{
"offset":222,"byteWidth":1,"height":8,"colStride":0,"rowStride":0,"isColor":true,"isFirstOdd":false}},
{
"Tag":"vis0000e6","VisGenIdent":"apple2-hi-res-bitmap","VisGenParams":{
"offset":230,"byteWidth":1,"height":8,"colStride":0,"rowStride":0,"isColor":true,"isFirstOdd":true}}]},
"238":{
"Items":[{
"offset":230,"byteWidth":1,"height":8,"colStride":0,"rowStride":0,"isColor":true,"isFirstOdd":true}},
{
"Tag":"vis0000ee","VisGenIdent":"apple2-hi-res-bitmap","VisGenParams":{
"offset":238,"byteWidth":2,"height":8,"colStride":0,"rowStride":0,"isColor":true,"isFirstOdd":false}}]},
"254":{
"Items":[{
"offset":238,"byteWidth":2,"height":8,"colStride":0,"rowStride":0,"isColor":true,"isFirstOdd":false}},
{
"Tag":"vis0000fe","VisGenIdent":"apple2-hi-res-bitmap","VisGenParams":{
"offset":254,"byteWidth":2,"height":8,"colStride":0,"rowStride":0,"isColor":true,"isFirstOdd":false}}]},
"270":{
"Items":[{
"offset":254,"byteWidth":2,"height":8,"colStride":0,"rowStride":0,"isColor":true,"isFirstOdd":false}},
{
"Tag":"vis00010e","VisGenIdent":"apple2-hi-res-bitmap","VisGenParams":{
"offset":270,"byteWidth":3,"height":8,"colStride":0,"rowStride":0,"isColor":true,"isFirstOdd":false}}]},
"294":{
"Items":[{
"offset":270,"byteWidth":3,"height":8,"colStride":0,"rowStride":0,"isColor":false,"isFirstOdd":false}},
{
"Tag":"vis000126","VisGenIdent":"apple2-hi-res-bitmap","VisGenParams":{
"offset":294,"byteWidth":3,"height":8,"colStride":2,"rowStride":8,"isColor":true,"isFirstOdd":false}}]}}}
"offset":294,"byteWidth":3,"height":8,"colStride":2,"rowStride":8,"isColor":true,"isFirstOdd":false}}],"VisualizationAnimations":[{
"Tags":["multi2","vis0000e6","vis0000de","vis0000ee","vis0000de"],"Tag":"anim000012","VisGenIdent":"(animation)","VisGenParams":{
"frame-delay-msec":400}}],"VisualizationSets":{
"18":{
"Tags":["anim000012","multi1","multi2","multi3"]},
"222":{
"Tags":["vis0000de"]},
"230":{
"Tags":["vis0000e6"]},
"238":{
"Tags":["vis0000ee"]},
"254":{
"Tags":["vis0000fe"]},
"270":{
"Tags":["vis00010e"]},
"294":{
"Tags":["vis000126"]}}}

View File

@ -22,8 +22,8 @@
"Length":8184,"Format":"Dense","SubFormat":"None","SymbolRef":null}},
"LvTables":{
},
"VisualizationSets":{
"Visualizations":[{
"Tag":"vis000000","VisGenIdent":"apple2-hi-res-screen","VisGenParams":{
"offset":0,"isColor":true}}],"VisualizationAnimations":[],"VisualizationSets":{
"0":{
"Items":[{
"Tag":"full screen image","VisGenIdent":"apple2-hi-res-screen","VisGenParams":{
"offset":0,"isColor":true}}]}}}
"Tags":["vis000000"]}}}

View File

@ -44,28 +44,35 @@
"Length":21,"Format":"Dense","SubFormat":"None","SymbolRef":null}},
"LvTables":{
},
"VisualizationSets":{
"Visualizations":[{
"Tag":"vis000014","VisGenIdent":"atari2600-sprite","VisGenParams":{
"offset":20,"height":3}},
{
"Tag":"vis000018","VisGenIdent":"atari2600-sprite","VisGenParams":{
"offset":24,"height":20}},
{
"Tag":"vis00002d","VisGenIdent":"atari2600-sprite","VisGenParams":{
"offset":45,"height":22}},
{
"Tag":"vis000044","VisGenIdent":"atari2600-playfield","VisGenParams":{
"offset":68,"height":7,"rowThickness":4,"reflected":false}},
{
"Tag":"vis000059","VisGenIdent":"atari2600-playfield","VisGenParams":{
"offset":89,"height":7,"rowThickness":4,"reflected":true}},
{
"Tag":"vis00006e","VisGenIdent":"atari2600-playfield","VisGenParams":{
"offset":110,"height":7,"rowThickness":4,"reflected":true}}],"VisualizationAnimations":[{
"Tags":["vis000018","vis00002d"],"Tag":"anim00002d","VisGenIdent":"(animation)","VisGenParams":{
"frame-delay-msec":500}}],"VisualizationSets":{
"20":{
"Items":[{
"Tag":"Key sprite","VisGenIdent":"atari2600-sprite","VisGenParams":{
"offset":20,"height":3}}]},
"Tags":["vis000014"]},
"24":{
"Items":[{
"Tag":"Dragon 0","VisGenIdent":"atari2600-sprite","VisGenParams":{
"offset":24,"height":20}}]},
"Tags":["vis000018"]},
"45":{
"Items":[{
"Tag":"Dragon 1","VisGenIdent":"atari2600-sprite","VisGenParams":{
"offset":45,"height":22}}]},
"Tags":["vis00002d","anim00002d"]},
"68":{
"Items":[{
"Tag":"Black Maze 3","VisGenIdent":"atari2600-playfield","VisGenParams":{
"offset":68,"height":7,"rowThickness":4,"reflected":false}}]},
"Tags":["vis000044"]},
"89":{
"Items":[{
"Tag":"Red Maze (bottom)","VisGenIdent":"atari2600-playfield","VisGenParams":{
"offset":89,"height":7,"rowThickness":4,"reflected":true}}]},
"Tags":["vis000059"]},
"110":{
"Items":[{
"Tag":"Castle Def","VisGenIdent":"atari2600-playfield","VisGenParams":{
"offset":110,"height":7,"rowThickness":4,"reflected":true}}]}}}
"Tags":["vis00006e"]}}}

View File

@ -38,20 +38,25 @@
"Length":1792,"Format":"Junk","SubFormat":"None","SymbolRef":null}},
"LvTables":{
},
"VisualizationSets":{
"0":{
"Items":[{
"Visualizations":[{
"Tag":"vis000000","VisGenIdent":"c64-multi-color-sprite","VisGenParams":{
"offset":0,"color":3,"color01":6,"color11":10,"doubleWide":false,"doubleHigh":false}}]},
"64":{
"Items":[{
"offset":0,"color":3,"color01":6,"color11":10,"doubleWide":false,"doubleHigh":false}},
{
"Tag":"vis000040","VisGenIdent":"c64-hi-res-sprite","VisGenParams":{
"offset":64,"color":14,"doubleWide":false,"doubleHigh":false}}]},
"128":{
"Items":[{
"offset":64,"color":14,"doubleWide":false,"doubleHigh":false}},
{
"Tag":"vis000080","VisGenIdent":"c64-multi-color-sprite","VisGenParams":{
"offset":128,"color":4,"color01":6,"color11":10,"doubleWide":true,"doubleHigh":false}}]},
"192":{
"Items":[{
"offset":128,"color":4,"color01":6,"color11":10,"doubleWide":true,"doubleHigh":false}},
{
"Tag":"vis0000c0","VisGenIdent":"c64-hi-res-sprite","VisGenParams":{
"offset":192,"color":5,"doubleWide":true,"doubleHigh":true}}]}}}
"offset":192,"color":5,"doubleWide":true,"doubleHigh":true}}],"VisualizationAnimations":[{
"Tags":["vis000040","vis000000","vis000080","vis0000c0"],"Tag":"anim000000","VisGenIdent":"(animation)","VisGenParams":{
"frame-delay-msec":800}}],"VisualizationSets":{
"0":{
"Tags":["vis000000","anim000000"]},
"64":{
"Tags":["vis000040"]},
"128":{
"Tags":["vis000080"]},
"192":{
"Tags":["vis0000c0"]}}}

View File

@ -83,13 +83,6 @@ namespace SourceGen {
CachedImage = ANIM_OVERLAY_IMAGE; // default to this
}
/// <summary>
/// The number of Visualizations linked from this animation.
/// </summary>
public int SerialCount {
get { return mSerialNumbers.Count; }
}
public void GenerateImage(SortedList<int, VisualizationSet> visSets) {
const int IMAGE_SIZE = 64;
@ -126,10 +119,22 @@ namespace SourceGen {
}
/// <summary>
/// Returns a list of serial numbers. The caller must not modify the list.
/// The number of Visualizations linked from this animation.
/// </summary>
public List<int> GetSerialNumbers() {
return mSerialNumbers;
/// <remarks>
/// Visualizations may appear more than once in the list. Each instance is counted.
/// </remarks>
public int Count {
get { return mSerialNumbers.Count; }
}
/// <summary>
/// Indexes the serial number list.
/// </summary>
public int this[int index] {
get {
return mSerialNumbers[index];
}
}
/// <summary>
@ -226,7 +231,6 @@ namespace SourceGen {
if (!base.Equals(obj)) {
return false;
}
Debug.WriteLine("Detailed: this=" + Tag + " other=" + Tag);
VisualizationAnimation other = (VisualizationAnimation)obj;
if (other.mSerialNumbers.Count != mSerialNumbers.Count) {
return false;
@ -236,7 +240,6 @@ namespace SourceGen {
return false;
}
}
Debug.WriteLine(" All serial numbers match");
return true;
}
public override int GetHashCode() {

View File

@ -126,7 +126,7 @@ namespace SourceGen {
if (VisualizationAnimation.StripEntries((VisualizationAnimation) vis,
removedSerials, out VisualizationAnimation newAnim)) {
somethingRemoved = true;
if (newAnim.SerialCount != 0) {
if (newAnim.Count != 0) {
newSet.Add(newAnim);
} else {
Debug.WriteLine("Deleting empty animation " + vis.Tag);

View File

@ -148,8 +148,9 @@ namespace SourceGen.WpfGui {
FrameDelayTimeMsec = DEFAULT_FRAME_DELAY.ToString();
if (origAnim != null) {
TagString = origAnim.Tag;
int frameDelay = PluginCommon.Util.GetFromObjDict(origAnim.VisGenParams,
mFrameDelayIntMsec = PluginCommon.Util.GetFromObjDict(origAnim.VisGenParams,
VisualizationAnimation.FRAME_DELAY_MSEC_PARAM, DEFAULT_FRAME_DELAY);
FrameDelayTimeMsec = mFrameDelayIntMsec.ToString();
} else {
TagString = "anim" + mSetOffset.ToString("x6");
}
@ -160,13 +161,17 @@ namespace SourceGen.WpfGui {
private void PopulateItemLists() {
// Add the animation's visualizations, in order.
if (mOrigAnim != null) {
foreach (int serial in mOrigAnim.GetSerialNumbers()) {
for (int i = 0; i < mOrigAnim.Count; i++) {
int serial = mOrigAnim[i];
Visualization vis = VisualizationSet.FindVisualizationBySerial(mEditedList,
serial);
if (vis != null) {
VisAnimItems.Add(vis);
} else {
Debug.Assert(false);
// Could happen if the Visualization exists but isn't referenced by
// any VisualizationSets. Shouldn't happen unless the project file
// was damaged. Silently ignore it.
Debug.WriteLine("WARNING: unknown vis serial " + serial);
}
}
}

View File

@ -87,7 +87,7 @@ limitations under the License.
</DataGrid>
<StackPanel Grid.Column="1" Grid.Row="1">
<Button Width="110" Margin="4" Content="_New Bitmap..."
<Button Width="110" Height="36" Margin="4" Content="_New Bitmap..."
IsEnabled="{Binding HasVisPlugins}" Click="NewBitmapButton_Click"/>
<Button Width="110" Margin="4" Content="_New Bitmap&#x0a;Animation..."
Click="NewBitmapAnimationButton_Click"/>

View File

@ -257,7 +257,7 @@ namespace SourceGen.WpfGui {
VisualizationAnimation newAnim;
if (VisualizationAnimation.StripEntries(visAnim,
new List<int>(1) { item.SerialNumber }, out newAnim)) {
if (newAnim.SerialCount == 0) {
if (newAnim.Count == 0) {
VisualizationList.Remove(visAnim);
} else {
index = VisualizationList.IndexOf(visAnim);