diff --git a/CommonUtil/Vector3.cs b/CommonUtil/Vector3.cs
new file mode 100644
index 0000000..92364ce
--- /dev/null
+++ b/CommonUtil/Vector3.cs
@@ -0,0 +1,55 @@
+/*
+ * 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;
+
+namespace CommonUtil {
+ ///
+ /// Simple 3-element column vector.
+ ///
+ public class Vector3 {
+ public double X {
+ get { return mX; }
+ set { mX = value; }
+ }
+ public double Y {
+ get { return mY; }
+ set { mY = value; }
+ }
+ public double Z {
+ get { return mZ; }
+ set { mZ = value; }
+ }
+
+ private double mX, mY, mZ;
+
+ public Vector3(double x, double y, double z) {
+ mX = x;
+ mY = y;
+ mZ = z;
+ }
+
+ public double Magnitude() {
+ return Math.Sqrt(X * X + Y * Y + Z * Z);
+ }
+
+ public void Normalize() {
+ double len_r = 1.0 / Magnitude();
+ mX *= len_r;
+ mY *= len_r;
+ mZ *= len_r;
+ }
+ }
+}
diff --git a/PluginCommon/Interfaces.cs b/PluginCommon/Interfaces.cs
index 9f8b6ac..b8e9c80 100644
--- a/PluginCommon/Interfaces.cs
+++ b/PluginCommon/Interfaces.cs
@@ -342,7 +342,7 @@ namespace PluginCommon {
/// TODO(maybe): specify colors for edges. Not widely used?
///
public interface IVisualizationWireframe {
- // Each function returns the specified data. Do not modify.
+ // Each function returns the specified data. Do not modify the returned arrays.
float[] GetVerticesX();
float[] GetVerticesY();
diff --git a/PluginCommon/VisWireframe.cs b/PluginCommon/VisWireframe.cs
index d0fde4c..6609f10 100644
--- a/PluginCommon/VisWireframe.cs
+++ b/PluginCommon/VisWireframe.cs
@@ -25,20 +25,26 @@ 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_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, "_isPerspective", typeof(bool), 0, 0, 0, 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, "_isBackfaceRemoved", typeof(bool), 0, 0, 0, defaultVal);
+ return new VisParamDescr(uiLabel, P_IS_BACKFACE_REMOVED, typeof(bool), 0, 0, 0, defaultVal);
}
public static VisParamDescr Param_EulerX(string uiLabel, int defaultVal) {
- return new VisParamDescr(uiLabel, "_eulerRotX", typeof(int), 0, 359, 0, 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, "_eulerRotY", typeof(int), 0, 359, 0, 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, "_eulerRotZ", typeof(int), 0, 359, 0, defaultVal);
+ return new VisParamDescr(uiLabel, P_EULER_ROT_Z, typeof(int), 0, 359, 0, defaultVal);
}
private List mVerticesX = new List();
@@ -136,6 +142,12 @@ namespace PluginCommon {
int faceCount = mNormalsX.Count;
int edgeCount = mEdges.Count;
+ // complain about empty objects (should we fail if no edges were defined?)
+ if (vertexCount == 0) {
+ msg = "no vertices defined";
+ return false;
+ }
+
// check edges
foreach (IntPair ip in mEdges) {
if (ip.Val0 < 0 || ip.Val0 >= vertexCount ||
diff --git a/SourceGen/SGTestData/Visualization/VisWireframeTest.cs b/SourceGen/SGTestData/Visualization/VisWireframeTest.cs
index 6baba6d..37c670f 100644
--- a/SourceGen/SGTestData/Visualization/VisWireframeTest.cs
+++ b/SourceGen/SGTestData/Visualization/VisWireframeTest.cs
@@ -124,7 +124,9 @@ namespace WireframeTest {
int edge = vw.AddEdge(v0, v1);
vw.AddEdgeFace(edge, f0);
- vw.AddEdgeFace(edge, f1);
+ if (f1 != f0) {
+ vw.AddEdgeFace(edge, f1);
+ }
}
while (true) {
diff --git a/SourceGen/SourceGen.csproj b/SourceGen/SourceGen.csproj
index 10cd9fd..a718bf8 100644
--- a/SourceGen/SourceGen.csproj
+++ b/SourceGen/SourceGen.csproj
@@ -105,6 +105,7 @@
+
AboutBox.xaml
diff --git a/SourceGen/Visualization.cs b/SourceGen/Visualization.cs
index 1935c15..baab770 100644
--- a/SourceGen/Visualization.cs
+++ b/SourceGen/Visualization.cs
@@ -172,13 +172,16 @@ namespace SourceGen {
}
}
+ ///
+ /// Updates the cached thumbnail image.
+ ///
+ /// Visualization object.
+ /// Visualization parameters.
public void SetThumbnail(IVisualizationWireframe visWire,
ReadOnlyDictionary parms) {
- if (visWire == null) {
- CachedImage = BROKEN_IMAGE;
- } else {
- CachedImage = GenerateWireframeImage(visWire, parms, 64);
- }
+ Debug.Assert(visWire != null);
+ Debug.Assert(parms != null);
+ CachedImage = GenerateWireframeImage(visWire, parms, 64);
}
///
@@ -250,8 +253,8 @@ namespace SourceGen {
// Create bitmap.
RenderTargetBitmap bitmap = new RenderTargetBitmap(
- (int)bounds.Width,
- (int)bounds.Height,
+ (int)dim,
+ (int)dim,
96,
96,
PixelFormats.Pbgra32);
@@ -297,7 +300,8 @@ namespace SourceGen {
// 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.
+ // 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
@@ -309,46 +313,39 @@ namespace SourceGen {
// (9.5,1.5) is drawn as a single-wide full-brightness line. This is because of
// the anti-aliasing.
//
- // The path has a bounding box that starts at (0,0) in the top left, and extends
- // out as far as needed. If we want a path-drawn shape 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.
+ // 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
+ // 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.
//
// 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.
GeometryGroup geo = new GeometryGroup();
- // This establishes the geometry bounds. It's a zero-length line segment, so
- // nothing is actually drawn.
Debug.WriteLine("using max=" + dim);
- // TODO: currently ignoring dim
- Point corner = new Point(8, 8);
- geo.Children.Add(new LineGeometry(corner, corner));
- corner = new Point(0, 0);
- geo.Children.Add(new LineGeometry(corner, corner));
- // TODO(xyzzy): render
- //geo.Children.Add(new LineGeometry(new Point(0.0, 0.0), new Point(1.0, 0.0)));
- //geo.Children.Add(new LineGeometry(new Point(0.5, 0.5), new Point(1.5, 0.5)));
- //geo.Children.Add(new LineGeometry(new Point(0.75, 0.75), new Point(1.75, 0.75)));
- geo.Children.Add(new LineGeometry(new Point(0.0, 0.0), new Point(5.0, 7.0)));
+ // Draw invisible line segments to establish Path bounds.
+ Point topLeft = new Point(0, 0);
+ Point botRight = new Point(dim, dim);
+ geo.Children.Add(new LineGeometry(topLeft, topLeft));
+ geo.Children.Add(new LineGeometry(botRight, botRight));
- geo.Children.Add(new LineGeometry(new Point(0.5, 2), new Point(0.5, 3)));
- geo.Children.Add(new LineGeometry(new Point(1.5, 3), new Point(1.5, 4)));
- geo.Children.Add(new LineGeometry(new Point(2.5, 2), new Point(2.5, 3)));
- geo.Children.Add(new LineGeometry(new Point(3.5, 3), new Point(3.5, 4)));
- geo.Children.Add(new LineGeometry(new Point(4.5, 2), new Point(4.5, 3)));
- geo.Children.Add(new LineGeometry(new Point(5.5, 3), new Point(5.5, 4)));
- geo.Children.Add(new LineGeometry(new Point(6.5, 2), new Point(6.5, 3)));
- geo.Children.Add(new LineGeometry(new Point(7.5, 3), new Point(7.5, 4)));
+ // Generate a list of clip-space line segments. Coordinate values are [-1,1].
+ WireframeObject wireObj = WireframeObject.Create(visWire);
+ List segs = wireObj.Generate(parms);
- //geo.Children.Add(new LineGeometry(new Point(4, 5), new Point(3, 5)));
- //geo.Children.Add(new LineGeometry(new Point(2, 5), new Point(1, 5)));
+ // 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.
+ double scale = (dim - 0.5) / 2;
+ double adj = 0.5;
+ foreach (WireframeObject.LineSeg seg in segs) {
+ Point start = new Point(Math.Round((seg.X0 + 1) * scale) + adj,
+ Math.Round((1 - seg.Y0) * scale) + adj);
+ Point end = new Point(Math.Round((seg.X1 + 1) * scale) + adj,
+ Math.Round((1 - seg.Y1) * scale) + adj);
+ geo.Children.Add(new LineGeometry(start, end));
+ }
- //geo.Children.Add(new LineGeometry(new Point(4, 7), new Point(1, 7)));
- //geo.Children.Add(new LineGeometry(new Point(5, 7), new Point(9, 7)));
- //geo.Children.Add(new LineGeometry(new Point(0, 8.5), new Point(9, 8.5)));
return geo;
}
diff --git a/SourceGen/WireframeObject.cs b/SourceGen/WireframeObject.cs
new file mode 100644
index 0000000..bacf49a
--- /dev/null
+++ b/SourceGen/WireframeObject.cs
@@ -0,0 +1,235 @@
+/*
+ * 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 CommonUtil;
+using PluginCommon;
+
+namespace SourceGen {
+ ///
+ /// Renders a wireframe visualization, generating a collection of line segments in clip space.
+ ///
+ public class WireframeObject {
+ ///
+ /// Line segment.
+ ///
+ public class LineSeg {
+ public double X0 { get; private set; }
+ public double Y0 { get; private set; }
+ public double X1 { get; private set; }
+ public double Y1 { get; private set; }
+
+ public LineSeg(double x0, double y0, double x1, double y1) {
+ X0 = x0;
+ Y0 = y0;
+ X1 = x1;
+ Y1 = y1;
+ }
+ }
+
+ private class Vertex {
+ public List Faces { get; private set; }
+
+ public Vector3 Vec { get; private set; }
+
+ public Vertex(double x, double y, double z) {
+ Vec = new Vector3(x, y, z);
+ Faces = new List();
+ }
+ }
+
+ private class Edge {
+ public Vertex Vertex0 { get; private set; }
+ public Vertex Vertex1 { get; private set; }
+ public List Faces { get; private set; }
+
+ public Edge(Vertex v0, Vertex v1) {
+ Vertex0 = v0;
+ Vertex1 = v1;
+ Faces = new List();
+ }
+ }
+
+ private class Face {
+ public Vector3 Normal { get; private set; }
+
+ public Face(double x, double y, double z) {
+ Normal = new Vector3(x, y, z);
+ Normal.Normalize();
+ }
+ }
+
+ private List mVertices = new List();
+ private List mEdges = new List();
+ private List mFaces = new List();
+ private double mBigMag = -1.0;
+
+
+ // private constructor; use Create()
+ private WireframeObject() { }
+
+ ///
+ /// Creates a new object from a wireframe visualization.
+ ///
+ /// Visualization object.
+ /// New object.
+ public static WireframeObject Create(IVisualizationWireframe visWire) {
+ WireframeObject wireObj = new WireframeObject();
+
+ //
+ // Start by extracting data from the visualization object. Everything stored
+ // there is loaded into this object.
+ //
+
+ float[] normalsX = visWire.GetNormalsX();
+ if (normalsX.Length > 0) {
+ float[] normalsY = visWire.GetNormalsY();
+ float[] normalsZ = visWire.GetNormalsZ();
+
+ if (normalsX.Length != normalsY.Length || normalsX.Length != normalsZ.Length) {
+ Debug.Assert(false);
+ return null;
+ }
+
+ for (int i = 0; i < normalsX.Length; i++) {
+ wireObj.mFaces.Add(new Face(normalsX[i], normalsY[i], normalsZ[i]));
+ }
+ }
+
+ float[] verticesX = visWire.GetVerticesX();
+ float[] verticesY = visWire.GetVerticesY();
+ float[] verticesZ = visWire.GetVerticesZ();
+ if (verticesX.Length == 0) {
+ Debug.Assert(false);
+ return null;
+ }
+ if (verticesX.Length != verticesY.Length || verticesX.Length != verticesZ.Length) {
+ Debug.Assert(false);
+ return null;
+ }
+
+ for (int i = 0; i < verticesX.Length; i++) {
+ wireObj.mVertices.Add(new Vertex(verticesX[i], verticesY[i], verticesZ[i]));
+ }
+
+ IntPair[] edges = visWire.GetEdges();
+ for (int i = 0; i < edges.Length; i++) {
+ int v0index = edges[i].Val0;
+ int v1index = edges[i].Val1;
+
+ if (v0index < 0 || v0index >= wireObj.mVertices.Count ||
+ v1index < 0 || v1index >= wireObj.mVertices.Count) {
+ Debug.Assert(false);
+ return null;
+ }
+
+ wireObj.mEdges.Add(
+ new Edge(wireObj.mVertices[v0index], wireObj.mVertices[v1index]));
+ }
+
+ IntPair[] vfaces = visWire.GetVertexFaces();
+ for (int i = 0; i < vfaces.Length; i++) {
+ int vindex = vfaces[i].Val0;
+ int findex = vfaces[i].Val1;
+
+ if (vindex < 0 || vindex >= wireObj.mVertices.Count ||
+ findex < 0 || findex >= wireObj.mFaces.Count) {
+ Debug.Assert(false);
+ return null;
+ }
+
+ wireObj.mVertices[vindex].Faces.Add(wireObj.mFaces[findex]);
+ }
+
+ IntPair[] efaces = visWire.GetEdgeFaces();
+ for (int i = 0; i < efaces.Length; i++) {
+ int eindex = efaces[i].Val0;
+ int findex = efaces[i].Val1;
+
+ if (eindex < 0 || eindex >= wireObj.mEdges.Count ||
+ findex < 0 || findex >= wireObj.mFaces.Count) {
+ Debug.Assert(false);
+ return null;
+ }
+
+ wireObj.mEdges[eindex].Faces.Add(wireObj.mFaces[findex]);
+ }
+
+ //
+ // All data has been loaded into friendly classes.
+ //
+
+ // Compute the magnitude of the largest vertex, for scaling.
+ double bigMag = -1.0;
+ for (int i = 0; i < wireObj.mVertices.Count; i++) {
+ double mag = wireObj.mVertices[i].Vec.Magnitude();
+ if (bigMag < mag) {
+ bigMag = mag;
+ }
+ }
+ wireObj.mBigMag = bigMag;
+
+ return wireObj;
+ }
+
+ ///
+ /// Generates a list of line segments for the wireframe data and the specified
+ /// parameters.
+ ///
+ /// Visualization parameters.
+ /// List of line segments, which could be empty if backface removal
+ /// was especially successful.
+ public List Generate(ReadOnlyDictionary parms) {
+ List segs = new List(mEdges.Count);
+
+ // Perspective distance adjustment.
+ const double zadj = 3.0;
+
+ // Scale values to [-1,1].
+ bool doPersp = Util.GetFromObjDict(parms, VisWireframe.P_IS_PERSPECTIVE, false);
+ double scale = 1.0 / mBigMag;
+ if (doPersp) {
+ scale = (scale * zadj) / (zadj + 1);
+ }
+
+ foreach (Edge edge in mEdges) {
+ 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);
+ } 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;
+ }
+
+ segs.Add(new LineSeg(x0, y0, x1, y1));
+ }
+
+ return segs;
+ }
+ }
+}
diff --git a/SourceGen/WpfGui/EditVisualizationSet.xaml b/SourceGen/WpfGui/EditVisualizationSet.xaml
index bc2322e..8c8b661 100644
--- a/SourceGen/WpfGui/EditVisualizationSet.xaml
+++ b/SourceGen/WpfGui/EditVisualizationSet.xaml
@@ -23,7 +23,7 @@ limitations under the License.
xmlns:local="clr-namespace:SourceGen.WpfGui"
mc:Ignorable="d"
Title="Edit Visualization Set"
- Width="600" Height="400" ResizeMode="NoResize"
+ Width="640" Height="480" ResizeMode="NoResize"
ShowInTaskbar="False" WindowStartupLocation="CenterOwner"
Closing="Window_Closing">
@@ -71,21 +71,21 @@ limitations under the License.
-
+
-
-
+
-
-
+
+