Code:
/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / wpf / src / Core / CSharp / MS / Internal / Media3D / GeneralTransform2DTo3DTo2D.cs / 1 / GeneralTransform2DTo3DTo2D.cs
using MS.Internal; using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.Design.Serialization; using System.Diagnostics; using System.Globalization; using System.Reflection; using System.Runtime.InteropServices; using System.Security.Permissions; using System.Windows; using System.Windows.Media.Animation; using System.Windows.Media.Composition; using System.Windows.Markup; using System.Windows.Media; using System.Windows.Media.Media3D; using MS.Internal.PresentationCore; using MS.Internal.Media3D; using SR = MS.Internal.PresentationCore.SR; using SRID = MS.Internal.PresentationCore.SRID; namespace MS.Internal.Media3D { ////// Helper class that encapsulates return data needed for the /// hit test capture methods. /// internal class HitTestEdge { ////// Constructs a new hit test edge /// /// First edge point /// Second edge point /// Texture coordinate of first edge point /// Texture coordinate of second edge point public HitTestEdge(Point3D p1, Point3D p2, Point uv1, Point uv2) { _p1 = p1; _p2 = p2; _uv1 = uv1; _uv2 = uv2; } ////// Projects the stored 3D points in to 2D. /// /// The transformation matrix to use public void Project(GeneralTransform3DTo2D objectToViewportTransform) { Point projPoint1 = objectToViewportTransform.Transform(_p1); Point projPoint2 = objectToViewportTransform.Transform(_p2); _p1Transformed = new Point(projPoint1.X, projPoint1.Y); _p2Transformed = new Point(projPoint2.X, projPoint2.Y); } internal Point3D _p1, _p2; internal Point _uv1, _uv2; // the transformed Point3D value internal Point _p1Transformed, _p2Transformed; } ////// This transform allows one to go from 2D through 3D and back in to 2D /// internal class GeneralTransform2DTo3DTo2D : GeneralTransform { ////// Constructor /// /// The Visual3D that contains the 2D visual /// The visual on the Visual3D internal GeneralTransform2DTo3DTo2D(Viewport2DVisual3D visual3D, Visual fromVisual) { IsInverse = false; // get a copy of the geometry information - we store our own model to reuse hit // test code on the GeometryModel3D _geometry = new MeshGeometry3D(); _geometry.Positions = visual3D.InternalPositionsCache; _geometry.TextureCoordinates = visual3D.InternalTextureCoordinatesCache; _geometry.TriangleIndices = visual3D.InternalTriangleIndicesCache; _geometry.Freeze(); // get a copy of the size of the visual brush and the rect on the // visual that the transform is going to/from Visual visual3Dchild = visual3D.Visual; _visualBrushBounds = visual3Dchild.CalculateSubgraphRenderBoundsOuterSpace(); _visualBounds = fromVisual.CalculateSubgraphRenderBoundsInnerSpace(); // get the transform that will let us go from the fromVisual to its last 2D // parent before it reaches the 3D part of the graph (i.e. visual3D.Child) GeneralTransformGroup transformGroup = new GeneralTransformGroup(); transformGroup.Children.Add(fromVisual.TransformToAncestor(visual3Dchild)); transformGroup.Children.Add(visual3Dchild.TransformToOuterSpace()); transformGroup.Freeze(); _transform2D = transformGroup; // store the inverse as well _transform2DInverse = (GeneralTransform)_transform2D.Inverse; if (_transform2DInverse != null) { _transform2DInverse.Freeze(); } // make a copy of the camera and other values on the Viewport3D Viewport3DVisual viewport3D = (Viewport3DVisual)VisualTreeHelper.GetContainingVisual2D(visual3D); _camera = viewport3D.Camera; if (_camera != null) { _camera = (Camera)viewport3D.Camera.GetAsFrozen(); } _viewSize = viewport3D.Viewport.Size; _boundingRect = viewport3D.ComputeSubgraphBounds3D(); _objectToViewport = visual3D.TransformToAncestor(viewport3D); // if the transform was not possible, it could be null - check before freezing if (_objectToViewport != null) { _objectToViewport.Freeze(); } // store the needed transformations for the various operations _worldTransformation = M3DUtil.GetWorldTransformationMatrix(visual3D); _validEdgesCache = null; } internal GeneralTransform2DTo3DTo2D() { } ////// Transforms a point /// /// input point /// output point ///false if the point cannot be transformed public override bool TryTransform(Point inPoint, out Point result) { if (IsInverse) { return TryInverseTransform(inPoint, out result); } else { return TryRegularTransform(inPoint, out result); } } ////// Performs the transform that goes from the parent Viewport3DVisual down in to the 2D on 3D /// contained within the 3D scene /// /// input point /// output point ///false if the point cannot be transformed private bool TryInverseTransform(Point inPoint, out Point result) { // set up the hit test parameters double distanceAdjust; bool foundIntersection = false; if (_camera != null) { RayHitTestParameters rayHitTestParameters = _camera.RayFromViewportPoint(inPoint, _viewSize, _boundingRect, out distanceAdjust); rayHitTestParameters.PushVisualTransform(new MatrixTransform3D(_worldTransformation)); // perfrom the hit test // no back material so we only need to concern ourselves with the front faces Point pointHit = new Point(); _geometry.RayHitTest(rayHitTestParameters, FaceType.Front); rayHitTestParameters.RaiseCallback(delegate (HitTestResult rawresult) { RayHitTestResult rayResult = rawresult as RayHitTestResult; if (rayResult != null) { foundIntersection = Viewport2DVisual3D.GetIntersectionInfo(rayResult, out pointHit); } return HitTestResultBehavior.Stop; }, null, HitTestResultBehavior.Continue, distanceAdjust); // perform capture positioning if we didn't hit anything and something has capture if (!foundIntersection) { foundIntersection = HandleOffMesh(inPoint, out pointHit); } // compute final point result = Viewport2DVisual3D.TextureCoordsToVisualCoords(pointHit, _visualBrushBounds); } else { result = new Point(); } return foundIntersection; } ////// Function to deal with mouse capture when off the mesh. /// /// The location of the mouse /// output point private bool HandleOffMesh(Point mousePos, out Point outPoint) { Point[] visCorners = new Point[4]; if (_validEdgesCache == null) { // get the points relative to the parent visCorners[0] = _transform2D.Transform(new Point(_visualBounds.Left, _visualBounds.Top)); visCorners[1] = _transform2D.Transform(new Point(_visualBounds.Right, _visualBounds.Top)); visCorners[2] = _transform2D.Transform(new Point(_visualBounds.Right, _visualBounds.Bottom)); visCorners[3] = _transform2D.Transform(new Point(_visualBounds.Left, _visualBounds.Bottom)); // get the u,v texture coordinate values of the above points Point[] texCoordsOfInterest = new Point[4]; for (int i = 0; i < visCorners.Length; i++) { texCoordsOfInterest[i] = Viewport2DVisual3D.VisualCoordsToTextureCoords(visCorners[i], _visualBrushBounds); } // get the edges that map to the given visual _validEdgesCache = GrabValidEdges(texCoordsOfInterest); } // find the closest intersection of the mouse position and the edge list return FindClosestIntersection(mousePos, _validEdgesCache, out outPoint); } ////// Function takes the passed in list of texture coordinate points, and then finds the /// visible outline of the rectangle specified by those points and returns it. /// /// The points specifying the rectangle to search for ///The edges of that rectangle private ListGrabValidEdges(Point[] visualTexCoordBounds) { // our final edge list List hitTestEdgeList = new List (); Dictionary adjInformation = new Dictionary (); // store some important info in local variables for easier access Point3DCollection positions = _geometry.Positions; PointCollection textureCoords = _geometry.TextureCoordinates; Int32Collection triIndices = _geometry.TriangleIndices; // if positions and texture coordinates are null, we can't really find what we need so return immediately if (positions == null || textureCoords == null) { return new List (); } // this call actually gets the object to camera transform, but we will invert it later, and because of that // the local variable is named cameraToObjecTransform. Matrix3D cameraToObjectTransform = _worldTransformation * _camera.GetViewMatrix(); try { cameraToObjectTransform.Invert(); } catch (InvalidOperationException) { return new List (); } Point3D camPosObjSpace = cameraToObjectTransform.Transform(new Point3D(0, 0, 0)); // get the bounding box around the passed in texture coordinates to help // with early rejection tests Rect bbox = Rect.Empty; for (int i = 0; i < visualTexCoordBounds.Length; i++) { bbox.Union(visualTexCoordBounds[i]); } // walk through the triangles - and look for the triangles we care about Point3D[] triangleVertices = new Point3D[3]; Point[] triangleTexCoords = new Point[3]; // switch depending on if the mesh is indexed or not if (triIndices == null || triIndices.Count == 0) { int texCoordCount = textureCoords.Count; // in this case we have a non-indexed mesh int count = positions.Count; count = count - (count % 3); for (int i = 0; i < count; i+=3) { // get the triangle indices Rect triBBox = Rect.Empty; for (int j = 0; j < 3; j++) { triangleVertices[j] = positions[i + j]; if (i + j < texCoordCount) { triangleTexCoords[j] = textureCoords[i + j]; } else { // In the case you have less texture coordinates than positions, MIL will set // missing ones to be 0,0. We do the same to stay consistent. // See CMILMesh3D::CopyTextureCoordinatesFromDoubles triangleTexCoords[j] = new Point(0,0); } triBBox.Union(triangleTexCoords[j]); } if (bbox.IntersectsWith(triBBox)) { ProcessTriangle(triangleVertices, triangleTexCoords, visualTexCoordBounds, hitTestEdgeList, adjInformation, camPosObjSpace); } } } else { // in this case we have an indexed mesh int count = triIndices.Count; int posLimit = positions.Count; int texCoordLimit = textureCoords.Count; int[] indices = new int[3]; for (int i = 2; i < count; i += 3) { // get the triangle indices Rect triBBox = Rect.Empty; bool validTextureCoordinates = true; bool validPositions = true; for (int j = 0; j < 3; j++) { // subtract 2 to take in to account we start i // at the high range of indices indices[j] = triIndices[(i-2) + j]; // if a point or texture coordinate is out of range, end early since this is an error if (indices[j] < 0 || indices[j] >= posLimit) { validPositions = false; break; } if (indices[j] < 0 || indices[j] >= texCoordLimit) { validTextureCoordinates = false; break; } triangleVertices[j] = positions[indices[j]]; triangleTexCoords[j] = textureCoords[indices[j]]; triBBox.Union(triangleTexCoords[j]); } // if the positions were ever invalid, we stop processing - see MeshGeometry3D RayHitTestIndexedList // for reasoning if (!validPositions) { break; } if (validTextureCoordinates && bbox.IntersectsWith(triBBox)) { ProcessTriangle(triangleVertices, triangleTexCoords, visualTexCoordBounds, hitTestEdgeList, adjInformation, camPosObjSpace); } } } // also handle the case of an edge that doesn't also have a backface - i.e a single plane foreach (Edge edge in adjInformation.Keys) { EdgeInfo ei = adjInformation[edge]; if (ei._hasFrontFace && ei._numSharing == 1) { HandleSilhouetteEdge(ei._uv1, ei._uv2, edge._start, edge._end, visualTexCoordBounds, hitTestEdgeList); } } // project all the edges to get at the 2D point of interest if (_objectToViewport != null) { for (int i = 0; i < hitTestEdgeList.Count; i++) { hitTestEdgeList[i].Project(_objectToViewport); } } else { hitTestEdgeList = new List (); } return hitTestEdgeList; } /// /// Processes the passed in triangle by checking to see if it is facing the camera and if /// so searches to see if the texture coordinate edges intersect it. It also looks /// to see if there are any silhouette edges and processes these as well. /// /// The triangle's vertices /// The texture coordinates for those vertices /// The texture coordinate edges to intersect with /// The edge list that results should be placed on /// The adjacency information for the mesh /// private void ProcessTriangle(Point3D[] p, Point[] uv, Point[] visualTexCoordBounds, ListedgeList, Dictionary adjInformation, Point3D camPosObjSpace) { // calculate the normal of the mesh and the vector from a point on the mesh to the camera // for back face removal calculations. Vector3D normal = Vector3D.CrossProduct(p[1] - p[0], p[2] - p[0]); Vector3D dirToCamera = camPosObjSpace - p[0]; // ignore any triangles that have a normal of (0,0,0) if (!(normal.X == 0 && normal.Y == 0 && normal.Z == 0)) { double dotProd = Vector3D.DotProduct(normal, dirToCamera); // if the dot product is > 0 then the triangle is visible, otherwise invisible if (dotProd > 0.0) { // loop over the triangle and update any edge information ProcessTriangleEdges(p, uv, visualTexCoordBounds, PolygonSide.FRONT, edgeList, adjInformation); // intersect the bounds of the visual with the triangle ProcessVisualBoundsIntersections(p, uv, visualTexCoordBounds, edgeList); } else { ProcessTriangleEdges(p, uv, visualTexCoordBounds, PolygonSide.BACK, edgeList, adjInformation); } } } /// /// Function intersects the edges specified by tc with the texture coordinates /// on the passed in triangle. If there are any intersections, the edges /// of these intersections are added to the edgelist /// /// The vertices of the triangle /// The texture coordinates for that triangle /// The texture coordinate edges to be intersected against /// The list of edges any intersecte edges should be added to private void ProcessVisualBoundsIntersections(Point3D[] p, Point[] uv, Point[] visualTexCoordBounds, ListedgeList) { Debug.Assert(uv.Length == p.Length, "vertices and texture coordinate sizes should match"); List pointList = new List (); List uvList = new List (); // loop over the visual's texture coordinate bounds for (int i = 0; i < visualTexCoordBounds.Length; i++) { Point visEdgeStart = visualTexCoordBounds[i]; Point visEdgeEnd = visualTexCoordBounds[(i + 1) % visualTexCoordBounds.Length]; // clear out anything that used to be there pointList.Clear(); uvList.Clear(); // loop over triangle edges bool skipListProcessing = false; for (int j = 0; j < uv.Length; j++) { Point uv1 = uv[j]; Point uv2 = uv[(j + 1) % uv.Length]; Point3D p3D1 = p[j]; Point3D p3D2 = p[(j + 1) % p.Length]; // initial rejection processing if (!((Math.Max(visEdgeStart.X, visEdgeEnd.X) < Math.Min(uv1.X, uv2.X)) || (Math.Min(visEdgeStart.X, visEdgeEnd.X) > Math.Max(uv1.X, uv2.X)) || (Math.Max(visEdgeStart.Y, visEdgeEnd.Y) < Math.Min(uv1.Y, uv2.Y)) || (Math.Min(visEdgeStart.Y, visEdgeEnd.Y) > Math.Max(uv1.Y, uv2.Y)))) { // intersect the two lines bool areCoincident = false; Vector dir = uv2 - uv1; double t = IntersectRayLine(uv1, dir, visEdgeStart, visEdgeEnd, out areCoincident); // if they are coincident then we have two intersections and don't need to // do anymore processing if (areCoincident) { HandleCoincidentLines(visEdgeStart, visEdgeEnd, p3D1, p3D2, uv1, uv2, edgeList); skipListProcessing = true; break; } else if (t >= 0 && t <= 1) { Point intersUV = uv1 + dir * t; Point3D intersPoint3D = p3D1 + (p3D2 - p3D1) * t; double visEdgeDiff = (visEdgeStart - visEdgeEnd).Length; if ((intersUV - visEdgeStart).Length < visEdgeDiff && (intersUV - visEdgeEnd).Length < visEdgeDiff) { pointList.Add(intersPoint3D); uvList.Add(intersUV); } } } } if (!skipListProcessing) { if (pointList.Count >= 2) { edgeList.Add(new HitTestEdge(pointList[0], pointList[1], uvList[0], uvList[1])); } else if (pointList.Count == 1) { Point3D outputPoint; // To avoid an edge cases caused by generating a point extremely // close to one of the bound points, we test if both points are inside // the bounds to be on the safe side - in the worst case we do // extra work or generate a small edge if (M3DUtil.IsPointInTriangle(visEdgeStart, uv, p, out outputPoint)) { edgeList.Add(new HitTestEdge(pointList[0], outputPoint, uvList[0], visEdgeStart)); } if (M3DUtil.IsPointInTriangle(visEdgeEnd, uv, p, out outputPoint)) { edgeList.Add(new HitTestEdge(pointList[0], outputPoint, uvList[0], visEdgeEnd)); } } else { Point3D outputPoint1, outputPoint2; if (M3DUtil.IsPointInTriangle(visEdgeStart, uv, p, out outputPoint1) && M3DUtil.IsPointInTriangle(visEdgeEnd, uv, p, out outputPoint2)) { edgeList.Add(new HitTestEdge(outputPoint1, outputPoint2, visEdgeStart, visEdgeEnd)); } } } } } /// /// Handles adding an edge when the two line segments are coincident. /// /// The texture coordinates of the boundary edge /// The texture coordinates of the boundary edge /// The 3D coordinate of the triangle edge /// The 3D coordinates of the triangle edge /// The texture coordinates of the triangle edge /// The texture coordinates of the triangle edge /// The edge list to add to private void HandleCoincidentLines(Point visUV1, Point visUV2, Point3D tri3D1, Point3D tri3D2, Point triUV1, Point triUV2, ListedgeList) { Point minVisUV, maxVisUV; Point minTriUV, maxTriUV; Point3D minTri3D, maxTri3D; // to be used in final edge creation Point uv1, uv2; Point3D p1, p2; // order the points and give refs to them for ease of use if (Math.Abs(visUV1.X - visUV2.X) > Math.Abs(visUV1.Y - visUV2.Y)) { if (visUV1.X <= visUV2.X) { minVisUV = visUV1; maxVisUV = visUV2; } else { minVisUV = visUV2; maxVisUV = visUV1; } if (triUV1.X <= triUV2.X) { minTriUV = triUV1; minTri3D = tri3D1; maxTriUV = triUV2; maxTri3D = tri3D2; } else { minTriUV = triUV2; minTri3D = tri3D2; maxTriUV = triUV1; maxTri3D = tri3D1; } // now actually create the edge // compute the minimum value if (minVisUV.X < minTriUV.X) { uv1 = minTriUV; p1 = minTri3D; } else { uv1 = minVisUV; p1 = minTri3D + (minVisUV.X - minTriUV.X) / (maxTriUV.X - minTriUV.X) * (maxTri3D - minTri3D); } // compute the maximum value if (maxVisUV.X > maxTriUV.X) { uv2 = maxTriUV; p2 = maxTri3D; } else { uv2 = maxVisUV; p2 = minTri3D + (maxVisUV.X - minTriUV.X) / (maxTriUV.X - minTriUV.X) * (maxTri3D - minTri3D); } } else { if (visUV1.Y <= visUV2.Y) { minVisUV = visUV1; maxVisUV = visUV2; } else { minVisUV = visUV2; maxVisUV = visUV1; } if (triUV1.Y <= triUV2.Y) { minTriUV = triUV1; minTri3D = tri3D1; maxTriUV = triUV2; maxTri3D = tri3D2; } else { minTriUV = triUV2; minTri3D = tri3D2; maxTriUV = triUV1; maxTri3D = tri3D1; } // now actually create the edge // compute the minimum value if (minVisUV.Y < minTriUV.Y) { uv1 = minTriUV; p1 = minTri3D; } else { uv1 = minVisUV; p1 = minTri3D + (minVisUV.Y - minTriUV.Y) / (maxTriUV.Y - minTriUV.Y) * (maxTri3D - minTri3D); } // compute the maximum value if (maxVisUV.Y > maxTriUV.Y) { uv2 = maxTriUV; p2 = maxTri3D; } else { uv2 = maxVisUV; p2 = minTri3D + (maxVisUV.Y - minTriUV.Y) / (maxTriUV.Y - minTriUV.Y) * (maxTri3D - minTri3D); } } // add the edge edgeList.Add(new HitTestEdge(p1, p2, uv1, uv2)); } /// /// Intersects a ray with the line specified by the passed in end points. The parameterized coordinate along the ray of /// intersection is returned. /// /// The ray origin /// The ray direction /// First point of the line to intersect against /// Second point of the line to intersect against /// Whether the ray and line are coincident ////// The parameter along the ray of the point of intersection. /// If the ray and line are parallel and not coincident, this will be -1. /// private double IntersectRayLine(Point o, Vector d, Point p1, Point p2, out bool coinc) { coinc = false; // deltas double dy = p2.Y - p1.Y; double dx = p2.X - p1.X; // handle case of a vertical line if (dx == 0) { if (d.X == 0) { coinc = (o.X == p1.X); return -1; } else { return (p2.X - o.X) / d.X; } } // now need to do more general intersection double numer = (o.X - p1.X) * dy / dx - o.Y + p1.Y; double denom = (d.Y - d.X * dy / dx); // if denominator is zero, then the lines are parallel if (denom == 0) { double b0 = -o.X * dy / dx + o.Y; double b1 = -p1.X * dy / dx + p1.Y; coinc = (b0 == b1); return -1; } else { return (numer / denom); } } ////// Helper structure to represent an edge /// private struct Edge { public Edge(Point3D s, Point3D e) { _start = s; _end = e; } public Point3D _start; public Point3D _end; } ////// Information about an edge such as whether it belongs to a front/back facing /// triangle, the texture coordinates for the edge, and how many polygons refer /// to that edge. /// private class EdgeInfo { public EdgeInfo() { _hasFrontFace = _hasBackFace = false; _numSharing = 0; } public bool _hasFrontFace; public bool _hasBackFace; public Point _uv1; public Point _uv2; public int _numSharing; } ////// Processes the edges of the given triangle. It does so by updating /// the adjacency information based on the direction the polygon is facing. /// If there is a silhouette edge found, then this edge is added to the list /// of edges if it is within the texture coordinate bounds passed to the function. /// /// The triangle's vertices /// The texture coordinates for those vertices /// The texture coordinate edges being searched for /// Which side the polygon is facing (greateer than 0 front, less than 0 back) /// The list of edges comprosing the visual outline /// The adjacency information structure private void ProcessTriangleEdges(Point3D[] p, Point[] uv, Point[] visualTexCoordBounds, PolygonSide polygonSide, ListedgeList, Dictionary adjInformation) { // loop over all the edges and add them to the adjacency list for (int i = 0; i < p.Length; i++) { Point uv1, uv2; Point3D p3D1 = p[i]; Point3D p3D2 = p[(i + 1) % p.Length]; Edge edge; // order the edge points so insertion in to adjInformation is consistent if (p3D1.X < p3D2.X || (p3D1.X == p3D2.X && p3D1.Y < p3D2.Y) || (p3D1.X == p3D2.X && p3D1.Y == p3D2.Y && p3D1.Z < p3D1.Z)) { edge = new Edge(p3D1, p3D2); uv1 = uv[i]; uv2 = uv[(i + 1) % p.Length]; } else { edge = new Edge(p3D2, p3D1); uv2 = uv[i]; uv1 = uv[(i + 1) % p.Length]; } // look up the edge information EdgeInfo edgeInfo; if (adjInformation.ContainsKey(edge)) { edgeInfo = adjInformation[edge]; } else { edgeInfo = new EdgeInfo(); adjInformation[edge] = edgeInfo; } edgeInfo._numSharing++; // whether or not the edge has already been added to the edge list bool alreadyAdded = edgeInfo._hasBackFace && edgeInfo._hasFrontFace; // add the edge to the info list if (polygonSide == PolygonSide.FRONT) { edgeInfo._hasFrontFace = true; edgeInfo._uv1 = uv1; edgeInfo._uv2 = uv2; } else { edgeInfo._hasBackFace = true; } // if the sides are different we may need to add an edge if (!alreadyAdded && edgeInfo._hasBackFace && edgeInfo._hasFrontFace) { HandleSilhouetteEdge(edgeInfo._uv1, edgeInfo._uv2, edge._start, edge._end, visualTexCoordBounds, edgeList); } } } /// /// Handles intersecting a silhouette edge against the passed in texture coordinate /// bounds. It behaves similarly to the case of intersection the bounds with a triangle /// except the testing order is switched. /// /// The texture coordinates of the edge /// The texture coordinates of the edge /// The 3D point of the edge /// The 3D point of the edge /// The texture coordinate bounds /// The list of edges private void HandleSilhouetteEdge(Point uv1, Point uv2, Point3D p3D1, Point3D p3D2, Point[] bounds, ListedgeList) { List pointList = new List (); List uvList = new List (); Vector dir = uv2 - uv1; // loop over object bounds for (int i = 0; i < bounds.Length; i++) { Point visEdgeStart = bounds[i]; Point visEdgeEnd = bounds[(i + 1) % bounds.Length]; // initial rejection processing if (!((Math.Max(visEdgeStart.X, visEdgeEnd.X) < Math.Min(uv1.X, uv2.X)) || (Math.Min(visEdgeStart.X, visEdgeEnd.X) > Math.Max(uv1.X, uv2.X)) || (Math.Max(visEdgeStart.Y, visEdgeEnd.Y) < Math.Min(uv1.Y, uv2.Y)) || (Math.Min(visEdgeStart.Y, visEdgeEnd.Y) > Math.Max(uv1.Y, uv2.Y)))) { // intersect the two lines bool areCoincident = false; double t = IntersectRayLine(uv1, dir, visEdgeStart, visEdgeEnd, out areCoincident); // silhouette edge processing will only include non-coincident lines if (areCoincident) { // if it's coincident, we'll let the normal processing handle this edge return; } else if (t >= 0 && t <= 1) { Point intersUV = uv1 + dir * t; Point3D intersPoint3D = p3D1 + (p3D2 - p3D1) * t; double visEdgeDiff = (visEdgeStart - visEdgeEnd).Length; if ((intersUV - visEdgeStart).Length < visEdgeDiff && (intersUV - visEdgeEnd).Length < visEdgeDiff) { pointList.Add(intersPoint3D); uvList.Add(intersUV); } } } } if (pointList.Count >= 2) { edgeList.Add(new HitTestEdge(pointList[0], pointList[1], uvList[0], uvList[1])); } else if (pointList.Count == 1) { // for the case that uv1/2 is actually a point on or extremely close to the bounds // of the polygon, we do the pointinpolygon test on both to avoid any numerical // precision issues - in the worst case we end up with a very small edge and // the right edge if (IsPointInPolygon(bounds, uv1)) { edgeList.Add(new HitTestEdge(pointList[0], p3D1, uvList[0], uv1)); } if (IsPointInPolygon(bounds, uv2)) { edgeList.Add(new HitTestEdge(pointList[0], p3D2, uvList[0], uv2)); } } else { if (IsPointInPolygon(bounds, uv1) && IsPointInPolygon(bounds, uv2)) { edgeList.Add(new HitTestEdge(p3D1, p3D2, uv1, uv2)); } } } /// /// Function tests to see whether the point p is contained within the polygon /// specified by the list of points passed to the function. p is considered within /// this polygon if it is on the same side of all the edges. A point on any of /// the edges of the polygon is not considered within the polygon. /// /// The polygon to test against /// The point to be tested against ///Whether the point is in the polygon private bool IsPointInPolygon(Point[] polygon, Point p) { bool sign = false; for (int i = 0; i < polygon.Length; i++) { double crossProduct = Vector.CrossProduct(polygon[(i + 1) % polygon.Length] - polygon[i], polygon[i] - p); bool currSign = crossProduct > 0; if (i == 0) { sign = currSign; } else { if (sign != currSign) return false; } } return true; } ////// Finds the point in edges that is closest ot the mouse position. Updates closestIntersectionInfo /// with the results of this calculation /// /// The mouse position /// The edges to test against /// The final intersection point ///The closest intersection point private bool FindClosestIntersection(Point mousePos, Listedges, out Point finalPoint) { bool success = false; double closestDistance = Double.MaxValue; Point closestIntersection = new Point(); // the uv of the closest intersection finalPoint = new Point(); // Find the closest point to the mouse position for (int i=0, count = edges.Count; i < count; i++) { Vector v1 = mousePos - edges[i]._p1Transformed; Vector v2 = edges[i]._p2Transformed - edges[i]._p1Transformed; Point currClosest; double distance; // calculate the distance from the mouse position to this edge // The closest distance can be computed by projecting v1 on to v2. If the // projectiong occurs between _p1Transformed and _p2Transformed, then this is the // closest point. Otherwise, depending on which side it lies, it is either _p1Transformed // or _p2Transformed. // // The projection equation is given as: (v1 DOT v2) / (v2 DOT v2) * v2. // v2 DOT v2 will always be positive. Thus, if v1 DOT v2 is negative, we know the projection // will occur before _p1Transformed (and so it is the closest point). If (v1 DOT v2) is greater // than (v2 DOT v2), then we have gone passed _p2Transformed and so it is the closest point. // Otherwise the projection gives us this value. // double denom = v2 * v2; if (denom == 0) { currClosest = edges[i]._p1Transformed; distance = v1.Length; } else { double numer = v2 * v1; if (numer < 0) { currClosest = edges[i]._p1Transformed; } else { if (numer > denom) { currClosest = edges[i]._p2Transformed; } else { currClosest = edges[i]._p1Transformed + (numer / denom) * v2; } } distance = (mousePos - currClosest).Length; } // see if we found a new closest distance if (distance < closestDistance) { closestDistance = distance; if (denom != 0) { closestIntersection = ((currClosest - edges[i]._p1Transformed).Length / Math.Sqrt(denom) * (edges[i]._uv2 - edges[i]._uv1)) + edges[i]._uv1; } else { closestIntersection = edges[i]._uv1; } } } if (closestDistance != Double.MaxValue) { Point ptOnVisual = Viewport2DVisual3D.TextureCoordsToVisualCoords(closestIntersection, _visualBrushBounds); if (_transform2DInverse != null) { Point ptRelToCapture = _transform2DInverse.Transform(ptOnVisual); // we want to "ring" around the outside so things like buttons are not pressed when we move off the mesh // this code here does that - the +BUFFER_SIZE and -BUFFER_SIZE are to give a bit of a // buffer for any numerical issues if (ptRelToCapture.X <= _visualBounds.Left + 1) ptRelToCapture.X -= BUFFER_SIZE; if (ptRelToCapture.Y <= _visualBounds.Top + 1) ptRelToCapture.Y -= BUFFER_SIZE; if (ptRelToCapture.X >= _visualBounds.Right - 1) ptRelToCapture.X += BUFFER_SIZE; if (ptRelToCapture.Y >= _visualBounds.Bottom - 1) ptRelToCapture.Y += BUFFER_SIZE; Point finalVisualPoint = _transform2D.Transform(ptRelToCapture); finalPoint = Viewport2DVisual3D.VisualCoordsToTextureCoords(finalVisualPoint, _visualBrushBounds); success = true; } } return success; } /// /// Performs the transform that goes from 2D on 3D content contained within the 3D scene /// up to the containing Viewport3DVisual /// /// input point /// output point ///false if the point cannot be transformed private bool TryRegularTransform(Point inPoint, out Point result) { Point texCoord = Viewport2DVisual3D.VisualCoordsToTextureCoords(inPoint, _visualBrushBounds); // need to walk the texture coordinates and look for where this point intersects one of them Point3D point3D; if (_objectToViewport != null && Viewport2DVisual3D.Get3DPointFor2DCoordinate(texCoord, out point3D, _geometry.Positions, _geometry.TextureCoordinates, _geometry.TriangleIndices)) { // convert from this 3D point up to the containing Viewport3D return _objectToViewport.TryTransform(point3D, out result); } else { result = new Point(); return false; } } ////// Transform the rect bounds into the smallest axis alligned bounding box that /// contains all the point in the original bounds. /// /// ///public override Rect TransformBounds(Rect rect) { List edges = null; // intersect the rect given to us with the bounds of the visual brush to guarantee the rect we are // searching for is within the visual brush rect.Intersect(_visualBrushBounds); // get the texture coordinate values for the rect's corners Point[] texCoordsOfInterest = new Point[4]; texCoordsOfInterest[0] = Viewport2DVisual3D.VisualCoordsToTextureCoords(rect.TopLeft, _visualBrushBounds); texCoordsOfInterest[1] = Viewport2DVisual3D.VisualCoordsToTextureCoords(rect.TopRight, _visualBrushBounds); texCoordsOfInterest[2] = Viewport2DVisual3D.VisualCoordsToTextureCoords(rect.BottomRight, _visualBrushBounds); texCoordsOfInterest[3] = Viewport2DVisual3D.VisualCoordsToTextureCoords(rect.BottomLeft, _visualBrushBounds); // get the edges that map to the given rect edges = GrabValidEdges(texCoordsOfInterest); Rect result = Rect.Empty; if (edges != null) { for (int i = 0, count = edges.Count; i < count; i++) { result.Union(edges[i]._p1Transformed); result.Union(edges[i]._p2Transformed); } } return result; } /// /// Returns the inverse transform if there is one, null otherwise /// public override GeneralTransform Inverse { get { // internal class - no ReadPreamble needed // ReadPreamble(); GeneralTransform2DTo3DTo2D inverseTransform = (GeneralTransform2DTo3DTo2D)Clone(); inverseTransform.IsInverse = !IsInverse; return inverseTransform; } } ////// Returns a best effort affine transform /// internal override Transform AffineTransform { [FriendAccessAllowed] // Built into Core, also used by Framework. get { return null; } } ////// Returns true if the transform is an inverse /// internal bool IsInverse { get { return _fInverse; } set { _fInverse = value; } } ////// Implementation of ///Freezable.CreateInstanceCore . ///The new Freezable. protected override Freezable CreateInstanceCore() { return new GeneralTransform2DTo3DTo2D(); } ////// Implementation of /// protected override void CloneCore(Freezable sourceFreezable) { GeneralTransform2DTo3DTo2D transform = (GeneralTransform2DTo3DTo2D)sourceFreezable; base.CloneCore(sourceFreezable); CopyCommon(transform); } ///Freezable.CloneCore . ////// Implementation of /// protected override void CloneCurrentValueCore(Freezable sourceFreezable) { GeneralTransform2DTo3DTo2D transform = (GeneralTransform2DTo3DTo2D)sourceFreezable; base.CloneCurrentValueCore(sourceFreezable); CopyCommon(transform); } ///Freezable.CloneCurrentValueCore . ////// Implementation of /// protected override void GetAsFrozenCore(Freezable sourceFreezable) { GeneralTransform2DTo3DTo2D transform = (GeneralTransform2DTo3DTo2D)sourceFreezable; base.GetAsFrozenCore(sourceFreezable); CopyCommon(transform); } ///Freezable.GetAsFrozenCore . ////// Implementation of /// protected override void GetCurrentValueAsFrozenCore(Freezable sourceFreezable) { GeneralTransform2DTo3DTo2D transform = (GeneralTransform2DTo3DTo2D)sourceFreezable; base.GetCurrentValueAsFrozenCore(sourceFreezable); CopyCommon(transform); } ///Freezable.GetCurrentValueAsFrozenCore . ////// Clones values that do not have corresponding DPs /// /// private void CopyCommon(GeneralTransform2DTo3DTo2D transform) { _fInverse = transform._fInverse; _geometry = transform._geometry; _visualBounds = transform._visualBounds; _visualBrushBounds = transform._visualBrushBounds; _transform2D = transform._transform2D; _transform2DInverse = transform._transform2DInverse; _camera = transform._camera; _viewSize = transform._viewSize; _boundingRect = transform._boundingRect; _worldTransformation = transform._worldTransformation; _objectToViewport = transform._objectToViewport; _validEdgesCache = null; } private bool _fInverse; // the geometry of the 3D object private MeshGeometry3D _geometry; // the size of the visual brush and the visual on it we're interested in private Rect _visualBounds; private Rect _visualBrushBounds; // the transform to go in to and out of the coordinate space fo the visual we're // interested in private GeneralTransform _transform2D; private GeneralTransform _transform2DInverse; // the camera being used on the 3D viewport private Camera _camera; private Size _viewSize; private Rect3D _boundingRect; // transformations through the 3D scene private Matrix3D _worldTransformation; private GeneralTransform3DTo2D _objectToViewport; // the cache of valid edges List_validEdgesCache = null; // the "ring" around the element with capture to use in the capture case private const double BUFFER_SIZE = 2.0; private enum PolygonSide { FRONT, BACK }; } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved. using MS.Internal; using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.Design.Serialization; using System.Diagnostics; using System.Globalization; using System.Reflection; using System.Runtime.InteropServices; using System.Security.Permissions; using System.Windows; using System.Windows.Media.Animation; using System.Windows.Media.Composition; using System.Windows.Markup; using System.Windows.Media; using System.Windows.Media.Media3D; using MS.Internal.PresentationCore; using MS.Internal.Media3D; using SR = MS.Internal.PresentationCore.SR; using SRID = MS.Internal.PresentationCore.SRID; namespace MS.Internal.Media3D { /// /// Helper class that encapsulates return data needed for the /// hit test capture methods. /// internal class HitTestEdge { ////// Constructs a new hit test edge /// /// First edge point /// Second edge point /// Texture coordinate of first edge point /// Texture coordinate of second edge point public HitTestEdge(Point3D p1, Point3D p2, Point uv1, Point uv2) { _p1 = p1; _p2 = p2; _uv1 = uv1; _uv2 = uv2; } ////// Projects the stored 3D points in to 2D. /// /// The transformation matrix to use public void Project(GeneralTransform3DTo2D objectToViewportTransform) { Point projPoint1 = objectToViewportTransform.Transform(_p1); Point projPoint2 = objectToViewportTransform.Transform(_p2); _p1Transformed = new Point(projPoint1.X, projPoint1.Y); _p2Transformed = new Point(projPoint2.X, projPoint2.Y); } internal Point3D _p1, _p2; internal Point _uv1, _uv2; // the transformed Point3D value internal Point _p1Transformed, _p2Transformed; } ////// This transform allows one to go from 2D through 3D and back in to 2D /// internal class GeneralTransform2DTo3DTo2D : GeneralTransform { ////// Constructor /// /// The Visual3D that contains the 2D visual /// The visual on the Visual3D internal GeneralTransform2DTo3DTo2D(Viewport2DVisual3D visual3D, Visual fromVisual) { IsInverse = false; // get a copy of the geometry information - we store our own model to reuse hit // test code on the GeometryModel3D _geometry = new MeshGeometry3D(); _geometry.Positions = visual3D.InternalPositionsCache; _geometry.TextureCoordinates = visual3D.InternalTextureCoordinatesCache; _geometry.TriangleIndices = visual3D.InternalTriangleIndicesCache; _geometry.Freeze(); // get a copy of the size of the visual brush and the rect on the // visual that the transform is going to/from Visual visual3Dchild = visual3D.Visual; _visualBrushBounds = visual3Dchild.CalculateSubgraphRenderBoundsOuterSpace(); _visualBounds = fromVisual.CalculateSubgraphRenderBoundsInnerSpace(); // get the transform that will let us go from the fromVisual to its last 2D // parent before it reaches the 3D part of the graph (i.e. visual3D.Child) GeneralTransformGroup transformGroup = new GeneralTransformGroup(); transformGroup.Children.Add(fromVisual.TransformToAncestor(visual3Dchild)); transformGroup.Children.Add(visual3Dchild.TransformToOuterSpace()); transformGroup.Freeze(); _transform2D = transformGroup; // store the inverse as well _transform2DInverse = (GeneralTransform)_transform2D.Inverse; if (_transform2DInverse != null) { _transform2DInverse.Freeze(); } // make a copy of the camera and other values on the Viewport3D Viewport3DVisual viewport3D = (Viewport3DVisual)VisualTreeHelper.GetContainingVisual2D(visual3D); _camera = viewport3D.Camera; if (_camera != null) { _camera = (Camera)viewport3D.Camera.GetAsFrozen(); } _viewSize = viewport3D.Viewport.Size; _boundingRect = viewport3D.ComputeSubgraphBounds3D(); _objectToViewport = visual3D.TransformToAncestor(viewport3D); // if the transform was not possible, it could be null - check before freezing if (_objectToViewport != null) { _objectToViewport.Freeze(); } // store the needed transformations for the various operations _worldTransformation = M3DUtil.GetWorldTransformationMatrix(visual3D); _validEdgesCache = null; } internal GeneralTransform2DTo3DTo2D() { } ////// Transforms a point /// /// input point /// output point ///false if the point cannot be transformed public override bool TryTransform(Point inPoint, out Point result) { if (IsInverse) { return TryInverseTransform(inPoint, out result); } else { return TryRegularTransform(inPoint, out result); } } ////// Performs the transform that goes from the parent Viewport3DVisual down in to the 2D on 3D /// contained within the 3D scene /// /// input point /// output point ///false if the point cannot be transformed private bool TryInverseTransform(Point inPoint, out Point result) { // set up the hit test parameters double distanceAdjust; bool foundIntersection = false; if (_camera != null) { RayHitTestParameters rayHitTestParameters = _camera.RayFromViewportPoint(inPoint, _viewSize, _boundingRect, out distanceAdjust); rayHitTestParameters.PushVisualTransform(new MatrixTransform3D(_worldTransformation)); // perfrom the hit test // no back material so we only need to concern ourselves with the front faces Point pointHit = new Point(); _geometry.RayHitTest(rayHitTestParameters, FaceType.Front); rayHitTestParameters.RaiseCallback(delegate (HitTestResult rawresult) { RayHitTestResult rayResult = rawresult as RayHitTestResult; if (rayResult != null) { foundIntersection = Viewport2DVisual3D.GetIntersectionInfo(rayResult, out pointHit); } return HitTestResultBehavior.Stop; }, null, HitTestResultBehavior.Continue, distanceAdjust); // perform capture positioning if we didn't hit anything and something has capture if (!foundIntersection) { foundIntersection = HandleOffMesh(inPoint, out pointHit); } // compute final point result = Viewport2DVisual3D.TextureCoordsToVisualCoords(pointHit, _visualBrushBounds); } else { result = new Point(); } return foundIntersection; } ////// Function to deal with mouse capture when off the mesh. /// /// The location of the mouse /// output point private bool HandleOffMesh(Point mousePos, out Point outPoint) { Point[] visCorners = new Point[4]; if (_validEdgesCache == null) { // get the points relative to the parent visCorners[0] = _transform2D.Transform(new Point(_visualBounds.Left, _visualBounds.Top)); visCorners[1] = _transform2D.Transform(new Point(_visualBounds.Right, _visualBounds.Top)); visCorners[2] = _transform2D.Transform(new Point(_visualBounds.Right, _visualBounds.Bottom)); visCorners[3] = _transform2D.Transform(new Point(_visualBounds.Left, _visualBounds.Bottom)); // get the u,v texture coordinate values of the above points Point[] texCoordsOfInterest = new Point[4]; for (int i = 0; i < visCorners.Length; i++) { texCoordsOfInterest[i] = Viewport2DVisual3D.VisualCoordsToTextureCoords(visCorners[i], _visualBrushBounds); } // get the edges that map to the given visual _validEdgesCache = GrabValidEdges(texCoordsOfInterest); } // find the closest intersection of the mouse position and the edge list return FindClosestIntersection(mousePos, _validEdgesCache, out outPoint); } ////// Function takes the passed in list of texture coordinate points, and then finds the /// visible outline of the rectangle specified by those points and returns it. /// /// The points specifying the rectangle to search for ///The edges of that rectangle private ListGrabValidEdges(Point[] visualTexCoordBounds) { // our final edge list List hitTestEdgeList = new List (); Dictionary adjInformation = new Dictionary (); // store some important info in local variables for easier access Point3DCollection positions = _geometry.Positions; PointCollection textureCoords = _geometry.TextureCoordinates; Int32Collection triIndices = _geometry.TriangleIndices; // if positions and texture coordinates are null, we can't really find what we need so return immediately if (positions == null || textureCoords == null) { return new List (); } // this call actually gets the object to camera transform, but we will invert it later, and because of that // the local variable is named cameraToObjecTransform. Matrix3D cameraToObjectTransform = _worldTransformation * _camera.GetViewMatrix(); try { cameraToObjectTransform.Invert(); } catch (InvalidOperationException) { return new List (); } Point3D camPosObjSpace = cameraToObjectTransform.Transform(new Point3D(0, 0, 0)); // get the bounding box around the passed in texture coordinates to help // with early rejection tests Rect bbox = Rect.Empty; for (int i = 0; i < visualTexCoordBounds.Length; i++) { bbox.Union(visualTexCoordBounds[i]); } // walk through the triangles - and look for the triangles we care about Point3D[] triangleVertices = new Point3D[3]; Point[] triangleTexCoords = new Point[3]; // switch depending on if the mesh is indexed or not if (triIndices == null || triIndices.Count == 0) { int texCoordCount = textureCoords.Count; // in this case we have a non-indexed mesh int count = positions.Count; count = count - (count % 3); for (int i = 0; i < count; i+=3) { // get the triangle indices Rect triBBox = Rect.Empty; for (int j = 0; j < 3; j++) { triangleVertices[j] = positions[i + j]; if (i + j < texCoordCount) { triangleTexCoords[j] = textureCoords[i + j]; } else { // In the case you have less texture coordinates than positions, MIL will set // missing ones to be 0,0. We do the same to stay consistent. // See CMILMesh3D::CopyTextureCoordinatesFromDoubles triangleTexCoords[j] = new Point(0,0); } triBBox.Union(triangleTexCoords[j]); } if (bbox.IntersectsWith(triBBox)) { ProcessTriangle(triangleVertices, triangleTexCoords, visualTexCoordBounds, hitTestEdgeList, adjInformation, camPosObjSpace); } } } else { // in this case we have an indexed mesh int count = triIndices.Count; int posLimit = positions.Count; int texCoordLimit = textureCoords.Count; int[] indices = new int[3]; for (int i = 2; i < count; i += 3) { // get the triangle indices Rect triBBox = Rect.Empty; bool validTextureCoordinates = true; bool validPositions = true; for (int j = 0; j < 3; j++) { // subtract 2 to take in to account we start i // at the high range of indices indices[j] = triIndices[(i-2) + j]; // if a point or texture coordinate is out of range, end early since this is an error if (indices[j] < 0 || indices[j] >= posLimit) { validPositions = false; break; } if (indices[j] < 0 || indices[j] >= texCoordLimit) { validTextureCoordinates = false; break; } triangleVertices[j] = positions[indices[j]]; triangleTexCoords[j] = textureCoords[indices[j]]; triBBox.Union(triangleTexCoords[j]); } // if the positions were ever invalid, we stop processing - see MeshGeometry3D RayHitTestIndexedList // for reasoning if (!validPositions) { break; } if (validTextureCoordinates && bbox.IntersectsWith(triBBox)) { ProcessTriangle(triangleVertices, triangleTexCoords, visualTexCoordBounds, hitTestEdgeList, adjInformation, camPosObjSpace); } } } // also handle the case of an edge that doesn't also have a backface - i.e a single plane foreach (Edge edge in adjInformation.Keys) { EdgeInfo ei = adjInformation[edge]; if (ei._hasFrontFace && ei._numSharing == 1) { HandleSilhouetteEdge(ei._uv1, ei._uv2, edge._start, edge._end, visualTexCoordBounds, hitTestEdgeList); } } // project all the edges to get at the 2D point of interest if (_objectToViewport != null) { for (int i = 0; i < hitTestEdgeList.Count; i++) { hitTestEdgeList[i].Project(_objectToViewport); } } else { hitTestEdgeList = new List (); } return hitTestEdgeList; } /// /// Processes the passed in triangle by checking to see if it is facing the camera and if /// so searches to see if the texture coordinate edges intersect it. It also looks /// to see if there are any silhouette edges and processes these as well. /// /// The triangle's vertices /// The texture coordinates for those vertices /// The texture coordinate edges to intersect with /// The edge list that results should be placed on /// The adjacency information for the mesh /// private void ProcessTriangle(Point3D[] p, Point[] uv, Point[] visualTexCoordBounds, ListedgeList, Dictionary adjInformation, Point3D camPosObjSpace) { // calculate the normal of the mesh and the vector from a point on the mesh to the camera // for back face removal calculations. Vector3D normal = Vector3D.CrossProduct(p[1] - p[0], p[2] - p[0]); Vector3D dirToCamera = camPosObjSpace - p[0]; // ignore any triangles that have a normal of (0,0,0) if (!(normal.X == 0 && normal.Y == 0 && normal.Z == 0)) { double dotProd = Vector3D.DotProduct(normal, dirToCamera); // if the dot product is > 0 then the triangle is visible, otherwise invisible if (dotProd > 0.0) { // loop over the triangle and update any edge information ProcessTriangleEdges(p, uv, visualTexCoordBounds, PolygonSide.FRONT, edgeList, adjInformation); // intersect the bounds of the visual with the triangle ProcessVisualBoundsIntersections(p, uv, visualTexCoordBounds, edgeList); } else { ProcessTriangleEdges(p, uv, visualTexCoordBounds, PolygonSide.BACK, edgeList, adjInformation); } } } /// /// Function intersects the edges specified by tc with the texture coordinates /// on the passed in triangle. If there are any intersections, the edges /// of these intersections are added to the edgelist /// /// The vertices of the triangle /// The texture coordinates for that triangle /// The texture coordinate edges to be intersected against /// The list of edges any intersecte edges should be added to private void ProcessVisualBoundsIntersections(Point3D[] p, Point[] uv, Point[] visualTexCoordBounds, ListedgeList) { Debug.Assert(uv.Length == p.Length, "vertices and texture coordinate sizes should match"); List pointList = new List (); List uvList = new List (); // loop over the visual's texture coordinate bounds for (int i = 0; i < visualTexCoordBounds.Length; i++) { Point visEdgeStart = visualTexCoordBounds[i]; Point visEdgeEnd = visualTexCoordBounds[(i + 1) % visualTexCoordBounds.Length]; // clear out anything that used to be there pointList.Clear(); uvList.Clear(); // loop over triangle edges bool skipListProcessing = false; for (int j = 0; j < uv.Length; j++) { Point uv1 = uv[j]; Point uv2 = uv[(j + 1) % uv.Length]; Point3D p3D1 = p[j]; Point3D p3D2 = p[(j + 1) % p.Length]; // initial rejection processing if (!((Math.Max(visEdgeStart.X, visEdgeEnd.X) < Math.Min(uv1.X, uv2.X)) || (Math.Min(visEdgeStart.X, visEdgeEnd.X) > Math.Max(uv1.X, uv2.X)) || (Math.Max(visEdgeStart.Y, visEdgeEnd.Y) < Math.Min(uv1.Y, uv2.Y)) || (Math.Min(visEdgeStart.Y, visEdgeEnd.Y) > Math.Max(uv1.Y, uv2.Y)))) { // intersect the two lines bool areCoincident = false; Vector dir = uv2 - uv1; double t = IntersectRayLine(uv1, dir, visEdgeStart, visEdgeEnd, out areCoincident); // if they are coincident then we have two intersections and don't need to // do anymore processing if (areCoincident) { HandleCoincidentLines(visEdgeStart, visEdgeEnd, p3D1, p3D2, uv1, uv2, edgeList); skipListProcessing = true; break; } else if (t >= 0 && t <= 1) { Point intersUV = uv1 + dir * t; Point3D intersPoint3D = p3D1 + (p3D2 - p3D1) * t; double visEdgeDiff = (visEdgeStart - visEdgeEnd).Length; if ((intersUV - visEdgeStart).Length < visEdgeDiff && (intersUV - visEdgeEnd).Length < visEdgeDiff) { pointList.Add(intersPoint3D); uvList.Add(intersUV); } } } } if (!skipListProcessing) { if (pointList.Count >= 2) { edgeList.Add(new HitTestEdge(pointList[0], pointList[1], uvList[0], uvList[1])); } else if (pointList.Count == 1) { Point3D outputPoint; // To avoid an edge cases caused by generating a point extremely // close to one of the bound points, we test if both points are inside // the bounds to be on the safe side - in the worst case we do // extra work or generate a small edge if (M3DUtil.IsPointInTriangle(visEdgeStart, uv, p, out outputPoint)) { edgeList.Add(new HitTestEdge(pointList[0], outputPoint, uvList[0], visEdgeStart)); } if (M3DUtil.IsPointInTriangle(visEdgeEnd, uv, p, out outputPoint)) { edgeList.Add(new HitTestEdge(pointList[0], outputPoint, uvList[0], visEdgeEnd)); } } else { Point3D outputPoint1, outputPoint2; if (M3DUtil.IsPointInTriangle(visEdgeStart, uv, p, out outputPoint1) && M3DUtil.IsPointInTriangle(visEdgeEnd, uv, p, out outputPoint2)) { edgeList.Add(new HitTestEdge(outputPoint1, outputPoint2, visEdgeStart, visEdgeEnd)); } } } } } /// /// Handles adding an edge when the two line segments are coincident. /// /// The texture coordinates of the boundary edge /// The texture coordinates of the boundary edge /// The 3D coordinate of the triangle edge /// The 3D coordinates of the triangle edge /// The texture coordinates of the triangle edge /// The texture coordinates of the triangle edge /// The edge list to add to private void HandleCoincidentLines(Point visUV1, Point visUV2, Point3D tri3D1, Point3D tri3D2, Point triUV1, Point triUV2, ListedgeList) { Point minVisUV, maxVisUV; Point minTriUV, maxTriUV; Point3D minTri3D, maxTri3D; // to be used in final edge creation Point uv1, uv2; Point3D p1, p2; // order the points and give refs to them for ease of use if (Math.Abs(visUV1.X - visUV2.X) > Math.Abs(visUV1.Y - visUV2.Y)) { if (visUV1.X <= visUV2.X) { minVisUV = visUV1; maxVisUV = visUV2; } else { minVisUV = visUV2; maxVisUV = visUV1; } if (triUV1.X <= triUV2.X) { minTriUV = triUV1; minTri3D = tri3D1; maxTriUV = triUV2; maxTri3D = tri3D2; } else { minTriUV = triUV2; minTri3D = tri3D2; maxTriUV = triUV1; maxTri3D = tri3D1; } // now actually create the edge // compute the minimum value if (minVisUV.X < minTriUV.X) { uv1 = minTriUV; p1 = minTri3D; } else { uv1 = minVisUV; p1 = minTri3D + (minVisUV.X - minTriUV.X) / (maxTriUV.X - minTriUV.X) * (maxTri3D - minTri3D); } // compute the maximum value if (maxVisUV.X > maxTriUV.X) { uv2 = maxTriUV; p2 = maxTri3D; } else { uv2 = maxVisUV; p2 = minTri3D + (maxVisUV.X - minTriUV.X) / (maxTriUV.X - minTriUV.X) * (maxTri3D - minTri3D); } } else { if (visUV1.Y <= visUV2.Y) { minVisUV = visUV1; maxVisUV = visUV2; } else { minVisUV = visUV2; maxVisUV = visUV1; } if (triUV1.Y <= triUV2.Y) { minTriUV = triUV1; minTri3D = tri3D1; maxTriUV = triUV2; maxTri3D = tri3D2; } else { minTriUV = triUV2; minTri3D = tri3D2; maxTriUV = triUV1; maxTri3D = tri3D1; } // now actually create the edge // compute the minimum value if (minVisUV.Y < minTriUV.Y) { uv1 = minTriUV; p1 = minTri3D; } else { uv1 = minVisUV; p1 = minTri3D + (minVisUV.Y - minTriUV.Y) / (maxTriUV.Y - minTriUV.Y) * (maxTri3D - minTri3D); } // compute the maximum value if (maxVisUV.Y > maxTriUV.Y) { uv2 = maxTriUV; p2 = maxTri3D; } else { uv2 = maxVisUV; p2 = minTri3D + (maxVisUV.Y - minTriUV.Y) / (maxTriUV.Y - minTriUV.Y) * (maxTri3D - minTri3D); } } // add the edge edgeList.Add(new HitTestEdge(p1, p2, uv1, uv2)); } /// /// Intersects a ray with the line specified by the passed in end points. The parameterized coordinate along the ray of /// intersection is returned. /// /// The ray origin /// The ray direction /// First point of the line to intersect against /// Second point of the line to intersect against /// Whether the ray and line are coincident ////// The parameter along the ray of the point of intersection. /// If the ray and line are parallel and not coincident, this will be -1. /// private double IntersectRayLine(Point o, Vector d, Point p1, Point p2, out bool coinc) { coinc = false; // deltas double dy = p2.Y - p1.Y; double dx = p2.X - p1.X; // handle case of a vertical line if (dx == 0) { if (d.X == 0) { coinc = (o.X == p1.X); return -1; } else { return (p2.X - o.X) / d.X; } } // now need to do more general intersection double numer = (o.X - p1.X) * dy / dx - o.Y + p1.Y; double denom = (d.Y - d.X * dy / dx); // if denominator is zero, then the lines are parallel if (denom == 0) { double b0 = -o.X * dy / dx + o.Y; double b1 = -p1.X * dy / dx + p1.Y; coinc = (b0 == b1); return -1; } else { return (numer / denom); } } ////// Helper structure to represent an edge /// private struct Edge { public Edge(Point3D s, Point3D e) { _start = s; _end = e; } public Point3D _start; public Point3D _end; } ////// Information about an edge such as whether it belongs to a front/back facing /// triangle, the texture coordinates for the edge, and how many polygons refer /// to that edge. /// private class EdgeInfo { public EdgeInfo() { _hasFrontFace = _hasBackFace = false; _numSharing = 0; } public bool _hasFrontFace; public bool _hasBackFace; public Point _uv1; public Point _uv2; public int _numSharing; } ////// Processes the edges of the given triangle. It does so by updating /// the adjacency information based on the direction the polygon is facing. /// If there is a silhouette edge found, then this edge is added to the list /// of edges if it is within the texture coordinate bounds passed to the function. /// /// The triangle's vertices /// The texture coordinates for those vertices /// The texture coordinate edges being searched for /// Which side the polygon is facing (greateer than 0 front, less than 0 back) /// The list of edges comprosing the visual outline /// The adjacency information structure private void ProcessTriangleEdges(Point3D[] p, Point[] uv, Point[] visualTexCoordBounds, PolygonSide polygonSide, ListedgeList, Dictionary adjInformation) { // loop over all the edges and add them to the adjacency list for (int i = 0; i < p.Length; i++) { Point uv1, uv2; Point3D p3D1 = p[i]; Point3D p3D2 = p[(i + 1) % p.Length]; Edge edge; // order the edge points so insertion in to adjInformation is consistent if (p3D1.X < p3D2.X || (p3D1.X == p3D2.X && p3D1.Y < p3D2.Y) || (p3D1.X == p3D2.X && p3D1.Y == p3D2.Y && p3D1.Z < p3D1.Z)) { edge = new Edge(p3D1, p3D2); uv1 = uv[i]; uv2 = uv[(i + 1) % p.Length]; } else { edge = new Edge(p3D2, p3D1); uv2 = uv[i]; uv1 = uv[(i + 1) % p.Length]; } // look up the edge information EdgeInfo edgeInfo; if (adjInformation.ContainsKey(edge)) { edgeInfo = adjInformation[edge]; } else { edgeInfo = new EdgeInfo(); adjInformation[edge] = edgeInfo; } edgeInfo._numSharing++; // whether or not the edge has already been added to the edge list bool alreadyAdded = edgeInfo._hasBackFace && edgeInfo._hasFrontFace; // add the edge to the info list if (polygonSide == PolygonSide.FRONT) { edgeInfo._hasFrontFace = true; edgeInfo._uv1 = uv1; edgeInfo._uv2 = uv2; } else { edgeInfo._hasBackFace = true; } // if the sides are different we may need to add an edge if (!alreadyAdded && edgeInfo._hasBackFace && edgeInfo._hasFrontFace) { HandleSilhouetteEdge(edgeInfo._uv1, edgeInfo._uv2, edge._start, edge._end, visualTexCoordBounds, edgeList); } } } /// /// Handles intersecting a silhouette edge against the passed in texture coordinate /// bounds. It behaves similarly to the case of intersection the bounds with a triangle /// except the testing order is switched. /// /// The texture coordinates of the edge /// The texture coordinates of the edge /// The 3D point of the edge /// The 3D point of the edge /// The texture coordinate bounds /// The list of edges private void HandleSilhouetteEdge(Point uv1, Point uv2, Point3D p3D1, Point3D p3D2, Point[] bounds, ListedgeList) { List pointList = new List (); List uvList = new List (); Vector dir = uv2 - uv1; // loop over object bounds for (int i = 0; i < bounds.Length; i++) { Point visEdgeStart = bounds[i]; Point visEdgeEnd = bounds[(i + 1) % bounds.Length]; // initial rejection processing if (!((Math.Max(visEdgeStart.X, visEdgeEnd.X) < Math.Min(uv1.X, uv2.X)) || (Math.Min(visEdgeStart.X, visEdgeEnd.X) > Math.Max(uv1.X, uv2.X)) || (Math.Max(visEdgeStart.Y, visEdgeEnd.Y) < Math.Min(uv1.Y, uv2.Y)) || (Math.Min(visEdgeStart.Y, visEdgeEnd.Y) > Math.Max(uv1.Y, uv2.Y)))) { // intersect the two lines bool areCoincident = false; double t = IntersectRayLine(uv1, dir, visEdgeStart, visEdgeEnd, out areCoincident); // silhouette edge processing will only include non-coincident lines if (areCoincident) { // if it's coincident, we'll let the normal processing handle this edge return; } else if (t >= 0 && t <= 1) { Point intersUV = uv1 + dir * t; Point3D intersPoint3D = p3D1 + (p3D2 - p3D1) * t; double visEdgeDiff = (visEdgeStart - visEdgeEnd).Length; if ((intersUV - visEdgeStart).Length < visEdgeDiff && (intersUV - visEdgeEnd).Length < visEdgeDiff) { pointList.Add(intersPoint3D); uvList.Add(intersUV); } } } } if (pointList.Count >= 2) { edgeList.Add(new HitTestEdge(pointList[0], pointList[1], uvList[0], uvList[1])); } else if (pointList.Count == 1) { // for the case that uv1/2 is actually a point on or extremely close to the bounds // of the polygon, we do the pointinpolygon test on both to avoid any numerical // precision issues - in the worst case we end up with a very small edge and // the right edge if (IsPointInPolygon(bounds, uv1)) { edgeList.Add(new HitTestEdge(pointList[0], p3D1, uvList[0], uv1)); } if (IsPointInPolygon(bounds, uv2)) { edgeList.Add(new HitTestEdge(pointList[0], p3D2, uvList[0], uv2)); } } else { if (IsPointInPolygon(bounds, uv1) && IsPointInPolygon(bounds, uv2)) { edgeList.Add(new HitTestEdge(p3D1, p3D2, uv1, uv2)); } } } /// /// Function tests to see whether the point p is contained within the polygon /// specified by the list of points passed to the function. p is considered within /// this polygon if it is on the same side of all the edges. A point on any of /// the edges of the polygon is not considered within the polygon. /// /// The polygon to test against /// The point to be tested against ///Whether the point is in the polygon private bool IsPointInPolygon(Point[] polygon, Point p) { bool sign = false; for (int i = 0; i < polygon.Length; i++) { double crossProduct = Vector.CrossProduct(polygon[(i + 1) % polygon.Length] - polygon[i], polygon[i] - p); bool currSign = crossProduct > 0; if (i == 0) { sign = currSign; } else { if (sign != currSign) return false; } } return true; } ////// Finds the point in edges that is closest ot the mouse position. Updates closestIntersectionInfo /// with the results of this calculation /// /// The mouse position /// The edges to test against /// The final intersection point ///The closest intersection point private bool FindClosestIntersection(Point mousePos, Listedges, out Point finalPoint) { bool success = false; double closestDistance = Double.MaxValue; Point closestIntersection = new Point(); // the uv of the closest intersection finalPoint = new Point(); // Find the closest point to the mouse position for (int i=0, count = edges.Count; i < count; i++) { Vector v1 = mousePos - edges[i]._p1Transformed; Vector v2 = edges[i]._p2Transformed - edges[i]._p1Transformed; Point currClosest; double distance; // calculate the distance from the mouse position to this edge // The closest distance can be computed by projecting v1 on to v2. If the // projectiong occurs between _p1Transformed and _p2Transformed, then this is the // closest point. Otherwise, depending on which side it lies, it is either _p1Transformed // or _p2Transformed. // // The projection equation is given as: (v1 DOT v2) / (v2 DOT v2) * v2. // v2 DOT v2 will always be positive. Thus, if v1 DOT v2 is negative, we know the projection // will occur before _p1Transformed (and so it is the closest point). If (v1 DOT v2) is greater // than (v2 DOT v2), then we have gone passed _p2Transformed and so it is the closest point. // Otherwise the projection gives us this value. // double denom = v2 * v2; if (denom == 0) { currClosest = edges[i]._p1Transformed; distance = v1.Length; } else { double numer = v2 * v1; if (numer < 0) { currClosest = edges[i]._p1Transformed; } else { if (numer > denom) { currClosest = edges[i]._p2Transformed; } else { currClosest = edges[i]._p1Transformed + (numer / denom) * v2; } } distance = (mousePos - currClosest).Length; } // see if we found a new closest distance if (distance < closestDistance) { closestDistance = distance; if (denom != 0) { closestIntersection = ((currClosest - edges[i]._p1Transformed).Length / Math.Sqrt(denom) * (edges[i]._uv2 - edges[i]._uv1)) + edges[i]._uv1; } else { closestIntersection = edges[i]._uv1; } } } if (closestDistance != Double.MaxValue) { Point ptOnVisual = Viewport2DVisual3D.TextureCoordsToVisualCoords(closestIntersection, _visualBrushBounds); if (_transform2DInverse != null) { Point ptRelToCapture = _transform2DInverse.Transform(ptOnVisual); // we want to "ring" around the outside so things like buttons are not pressed when we move off the mesh // this code here does that - the +BUFFER_SIZE and -BUFFER_SIZE are to give a bit of a // buffer for any numerical issues if (ptRelToCapture.X <= _visualBounds.Left + 1) ptRelToCapture.X -= BUFFER_SIZE; if (ptRelToCapture.Y <= _visualBounds.Top + 1) ptRelToCapture.Y -= BUFFER_SIZE; if (ptRelToCapture.X >= _visualBounds.Right - 1) ptRelToCapture.X += BUFFER_SIZE; if (ptRelToCapture.Y >= _visualBounds.Bottom - 1) ptRelToCapture.Y += BUFFER_SIZE; Point finalVisualPoint = _transform2D.Transform(ptRelToCapture); finalPoint = Viewport2DVisual3D.VisualCoordsToTextureCoords(finalVisualPoint, _visualBrushBounds); success = true; } } return success; } /// /// Performs the transform that goes from 2D on 3D content contained within the 3D scene /// up to the containing Viewport3DVisual /// /// input point /// output point ///false if the point cannot be transformed private bool TryRegularTransform(Point inPoint, out Point result) { Point texCoord = Viewport2DVisual3D.VisualCoordsToTextureCoords(inPoint, _visualBrushBounds); // need to walk the texture coordinates and look for where this point intersects one of them Point3D point3D; if (_objectToViewport != null && Viewport2DVisual3D.Get3DPointFor2DCoordinate(texCoord, out point3D, _geometry.Positions, _geometry.TextureCoordinates, _geometry.TriangleIndices)) { // convert from this 3D point up to the containing Viewport3D return _objectToViewport.TryTransform(point3D, out result); } else { result = new Point(); return false; } } ////// Transform the rect bounds into the smallest axis alligned bounding box that /// contains all the point in the original bounds. /// /// ///public override Rect TransformBounds(Rect rect) { List edges = null; // intersect the rect given to us with the bounds of the visual brush to guarantee the rect we are // searching for is within the visual brush rect.Intersect(_visualBrushBounds); // get the texture coordinate values for the rect's corners Point[] texCoordsOfInterest = new Point[4]; texCoordsOfInterest[0] = Viewport2DVisual3D.VisualCoordsToTextureCoords(rect.TopLeft, _visualBrushBounds); texCoordsOfInterest[1] = Viewport2DVisual3D.VisualCoordsToTextureCoords(rect.TopRight, _visualBrushBounds); texCoordsOfInterest[2] = Viewport2DVisual3D.VisualCoordsToTextureCoords(rect.BottomRight, _visualBrushBounds); texCoordsOfInterest[3] = Viewport2DVisual3D.VisualCoordsToTextureCoords(rect.BottomLeft, _visualBrushBounds); // get the edges that map to the given rect edges = GrabValidEdges(texCoordsOfInterest); Rect result = Rect.Empty; if (edges != null) { for (int i = 0, count = edges.Count; i < count; i++) { result.Union(edges[i]._p1Transformed); result.Union(edges[i]._p2Transformed); } } return result; } /// /// Returns the inverse transform if there is one, null otherwise /// public override GeneralTransform Inverse { get { // internal class - no ReadPreamble needed // ReadPreamble(); GeneralTransform2DTo3DTo2D inverseTransform = (GeneralTransform2DTo3DTo2D)Clone(); inverseTransform.IsInverse = !IsInverse; return inverseTransform; } } ////// Returns a best effort affine transform /// internal override Transform AffineTransform { [FriendAccessAllowed] // Built into Core, also used by Framework. get { return null; } } ////// Returns true if the transform is an inverse /// internal bool IsInverse { get { return _fInverse; } set { _fInverse = value; } } ////// Implementation of ///Freezable.CreateInstanceCore . ///The new Freezable. protected override Freezable CreateInstanceCore() { return new GeneralTransform2DTo3DTo2D(); } ////// Implementation of /// protected override void CloneCore(Freezable sourceFreezable) { GeneralTransform2DTo3DTo2D transform = (GeneralTransform2DTo3DTo2D)sourceFreezable; base.CloneCore(sourceFreezable); CopyCommon(transform); } ///Freezable.CloneCore . ////// Implementation of /// protected override void CloneCurrentValueCore(Freezable sourceFreezable) { GeneralTransform2DTo3DTo2D transform = (GeneralTransform2DTo3DTo2D)sourceFreezable; base.CloneCurrentValueCore(sourceFreezable); CopyCommon(transform); } ///Freezable.CloneCurrentValueCore . ////// Implementation of /// protected override void GetAsFrozenCore(Freezable sourceFreezable) { GeneralTransform2DTo3DTo2D transform = (GeneralTransform2DTo3DTo2D)sourceFreezable; base.GetAsFrozenCore(sourceFreezable); CopyCommon(transform); } ///Freezable.GetAsFrozenCore . ////// Implementation of /// protected override void GetCurrentValueAsFrozenCore(Freezable sourceFreezable) { GeneralTransform2DTo3DTo2D transform = (GeneralTransform2DTo3DTo2D)sourceFreezable; base.GetCurrentValueAsFrozenCore(sourceFreezable); CopyCommon(transform); } ///Freezable.GetCurrentValueAsFrozenCore . ////// Clones values that do not have corresponding DPs /// /// private void CopyCommon(GeneralTransform2DTo3DTo2D transform) { _fInverse = transform._fInverse; _geometry = transform._geometry; _visualBounds = transform._visualBounds; _visualBrushBounds = transform._visualBrushBounds; _transform2D = transform._transform2D; _transform2DInverse = transform._transform2DInverse; _camera = transform._camera; _viewSize = transform._viewSize; _boundingRect = transform._boundingRect; _worldTransformation = transform._worldTransformation; _objectToViewport = transform._objectToViewport; _validEdgesCache = null; } private bool _fInverse; // the geometry of the 3D object private MeshGeometry3D _geometry; // the size of the visual brush and the visual on it we're interested in private Rect _visualBounds; private Rect _visualBrushBounds; // the transform to go in to and out of the coordinate space fo the visual we're // interested in private GeneralTransform _transform2D; private GeneralTransform _transform2DInverse; // the camera being used on the 3D viewport private Camera _camera; private Size _viewSize; private Rect3D _boundingRect; // transformations through the 3D scene private Matrix3D _worldTransformation; private GeneralTransform3DTo2D _objectToViewport; // the cache of valid edges List_validEdgesCache = null; // the "ring" around the element with capture to use in the capture case private const double BUFFER_SIZE = 2.0; private enum PolygonSide { FRONT, BACK }; } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- ProtocolsConfigurationHandler.cs
- GacUtil.cs
- VersionConverter.cs
- StaticExtension.cs
- GraphicsContainer.cs
- PersistencePipeline.cs
- ReliableOutputConnection.cs
- Input.cs
- DataGridViewUtilities.cs
- AssociationTypeEmitter.cs
- XmlSchemaAny.cs
- DynamicDataExtensions.cs
- TransformedBitmap.cs
- CommandValueSerializer.cs
- EventToken.cs
- Column.cs
- DataGridViewSortCompareEventArgs.cs
- CharacterShapingProperties.cs
- WinEventTracker.cs
- ValidationHelper.cs
- ImageList.cs
- DataGridViewTopRowAccessibleObject.cs
- TdsRecordBufferSetter.cs
- MatrixConverter.cs
- RegistryPermission.cs
- WpfGeneratedKnownTypes.cs
- TreeView.cs
- XmlSchemaGroup.cs
- httpserverutility.cs
- IPPacketInformation.cs
- SourceFilter.cs
- ComponentRenameEvent.cs
- ValidatingPropertiesEventArgs.cs
- MetadataItemEmitter.cs
- Exceptions.cs
- TraceLevelStore.cs
- SubtreeProcessor.cs
- HWStack.cs
- PolyQuadraticBezierSegment.cs
- IpcChannelHelper.cs
- CompositeControl.cs
- UInt64.cs
- QilInvokeLateBound.cs
- HatchBrush.cs
- TextEffectCollection.cs
- ActivityBindForm.Designer.cs
- FusionWrap.cs
- XamlRtfConverter.cs
- WebPartConnectionCollection.cs
- PipeConnection.cs
- NetTcpSectionData.cs
- IdentifierCreationService.cs
- OuterGlowBitmapEffect.cs
- AsyncCompletedEventArgs.cs
- LocalizationComments.cs
- WebEventCodes.cs
- CommentGlyph.cs
- CodeDelegateCreateExpression.cs
- HMACSHA512.cs
- ServiceCredentials.cs
- WorkflowShape.cs
- BatchWriter.cs
- BinHexDecoder.cs
- XmlQueryCardinality.cs
- InputLanguage.cs
- Point3DKeyFrameCollection.cs
- TabRenderer.cs
- IArgumentProvider.cs
- ScriptControlManager.cs
- LogReserveAndAppendState.cs
- XPathNodePointer.cs
- ButtonFieldBase.cs
- x509store.cs
- GridLength.cs
- TextTreeNode.cs
- OleCmdHelper.cs
- LinqDataSource.cs
- ChannelPoolSettings.cs
- Comparer.cs
- PropertyIDSet.cs
- WindowsAuthenticationEventArgs.cs
- recordstate.cs
- AuthenticationServiceManager.cs
- SiteMapNode.cs
- SelectManyQueryOperator.cs
- MatchingStyle.cs
- _DigestClient.cs
- MimeMapping.cs
- ClickablePoint.cs
- WebConfigurationFileMap.cs
- Int64Animation.cs
- FtpWebResponse.cs
- QilReplaceVisitor.cs
- AttributedMetaModel.cs
- SecurityPermission.cs
- DataFormat.cs
- EntitySqlQueryCacheEntry.cs
- InvalidProgramException.cs
- WindowsAltTab.cs
- TrustManager.cs