/// <summary> /// Determines whether the provided plane is hit by the ray (this is almost ever the case except if the plane's normal is perpendicular to the hit ray), /// and determines the point on the plane that is hit. /// </summary> /// <param name="plane">The plane.</param> /// <param name="planePointHit">The point on the plane that is hit (if the return value is true).</param> /// <returns>True if the plane is hit; otherwise false.</returns> public bool IsPlaneHitByRay(PlaneD3D plane, out PointD3D planePointHit) { plane = plane.Normalized; double pnX = plane.X; double pnY = plane.Y; double pnZ = plane.Z; var l = _hitTransformation; double denom = pnX * (l.M21 * l.M32 - l.M22 * l.M31) + pnY * (l.M12 * l.M31 - l.M11 * l.M32) + pnZ * (l.M11 * l.M22 - l.M12 * l.M21); if (0 == denom) { planePointHit = PointD3D.Empty; return(false); } // Point on the plane that is closest to orgin double ppX = plane.W * pnX; double ppY = plane.W * pnY; double ppZ = plane.W * pnZ; double numX = pnX * (-(l.M22 * l.M31 * ppX) + l.M21 * l.M32 * ppX) + pnY * (l.M32 * l.M41 - l.M31 * l.M42 - l.M22 * l.M31 * ppY + l.M21 * l.M32 * ppY) + pnZ * (-(l.M22 * l.M41) + l.M21 * l.M42 - l.M22 * l.M31 * ppZ + l.M21 * l.M32 * ppZ); double numY = pnX * (-(l.M32 * l.M41) + l.M31 * l.M42 + l.M12 * l.M31 * ppX - l.M11 * l.M32 * ppX) + pnY * (l.M12 * l.M31 * ppY - l.M11 * l.M32 * ppY) + pnZ * (l.M12 * l.M41 - l.M11 * l.M42 + l.M12 * l.M31 * ppZ - l.M11 * l.M32 * ppZ); double numZ = pnX * (l.M22 * l.M41 - l.M21 * l.M42 - l.M12 * l.M21 * ppX + l.M11 * l.M22 * ppX) + pnY * (-(l.M12 * l.M41) + l.M11 * l.M42 - l.M12 * l.M21 * ppY + l.M11 * l.M22 * ppY) + pnZ * (-(l.M12 * l.M21 * ppZ) + l.M11 * l.M22 * ppZ); planePointHit = new PointD3D(numX / denom, numY / denom, numZ / denom); return(true); }
public PositionNormalColorIndexedTriangleBufferWithClipping(D3D10GraphicsContext parent, PlaneD3D[] clipPlanes) : base(parent) { if (null == clipPlanes) throw new ArgumentNullException(nameof(clipPlanes)); ClipPlanes = clipPlanes; }
public MaterialPlusClippingPlusColorProviderKey(IMaterial material, PlaneD3D[] clipPlanes, Gdi.Plot.IColorProvider colorProvider) : base(material, clipPlanes) { if (null == colorProvider) throw new ArgumentNullException(nameof(colorProvider)); this.ColorProvider = colorProvider; }
/// <summary> /// Transforms the specified plane p. /// </summary> /// <param name="p">The plane to transform.</param> /// <returns>Plane transformed by this matrix.</returns> public PlaneD3D Transform(PlaneD3D p) { var x = p.X * (M22 * M33 - M23 * M32) + p.Y * (M13 * M32 - M12 * M33) + p.Z * (M12 * M23 - M13 * M22); var y = p.X * (M23 * M31 - M21 * M33) + p.Y * (M11 * M33 - M13 * M31) + p.Z * (M13 * M21 - M11 * M23); var z = p.X * (M21 * M32 - M22 * M31) + p.Y * (M12 * M31 - M11 * M32) + p.Z * (M11 * M22 - M12 * M21); var l = 1 / Math.Sqrt(x * x + y * y + z * z); x *= l; y *= l; z *= l; // Transform the point that was located on the original plane.... var pp = Transform(new PointD3D(p.X * p.W, p.Y * p.W, p.Z * p.W)); // but the transformed point is not neccessarly located from the origin in the direction of the new plane vector // thus we have to take the dot-product between the transformed normal and the transformed plane-point to get the new distance double w = x * pp.X + y * pp.Y + z * pp.Z; return new PlaneD3D(x, y, z, w); }
/// <summary> /// Gets the hit point on that plane of the active layer rectangle, that is facing the camera. /// </summary> /// <param name="doc">The graph document containing the active layer.</param> /// <param name="activeLayer">The active layer of the graph document.</param> /// <param name="hitposition">Hit point in relative screen coordinates. The z-component is the aspect ratio of the screen (y/x).</param> /// <param name="hitPointOnPlaneInActiveLayerCoordinates">Output: The hit point on the plane of the active layer that faces the camera. The hit point is returned in active layer coordinates.</param> /// <param name="rotationsRadian">The rotation angles that can be used e.g. to orient text so that the text is most readable from the current camera setting. Rotation angle around x is the x-component of the returned vector, and so on.</param> /// <exception cref="InvalidProgramException">There should always be a plane of a rectangle that can be hit!</exception> public static void GetHitPointOnActiveLayerPlaneFacingTheCamera(GraphDocument doc, HostLayer activeLayer, PointD3D hitposition, out PointD3D hitPointOnPlaneInActiveLayerCoordinates, out VectorD3D rotationsRadian) { var activeLayerTransformation = activeLayer.TransformationFromRootToHere(); var camera = doc.Camera; var hitData = new HitTestPointData(camera.GetHitRayMatrix(hitposition)); hitData = hitData.NewFromAdditionalTransformation(activeLayerTransformation); // now hitdata are in layer cos var targetToEye = hitData.WorldTransformation.Transform(camera.TargetToEyeVectorNormalized); // targetToEye in layer coordinates var upEye = hitData.WorldTransformation.Transform(camera.UpVectorPerpendicularToEyeVectorNormalized); // camera up vector in layer coordinates // get the face which has the best dot product between the eye vector of the camera and the plane's normal var layerRect = new RectangleD3D(PointD3D.Empty, activeLayer.Size); double maxval = double.MinValue; PlaneD3D maxPlane = PlaneD3D.Empty; foreach (var plane in layerRect.Planes) { double val = VectorD3D.DotProduct(plane.Normal, targetToEye); if (val > maxval) { maxval = val; maxPlane = plane; } } bool isHit = hitData.IsPlaneHitByRay(maxPlane, out hitPointOnPlaneInActiveLayerCoordinates); // hitPointOnPlane is in layer coordinates too if (!isHit) { throw new InvalidProgramException("There should always be a plane of a rectangle that can be hit!"); } VectorD3D zaxis = maxPlane.Normal; VectorD3D yaxis = upEye; // Find y axis perpendicular to zaxis maxval = double.MinValue; foreach (var plane in layerRect.Planes) { double val = VectorD3D.DotProduct(plane.Normal, upEye); if (val > maxval && 0 == VectorD3D.DotProduct(plane.Normal, zaxis)) { maxval = val; yaxis = plane.Normal; } } var xaxis = VectorD3D.CrossProduct(yaxis, zaxis); // now we have all information about the spatial position and orientation of the text: // hitPointOnPlane is the position of the text // maxPlane.Normal is the face orientation of the text // maxUpVector is the up orientation of the text double cx, sx, cy, sy, cz, sz; sy = xaxis.Z; if (1 != Math.Abs(sy)) { cy = Math.Sqrt(1 - sy * sy); cz = xaxis.X / cy; sz = xaxis.Y / cy; sx = yaxis.Z / cy; cx = zaxis.Z / cy; } else // sy is +1, thus cy is zero { // we set x-rotation to zero, i.e. cx==1 and sx==0 cy = 0; cx = 1; sx = 0; cz = yaxis.Y; sz = -yaxis.X; } rotationsRadian = new VectorD3D(Math.Atan2(sx, cx), Math.Atan2(sy, cy), Math.Atan2(sz, cz)); }
private void BuildImageWithUColor( IGraphicsContext3D g, IPlotArea gl, IReadOnlyList <double> lx, IReadOnlyList <double> ly, IROMatrix <double> matrix) { IPositionNormalUIndexedTriangleBuffer buffers; if (gl.ClipDataToFrame == LayerDataClipping.None && !_clipToLayer) { buffers = g.GetPositionNormalUIndexedTriangleBuffer(_material, null, _colorProvider); } else { var clipPlanes = new PlaneD3D[6]; clipPlanes[0] = new PlaneD3D(1, 0, 0, 0); clipPlanes[1] = new PlaneD3D(-1, 0, 0, -gl.Size.X); clipPlanes[2] = new PlaneD3D(0, 1, 0, 0); clipPlanes[3] = new PlaneD3D(0, -1, 0, -gl.Size.Y); clipPlanes[4] = new PlaneD3D(0, 0, 1, 0); clipPlanes[5] = new PlaneD3D(0, 0, -1, -gl.Size.Z); buffers = g.GetPositionNormalUIndexedTriangleBuffer(_material, clipPlanes, _colorProvider); } var buf = buffers; var offs = buf.VertexCount; int lxl = lx.Count; int lyl = ly.Count; int lxlm1 = lx.Count - 1; int lylm1 = ly.Count - 1; var vertexPoints = new PointD3D[lxl, lyl]; var isValid = new bool[lxl, lyl]; // array which stores for every point[i, j], if it is valid, to speed up calculations var zScale = gl.ZAxis; for (int i = 0; i < lx.Count; ++i) { for (int j = 0; j < ly.Count; ++j) { double lz = zScale.PhysicalVariantToNormal(matrix[i, j]); gl.CoordinateSystem.LogicalToLayerCoordinates(new Logical3D(lx[i], ly[j], lz), out var pt); isValid[i, j] = !pt.IsNaN; vertexPoints[i, j] = pt; } } // ------------------------------------------------------------------ // ------------------ Calculation of normals ------------------------ // (this can be laborious, if both neighboring points are invalid) // ------------------------------------------------------------------ for (int i = 0; i < lxl; ++i) { for (int j = 0; j < lyl; ++j) { if (isValid[i, j]) { var pm = vertexPoints[i, j]; // Strategy here: we calculate the vectors (right-left) and (upper-lower) and calculate the cross product. This is our normal vector. // right - left var vec1 = vertexPoints[(i < lxlm1 && isValid[i + 1, j]) ? i + 1 : i, j] - // right side vertexPoints[(i > 0 && isValid[i - 1, j]) ? i - 1 : i, j]; // left side if (vec1.IsEmpty) // if vector 1 is empty (because both the right _and_ the left neighbor points are invalid), then we have to try the diagonals { bool rightup = (i < lxlm1 && j < lylm1 && isValid[i + 1, j + 1]); // right-up neighbor valid? bool leftlow = (i > 0 && j > 0 && isValid[i - 1, j - 1]); // left-lower neighbor valid? var vec1a = vertexPoints[rightup ? i + 1 : i, rightup ? j + 1 : j] - // right / upper side vertexPoints[leftlow ? i - 1 : i, leftlow ? j - 1 : j]; // left / lower side bool rightlow = (i < lxlm1 && j > 0 && isValid[i + 1, j - 1]); // right-lower neighbor valid? bool leftup = (i > 0 && j < lylm1 && isValid[i - 1, j + 1]); // left-upper neighbor valid? var vec1b = vertexPoints[rightlow ? i + 1 : i, rightlow ? j - 1 : j] - // right / lower side vertexPoints[leftup ? i - 1 : i, leftup ? j + 1 : j]; // left / upper side vec1 = vec1a + vec1b; // if one of these two vectors is empty, it doesn't matter for the addition } // upper - lower var vec2 = vertexPoints[i, (j < lylm1 && isValid[i, j + 1]) ? j + 1 : j] - // upper side vertexPoints[i, (j > 0 && isValid[i, j - 1]) ? j - 1 : j]; // lower side if (vec2.IsEmpty) // if vector 2 is empty (because both the upper _and_ the lower neighbor points are invalid, then we have to try the diagonals { bool rightup = (i < lxlm1 && j < lylm1 && isValid[i + 1, j + 1]); // right-up neighbor valid? bool leftlow = (i > 0 && j > 0 && isValid[i - 1, j - 1]); // left-lower neighbor valid? var vec2a = vertexPoints[rightup ? i + 1 : i, rightup ? j + 1 : j] - // upper side / right vertexPoints[leftlow ? i - 1 : i, leftlow ? j - 1 : j]; // lower side / left bool leftup = (i > 0 && j < lylm1 && isValid[i - 1, j + 1]); // left-upper neighbor valid? bool rightlow = (i < lxlm1 && j > 0 && isValid[i + 1, j - 1]); // right-lower neighbor valid? var vec2b = vertexPoints[leftup ? i - 1 : i, leftup ? j + 1 : j] - // upper side / left vertexPoints[rightlow ? i + 1 : i, rightlow ? j - 1 : j]; // lower side / right vec2 = vec2a + vec2b; // if one of these two vectors is empty, it doesn't matter for the addition } var normal = VectorD3D.CrossProduct(vec1, vec2).Normalized; double lz = null != _colorScale?_colorScale.PhysicalVariantToNormal(matrix[i, j]) : zScale.PhysicalVariantToNormal(matrix[i, j]); buf.AddTriangleVertex(pm.X, pm.Y, pm.Z, normal.X, normal.Y, normal.Z, lz); buf.AddTriangleVertex(pm.X, pm.Y, pm.Z, -normal.X, -normal.Y, -normal.Z, lz); } else // if this point is not valid, we still add triangle vertices to keep the order of points { buf.AddTriangleVertex(double.NaN, double.NaN, double.NaN, double.NaN, double.NaN, double.NaN, double.NaN); buf.AddTriangleVertex(double.NaN, double.NaN, double.NaN, double.NaN, double.NaN, double.NaN, double.NaN); } } } // now add the triangle indices // we don't make the effort to sort out the invalid point, because they are suppressed anyways for (int i = 0; i < lxlm1; ++i) { for (int j = 0; j < lylm1; ++j) { // upper side buf.AddTriangleIndices(offs + 0, offs + 2 * lyl, offs + 2); buf.AddTriangleIndices(offs + 2, offs + 2 * lyl, offs + 2 * lyl + 2); // from below buf.AddTriangleIndices(offs + 0 + 1, offs + 2 + 1, offs + 2 * lyl + 1); buf.AddTriangleIndices(offs + 2 + 1, offs + 2 * lyl + 2 + 1, offs + 2 * lyl + 1); offs += 2; } offs += 2; // one extra increment because inner loop ends at one less than array size } }
private void BuildImageWithUColor( IGraphicsContext3D g, IPlotArea gl, IROVector lx, IROVector ly, IROMatrix matrix) { IPositionNormalUIndexedTriangleBuffer buffers; if (gl.ClipDataToFrame == LayerDataClipping.None && !_clipToLayer) { buffers = g.GetPositionNormalUIndexedTriangleBuffer(_material, null, _colorProvider); } else { var clipPlanes = new PlaneD3D[6]; clipPlanes[0] = new PlaneD3D(1, 0, 0, 0); clipPlanes[1] = new PlaneD3D(-1, 0, 0, -gl.Size.X); clipPlanes[2] = new PlaneD3D(0, 1, 0, 0); clipPlanes[3] = new PlaneD3D(0, -1, 0, -gl.Size.Y); clipPlanes[4] = new PlaneD3D(0, 0, 1, 0); clipPlanes[5] = new PlaneD3D(0, 0, -1, -gl.Size.Z); buffers = g.GetPositionNormalUIndexedTriangleBuffer(_material, clipPlanes, _colorProvider); } var buf = buffers; var offs = buf.VertexCount; int lxl = lx.Length; int lyl = ly.Length; int lxlm1 = lx.Length - 1; int lylm1 = ly.Length - 1; var vertexPoints = new PointD3D[lxl, lyl]; var vertexColors = new Color[lxl, lyl]; PointD3D pt; var zScale = gl.ZAxis; for (int i = 0; i < lx.Length; ++i) { for (int j = 0; j < ly.Length; ++j) { double lz = zScale.PhysicalVariantToNormal(matrix[i, j]); gl.CoordinateSystem.LogicalToLayerCoordinates(new Logical3D(lx[i], ly[j], lz), out pt); vertexPoints[i, j] = pt; vertexColors[i, j] = _colorProvider.GetColor(null == _colorScale ? lz : _colorScale.PhysicalVariantToNormal(matrix[i, j])); // either use the scale of the coordinate system or our own color scale } } // calculate the normals for (int i = 0; i < lx.Length; ++i) { for (int j = 0; j < ly.Length; ++j) { var pm = vertexPoints[i, j]; var vec1 = vertexPoints[Math.Min(i + 1, lxlm1), j] - vertexPoints[Math.Max(i - 1, 0), j]; var vec2 = vertexPoints[i, Math.Min(j + 1, lylm1)] - vertexPoints[i, Math.Max(j - 1, 0)]; var normal = VectorD3D.CrossProduct(vec1, vec2).Normalized; double lz = null != _colorScale ? _colorScale.PhysicalVariantToNormal(matrix[i, j]) : zScale.PhysicalVariantToNormal(matrix[i, j]); buf.AddTriangleVertex(pm.X, pm.Y, pm.Z, normal.X, normal.Y, normal.Z, lz); buf.AddTriangleVertex(pm.X, pm.Y, pm.Z, -normal.X, -normal.Y, -normal.Z, lz); } } for (int i = 0; i < lxlm1; ++i) { for (int j = 0; j < lylm1; ++j) { // upper side buf.AddTriangleIndices(offs + 0, offs + 2 * lyl, offs + 2); buf.AddTriangleIndices(offs + 2, offs + 2 * lyl, offs + 2 * lyl + 2); // from below buf.AddTriangleIndices(offs + 0 + 1, offs + 2 + 1, offs + 2 * lyl + 1); buf.AddTriangleIndices(offs + 2 + 1, offs + 2 * lyl + 2 + 1, offs + 2 * lyl + 1); offs += 2; } offs += 2; // one extra increment because inner loop ends at one less than array size } }
public abstract IPositionNormalUIndexedTriangleBuffer GetPositionNormalUIndexedTriangleBuffer(IMaterial material, PlaneD3D[] clipPlanes, IColorProvider colorProvider);
/// <summary> /// Determines whether the provided plane is hit by the ray (this is almost ever the case except if the plane's normal is perpendicular to the hit ray), /// and determines the point on the plane that is hit. /// </summary> /// <param name="plane">The plane.</param> /// <param name="planePointHit">The point on the plane that is hit (if the return value is true).</param> /// <returns>True if the plane is hit; otherwise false.</returns> public bool IsPlaneHitByRay(PlaneD3D plane, out PointD3D planePointHit) { plane = plane.Normalized; double pnX = plane.X; double pnY = plane.Y; double pnZ = plane.Z; var l = _hitTransformation; double denom = pnX * (l.M21 * l.M32 - l.M22 * l.M31) + pnY * (l.M12 * l.M31 - l.M11 * l.M32) + pnZ * (l.M11 * l.M22 - l.M12 * l.M21); if (0 == denom) { planePointHit = PointD3D.Empty; return false; } // Point on the plane that is closest to orgin double ppX = plane.W * pnX; double ppY = plane.W * pnY; double ppZ = plane.W * pnZ; double numX = pnX * (-(l.M22 * l.M31 * ppX) + l.M21 * l.M32 * ppX) + pnY * (l.M32 * l.M41 - l.M31 * l.M42 - l.M22 * l.M31 * ppY + l.M21 * l.M32 * ppY) + pnZ * (-(l.M22 * l.M41) + l.M21 * l.M42 - l.M22 * l.M31 * ppZ + l.M21 * l.M32 * ppZ); double numY = pnX * (-(l.M32 * l.M41) + l.M31 * l.M42 + l.M12 * l.M31 * ppX - l.M11 * l.M32 * ppX) + pnY * (l.M12 * l.M31 * ppY - l.M11 * l.M32 * ppY) + pnZ * (l.M12 * l.M41 - l.M11 * l.M42 + l.M12 * l.M31 * ppZ - l.M11 * l.M32 * ppZ); double numZ = pnX * (l.M22 * l.M41 - l.M21 * l.M42 - l.M12 * l.M21 * ppX + l.M11 * l.M22 * ppX) + pnY * (-(l.M12 * l.M41) + l.M11 * l.M42 - l.M12 * l.M21 * ppY + l.M11 * l.M22 * ppY) + pnZ * (-(l.M12 * l.M21 * ppZ) + l.M11 * l.M22 * ppZ); planePointHit = new PointD3D(numX / denom, numY / denom, numZ / denom); return true; }
public abstract PositionNormalIndexedTriangleBuffers GetPositionNormalIndexedTriangleBufferWithClipping(IMaterial material, PlaneD3D[] planes);
public override IPositionNormalUIndexedTriangleBuffer GetPositionNormalUIndexedTriangleBuffer(IMaterial material, PlaneD3D[] clipPlanes, Gdi.Plot.IColorProvider colorProvider) { // Transform the clip planes to our coordinate system var clipPlanesTransformed = clipPlanes.Select(plane => _transformation.Transform(plane)).ToArray(); PositionNormalUIndexedTriangleBuffer result; var key = new MaterialPlusClippingPlusColorProviderKey(material, clipPlanesTransformed, colorProvider); if (!_positionNormalUIndexedTriangleBuffers.TryGetValue(key, out result)) { result = new PositionNormalUIndexedTriangleBuffer(this); _positionNormalUIndexedTriangleBuffers.Add(key, result); } return result; }
private PositionNormalColorIndexedTriangleBuffer InternalGetPositionNormalColorIndexedTriangleBuffer(IMaterial material, PlaneD3D[] clipPlanes) { // Transform the clip planes to our coordinate system var clipPlanesTransformed = clipPlanes.Select(plane => _transformation.Transform(plane)).ToArray(); PositionNormalColorIndexedTriangleBuffer result; var key = new MaterialPlusClippingKey(material, clipPlanesTransformed); if (!_positionNormalColorIndexedTriangleBuffers.TryGetValue(key, out result)) { result = new PositionNormalColorIndexedTriangleBufferWithClipping(this, clipPlanesTransformed); _positionNormalColorIndexedTriangleBuffers.Add(key, result); } return result; }
public override PositionNormalIndexedTriangleBuffers GetPositionNormalIndexedTriangleBufferWithClipping(IMaterial material, PlaneD3D[] clipPlanes) { var result = new PositionNormalIndexedTriangleBuffers(); if (material.HasTexture) { throw new NotImplementedException(); } else if (material.HasColor) { throw new NotImplementedException(); } else { result.IndexedTriangleBuffer = result.PositionNormalColorIndexedTriangleBuffer = InternalGetPositionNormalColorIndexedTriangleBuffer(material, clipPlanes); } return result; }
public MaterialPlusClippingKey(IMaterial material, PlaneD3D[] clipPlanes) : base(material) { ClipPlanes = clipPlanes; }