mirror of
https://github.com/fadden/6502bench.git
synced 2025-02-17 17:30:29 +00:00
Add rotation and backface culling
Also, correctly update the thumbnail when leaving the visualization editor.
This commit is contained in:
parent
eec847d5f1
commit
b686d2d208
125
CommonUtil/Matrix44.cs
Normal file
125
CommonUtil/Matrix44.cs
Normal file
@ -0,0 +1,125 @@
|
||||
/*
|
||||
* 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.Text;
|
||||
|
||||
namespace CommonUtil {
|
||||
/// <summary>
|
||||
/// Simple 4x4 matrix.
|
||||
/// </summary>
|
||||
public class Matrix44 {
|
||||
public double[,] Val {
|
||||
get { return mVal; }
|
||||
private set { mVal = value; }
|
||||
}
|
||||
|
||||
private double[,] mVal;
|
||||
|
||||
public Matrix44() {
|
||||
Val = new double[4, 4];
|
||||
}
|
||||
|
||||
public void Clear() {
|
||||
for (int col = 0; col < 4; col++) {
|
||||
for (int row = 0; row < 4; row++) {
|
||||
Val[col, row] = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetToIdentity() {
|
||||
Clear();
|
||||
Val[0, 0] = Val[1, 1] = Val[2, 2] = Val[3, 3] = 1.0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the matrix to perform rotation about Euler angles in the order X, Y, Z.
|
||||
/// </summary>
|
||||
/// <param name="xdeg">Rotation about the X axis, in degrees.</param>
|
||||
/// <param name="ydeg">Rotation about the Y axis, in degrees.</param>
|
||||
/// <param name="zdeg">Rotation about the Z axis, in degrees.</param>
|
||||
public void SetRotationEuler(int xdeg, int ydeg, int zdeg) {
|
||||
const double degToRad = Math.PI / 180.0;
|
||||
double xrad = xdeg * degToRad;
|
||||
double yrad = ydeg * degToRad;
|
||||
double zrad = zdeg * degToRad;
|
||||
|
||||
double cx = Math.Cos(xrad);
|
||||
double sx = Math.Sin(xrad);
|
||||
double cy = Math.Cos(yrad);
|
||||
double sy = Math.Sin(yrad);
|
||||
double cz = Math.Cos(zrad);
|
||||
double sz = Math.Sin(zrad);
|
||||
double sycx = sy * cx;
|
||||
double sysx = sy * sx;
|
||||
|
||||
bool useXyz = false;
|
||||
if (useXyz) {
|
||||
// R = Rz * Ry * Rx (from wikipedia)
|
||||
Val[0, 0] = cz * cy;
|
||||
Val[0, 1] = sz * cy;
|
||||
Val[0, 2] = -sy;
|
||||
|
||||
Val[1, 0] = cz * sysx - sz * cx;
|
||||
Val[1, 1] = sz * sysx + cz * cx;
|
||||
Val[1, 2] = cy * sx;
|
||||
|
||||
Val[2, 0] = cz * sycx + sz * sx;
|
||||
Val[2, 1] = sz * sycx - cz * sx;
|
||||
Val[2, 2] = cy * cx;
|
||||
} else {
|
||||
// R = Rx * Ry * Rz (from Arc3D)
|
||||
Val[0, 0] = cz * cy;
|
||||
Val[0, 1] = -sz * cy;
|
||||
Val[0, 2] = sy;
|
||||
|
||||
Val[1, 0] = cz * sysx + sz * cx;
|
||||
Val[1, 1] = -sz * sysx + cz * cx;
|
||||
Val[1, 2] = -cy * sx;
|
||||
|
||||
Val[2, 0] = -cz * sycx + sz * sx;
|
||||
Val[2, 1] = sz * sycx + cz * sx;
|
||||
Val[2, 2] = cy * cx;
|
||||
}
|
||||
//Val[0, 3] = Val[1, 3] = Val[2, 3] = Val[3, 0] = Val[3, 1] = Val[3, 2] = 0.0;
|
||||
Val[3, 3] = 1.0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies a 3-element vector. The vector's 4th element is implicitly set to 1.
|
||||
/// </summary>
|
||||
/// <param name="vec">Column vector to multiply.</param>
|
||||
/// <returns>Result vector.</returns>
|
||||
public Vector3 Multiply(Vector3 vec) {
|
||||
double rx = vec.X * Val[0, 0] + vec.Y * Val[1, 0] + vec.Z * Val[2, 0] + Val[3, 0];
|
||||
double ry = vec.X * Val[0, 1] + vec.Y * Val[1, 1] + vec.Z * Val[2, 1] + Val[3, 1];
|
||||
double rz = vec.X * Val[0, 2] + vec.Y * Val[1, 2] + vec.Z * Val[2, 2] + Val[3, 2];
|
||||
return new Vector3(rx, ry, rz);
|
||||
}
|
||||
|
||||
|
||||
public override string ToString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int row = 0; row < 4; row++) {
|
||||
sb.AppendFormat("|{0,8:N3} {1,8:N3} {2,8:N3} {3,8:N3}|",
|
||||
Val[0, row], Val[1, row], Val[2, row], Val[3, row]);
|
||||
sb.AppendLine();
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
@ -51,5 +51,20 @@ namespace CommonUtil {
|
||||
mY *= len_r;
|
||||
mZ *= len_r;
|
||||
}
|
||||
|
||||
public void Multiply(double sc) {
|
||||
mX *= sc;
|
||||
mY *= sc;
|
||||
mZ *= sc;
|
||||
}
|
||||
|
||||
public static double Dot(Vector3 v0, Vector3 v1) {
|
||||
return v0.X * v1.X + v0.Y * v1.Y + v0.Z * v1.Z;
|
||||
}
|
||||
|
||||
|
||||
public override string ToString() {
|
||||
return string.Format("|{0,8:N3} {1,8:N3} {2,8:N3}|", X, Y, Z);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -333,11 +333,12 @@ namespace PluginCommon {
|
||||
/// All objects will have vertices and edges. Face normals are optional.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The face-normal stuff is designed specifically for Elite, which is one of the very
|
||||
/// few games to use backface removal.
|
||||
/// The face-normal stuff is designed specifically for Elite. Besides being one of the
|
||||
/// very few 6502-based games to use backface culling, it extended the concept to allow
|
||||
/// convex shapes to have protrusions.
|
||||
///
|
||||
/// We favor multiple arrays over compound objects for this interface to avoid having
|
||||
/// to define those at the plugin interface level.
|
||||
/// We favor multiple arrays over compound objects for this interface to avoid making
|
||||
/// such objects part of the plugin interface.
|
||||
///
|
||||
/// TODO(maybe): specify colors for edges. Not widely used?
|
||||
/// </remarks>
|
||||
|
@ -26,7 +26,7 @@ namespace PluginCommon {
|
||||
[Serializable]
|
||||
public class VisWireframe : IVisualizationWireframe {
|
||||
public const string P_IS_PERSPECTIVE = "_isPerspective";
|
||||
public const string P_IS_BACKFACE_REMOVED = "_isBackfaceRemoved";
|
||||
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";
|
||||
@ -34,8 +34,8 @@ namespace PluginCommon {
|
||||
public static VisParamDescr Param_IsPerspective(string uiLabel, bool defaultVal) {
|
||||
return new VisParamDescr(uiLabel, P_IS_PERSPECTIVE, typeof(bool), 0, 0, 0, defaultVal);
|
||||
}
|
||||
public static VisParamDescr Param_IsBackfaceRemoved(string uiLabel, bool defaultVal) {
|
||||
return new VisParamDescr(uiLabel, P_IS_BACKFACE_REMOVED, typeof(bool), 0, 0, 0, defaultVal);
|
||||
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);
|
||||
@ -183,6 +183,9 @@ namespace PluginCommon {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(maybe): confirm that every face has a vertex. Not strictly necessary
|
||||
// since you can do orthographic-projection BFC without it... but who does that?
|
||||
|
||||
msg = string.Empty;
|
||||
return true;
|
||||
}
|
||||
|
@ -43,10 +43,11 @@ namespace WireframeTest {
|
||||
P_OFFSET, typeof(int), 0, 0x00ffffff, VisParamDescr.SpecialMode.Offset, 0),
|
||||
|
||||
// These are interpreted by the main app.
|
||||
VisWireframe.Param_EulerX("Euler rotation X", 0),
|
||||
VisWireframe.Param_EulerY("Euler rotation Y", 0),
|
||||
VisWireframe.Param_EulerZ("Euler rotation Z", 0),
|
||||
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),
|
||||
}),
|
||||
};
|
||||
|
||||
|
Binary file not shown.
@ -29,21 +29,21 @@ vertices
|
||||
|
||||
; List of edges (vertex0, vertex1, face0, face1).
|
||||
edges
|
||||
dfb 0,1, 0,1 ;0
|
||||
dfb 1,2, 0,3 ;1
|
||||
dfb 2,3, 0,4 ;2
|
||||
dfb 3,0, 0,2 ;3
|
||||
dfb 4,5, 1,5 ;4
|
||||
dfb 5,6, 1,3 ;5
|
||||
dfb 6,7, 1,4 ;6
|
||||
dfb 7,4, 1,2 ;7
|
||||
dfb 0,4, 2,5 ;8
|
||||
dfb 1,5, 3,5 ;9
|
||||
dfb 2,6, 3,4 ;10
|
||||
dfb 3,7, 2,4 ;11
|
||||
dfb 0,1, 0,5 ;0
|
||||
dfb 1,2, 0,3 ;1
|
||||
dfb 2,3, 0,4 ;2
|
||||
dfb 3,0, 0,2 ;3
|
||||
dfb 4,5, 1,5 ;4
|
||||
dfb 5,6, 1,3 ;5
|
||||
dfb 6,7, 1,4 ;6
|
||||
dfb 7,4, 1,2 ;7
|
||||
dfb 0,4, 2,5 ;8
|
||||
dfb 1,5, 3,5 ;9
|
||||
dfb 2,6, 3,4 ;10
|
||||
dfb 3,7, 2,4 ;11
|
||||
|
||||
dfb 8,9, 0,0 ;12
|
||||
dfb 9,10,0,0 ;13
|
||||
dfb 8,9, 0,0 ;12
|
||||
dfb 9,10, 0,0 ;13
|
||||
dfb $80
|
||||
|
||||
; List of faces (surface normal X,Y,Z).
|
||||
|
@ -2,7 +2,7 @@
|
||||
{
|
||||
"_ContentVersion":3,
|
||||
"FileDataLength":120,
|
||||
"FileDataCrc32":1432202158,
|
||||
"FileDataCrc32":1015994132,
|
||||
"ProjectProps":{
|
||||
"CpuName":"6502",
|
||||
"IncludeUndocumentedInstr":false,
|
||||
@ -245,10 +245,11 @@
|
||||
"VisGenIdent":"wireframe-test",
|
||||
"VisGenParams":{
|
||||
"offset":10,
|
||||
"_eulerRotX":0,
|
||||
"_eulerRotY":0,
|
||||
"_eulerRotX":15,
|
||||
"_eulerRotY":30,
|
||||
"_eulerRotZ":0,
|
||||
"_isPerspective":true}},
|
||||
"_isPerspective":true,
|
||||
"_isBfcEnabled":true}},
|
||||
|
||||
{
|
||||
"Tag":"bmp_data",
|
||||
|
@ -37,6 +37,8 @@ namespace SourceGen {
|
||||
/// that weren't changed while sitting in the undo buffer.
|
||||
/// </remarks>
|
||||
public class Visualization {
|
||||
public const double THUMBNAIL_DIM = 64;
|
||||
|
||||
/// <summary>
|
||||
/// Unique user-specified tag. This may be any valid string that is at least two
|
||||
/// characters long after the leading and trailing whitespace have been trimmed.
|
||||
@ -181,7 +183,7 @@ namespace SourceGen {
|
||||
ReadOnlyDictionary<string, object> parms) {
|
||||
Debug.Assert(visWire != null);
|
||||
Debug.Assert(parms != null);
|
||||
CachedImage = GenerateWireframeImage(visWire, parms, 64);
|
||||
CachedImage = GenerateWireframeImage(visWire, parms, THUMBNAIL_DIM);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -322,7 +324,6 @@ namespace SourceGen {
|
||||
// bounds, then draw lines with coordinates from 0.5 to 7.5.
|
||||
|
||||
GeometryGroup geo = new GeometryGroup();
|
||||
Debug.WriteLine("using max=" + dim);
|
||||
|
||||
// Draw invisible line segments to establish Path bounds.
|
||||
Point topLeft = new Point(0, 0);
|
||||
@ -330,12 +331,14 @@ namespace SourceGen {
|
||||
geo.Children.Add(new LineGeometry(topLeft, topLeft));
|
||||
geo.Children.Add(new LineGeometry(botRight, botRight));
|
||||
|
||||
// Generate a list of clip-space line segments. Coordinate values are [-1,1].
|
||||
// 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);
|
||||
|
||||
// Convert clip-space coords to screen. We need to scale up, round them to the
|
||||
// nearest whole pixel, and add +0.5 to make the thumbnails look crisp.
|
||||
// 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,
|
||||
// and add +0.5 to make thumbnail-size bitmaps look crisp.
|
||||
double scale = (dim - 0.5) / 2;
|
||||
double adj = 0.5;
|
||||
foreach (WireframeObject.LineSeg seg in segs) {
|
||||
|
@ -67,11 +67,17 @@ namespace SourceGen {
|
||||
}
|
||||
|
||||
private class Face {
|
||||
// Surface normal.
|
||||
public Vector3 Normal { get; private set; }
|
||||
// One vertex on the face, for BFC.
|
||||
public Vertex Vert { get; set; }
|
||||
// Flag set during BFC calculation.
|
||||
public bool IsVisible { get; set; }
|
||||
|
||||
public Face(double x, double y, double z) {
|
||||
Normal = new Vector3(x, y, z);
|
||||
Normal.Normalize();
|
||||
Normal.Normalize(); // not necessary, but easier to read in debug output
|
||||
IsVisible = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -154,7 +160,11 @@ namespace SourceGen {
|
||||
return null;
|
||||
}
|
||||
|
||||
wireObj.mVertices[vindex].Faces.Add(wireObj.mFaces[findex]);
|
||||
Face face = wireObj.mFaces[findex];
|
||||
wireObj.mVertices[vindex].Faces.Add(face);
|
||||
if (face.Vert == null) {
|
||||
face.Vert = wireObj.mVertices[vindex];
|
||||
}
|
||||
}
|
||||
|
||||
IntPair[] efaces = visWire.GetEdgeFaces();
|
||||
@ -168,7 +178,11 @@ namespace SourceGen {
|
||||
return null;
|
||||
}
|
||||
|
||||
wireObj.mEdges[eindex].Faces.Add(wireObj.mFaces[findex]);
|
||||
Face face = wireObj.mFaces[findex];
|
||||
wireObj.mEdges[eindex].Faces.Add(face);
|
||||
if (face.Vert == null) {
|
||||
face.Vert = wireObj.mEdges[eindex].Vertex0;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
@ -197,33 +211,89 @@ namespace SourceGen {
|
||||
/// was especially successful.</returns>
|
||||
public List<LineSeg> Generate(ReadOnlyDictionary<string, object> parms) {
|
||||
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);
|
||||
|
||||
// Perspective distance adjustment.
|
||||
// Camera Z coordinate adjustment, used to control how perspective projections
|
||||
// appear. The larger the value, the farther the object appears to be. Very
|
||||
// large values approximate an orthographic projection.
|
||||
const double zadj = 3.0;
|
||||
|
||||
// Scale values to [-1,1].
|
||||
bool doPersp = Util.GetFromObjDict(parms, VisWireframe.P_IS_PERSPECTIVE, false);
|
||||
// Scale coordinate values to [-1,1].
|
||||
double scale = 1.0 / mBigMag;
|
||||
if (doPersp) {
|
||||
scale = (scale * zadj) / (zadj + 1);
|
||||
// objects closer to camera are bigger; reduce scale slightly
|
||||
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);
|
||||
|
||||
if (doBfc) {
|
||||
// Mark faces as visible or not. This is determined with the surface normal,
|
||||
// rather than by checking whether a transformed triangle is clockwise.
|
||||
foreach (Face face in mFaces) {
|
||||
// Transform the surface normal.
|
||||
Vector3 rotNorm = rotMat.Multiply(face.Normal);
|
||||
if (doPersp) {
|
||||
// Transform one vertex to get a vector from the camera to the
|
||||
// surface. We want (V0 - C), where C is the camera; since we're
|
||||
// at the origin, we just need -C.
|
||||
if (face.Vert == null) {
|
||||
Debug.WriteLine("GLITCH: no vertex for face");
|
||||
face.IsVisible = true;
|
||||
continue;
|
||||
}
|
||||
Vector3 camVec = rotMat.Multiply(face.Vert.Vec);
|
||||
camVec.Multiply(-scale); // scale to [-1,1] and negate to get -C
|
||||
camVec.Z += zadj; // translate
|
||||
|
||||
// Now compute the dot product of the camera vector.
|
||||
double dot = Vector3.Dot(camVec, rotNorm);
|
||||
face.IsVisible = (dot >= 0);
|
||||
//Debug.WriteLine(string.Format(
|
||||
// "Face {0} vis={1,-5} dot={2,-8:N2}: camVec={3} rotNorm={4}",
|
||||
// index++, face.IsVisible, dot, camVec, rotNorm));
|
||||
} else {
|
||||
// For orthographic projection, the camera is essentially looking
|
||||
// down the Z axis at every X,Y, so we can trivially check the
|
||||
// value of Z in the transformed normal.
|
||||
face.IsVisible = (rotNorm.Z >= 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Edge edge in mEdges) {
|
||||
if (doBfc) {
|
||||
// To be visible, vertices and edges must either not specify any
|
||||
// faces, or must specify a visible face.
|
||||
if (!IsVertexVisible(edge.Vertex0) ||
|
||||
!IsVertexVisible(edge.Vertex1) ||
|
||||
!IsEdgeVisible(edge)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
Vector3 trv0 = rotMat.Multiply(edge.Vertex0.Vec);
|
||||
Vector3 trv1 = rotMat.Multiply(edge.Vertex1.Vec);
|
||||
double x0, y0, x1, y1;
|
||||
|
||||
if (doPersp) {
|
||||
// +Z is closer to the viewer, so we negate it here
|
||||
double z0 = -edge.Vertex0.Vec.Z * scale;
|
||||
double z1 = -edge.Vertex1.Vec.Z * scale;
|
||||
x0 = (edge.Vertex0.Vec.X * scale * zadj) / (zadj + z0);
|
||||
y0 = (edge.Vertex0.Vec.Y * scale * zadj) / (zadj + z0);
|
||||
x1 = (edge.Vertex1.Vec.X * scale * zadj) / (zadj + z1);
|
||||
y1 = (edge.Vertex1.Vec.Y * scale * zadj) / (zadj + z1);
|
||||
// +Z on the shape is closer to the viewer, so we negate it here
|
||||
double z0 = -trv0.Z * scale;
|
||||
double z1 = -trv1.Z * scale;
|
||||
x0 = (trv0.X * scale * zadj) / (zadj + z0);
|
||||
y0 = (trv0.Y * scale * zadj) / (zadj + z0);
|
||||
x1 = (trv1.X * scale * zadj) / (zadj + z1);
|
||||
y1 = (trv1.Y * scale * zadj) / (zadj + z1);
|
||||
} else {
|
||||
x0 = edge.Vertex0.Vec.X * scale;
|
||||
y0 = edge.Vertex0.Vec.Y * scale;
|
||||
x1 = edge.Vertex1.Vec.X * scale;
|
||||
y1 = edge.Vertex1.Vec.Y * scale;
|
||||
x0 = trv0.X * scale;
|
||||
y0 = trv0.Y * scale;
|
||||
x1 = trv1.X * scale;
|
||||
y1 = trv1.Y * scale;
|
||||
}
|
||||
|
||||
segs.Add(new LineSeg(x0, y0, x1, y1));
|
||||
@ -231,5 +301,29 @@ namespace SourceGen {
|
||||
|
||||
return segs;
|
||||
}
|
||||
|
||||
private bool IsVertexVisible(Vertex vert) {
|
||||
if (vert.Faces.Count == 0) {
|
||||
return true;
|
||||
}
|
||||
foreach (Face face in vert.Faces) {
|
||||
if (face.IsVisible) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool IsEdgeVisible(Edge edg) {
|
||||
if (edg.Faces.Count == 0) {
|
||||
return true;
|
||||
}
|
||||
foreach (Face face in edg.Faces) {
|
||||
if (face.IsVisible) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -43,6 +43,8 @@ namespace SourceGen.WpfGui {
|
||||
private SortedList<int, VisualizationSet> mEditedList;
|
||||
private Visualization mOrigVis;
|
||||
|
||||
public BitmapSource mThumbnail;
|
||||
|
||||
/// <summary>
|
||||
/// Visualization generation identifier for the last visualizer we used, for the benefit
|
||||
/// of "new".
|
||||
@ -320,7 +322,7 @@ namespace SourceGen.WpfGui {
|
||||
string trimTag = Visualization.TrimAndValidateTag(TagString, out bool isTagValid);
|
||||
Debug.Assert(isTagValid);
|
||||
NewVis = new Visualization(trimTag, item.VisDescriptor.Ident, valueDict, mOrigVis);
|
||||
NewVis.CachedImage = (BitmapSource)previewImage.Source;
|
||||
NewVis.CachedImage = mThumbnail;
|
||||
|
||||
sLastVisIdent = NewVis.VisGenIdent;
|
||||
|
||||
@ -446,6 +448,8 @@ namespace SourceGen.WpfGui {
|
||||
BitmapDimensions = "?";
|
||||
if (!IsValid || item == null) {
|
||||
previewImage.Source = sBadParamsImage;
|
||||
previewGrid.Background = null;
|
||||
wireframePath.Data = new GeometryGroup();
|
||||
} else {
|
||||
// Invoke the plugin.
|
||||
PluginErrMessage = string.Empty;
|
||||
@ -491,12 +495,17 @@ namespace SourceGen.WpfGui {
|
||||
wireframePath.Data = new GeometryGroup();
|
||||
BitmapDimensions = string.Format("{0}x{1}",
|
||||
previewImage.Source.Width, previewImage.Source.Height);
|
||||
|
||||
mThumbnail = (BitmapSource)previewImage.Source;
|
||||
} else {
|
||||
previewGrid.Background = Brushes.Black;
|
||||
previewImage.Source = Visualization.BLANK_IMAGE;
|
||||
wireframePath.Data = Visualization.GenerateWireframePath(visWire, parms,
|
||||
previewImage.ActualWidth / 2);
|
||||
BitmapDimensions = "n/a";
|
||||
|
||||
mThumbnail = Visualization.GenerateWireframeImage(visWire, parms,
|
||||
Visualization.THUMBNAIL_DIM);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user