public void PaintBackground(IGraphicsContext3D g, IPlotArea layer) { if (null == _background) { return; } var cs = layer.CoordinateSystem; if (layer.CoordinateSystem is CS.G3DCartesicCoordinateSystem) { var p = new PointD3D[4]; p[0] = cs.GetPointOnPlane(_planeID, 0, 0); p[1] = cs.GetPointOnPlane(_planeID, 0, 1); p[2] = cs.GetPointOnPlane(_planeID, 1, 0); p[3] = cs.GetPointOnPlane(_planeID, 1, 1); var normal = VectorD3D.CrossProduct(p[1] - p[0], p[2] - p[0]).Normalized; var buffer = g.GetPositionNormalIndexedTriangleBuffer(_background); if (null != buffer.PositionNormalIndexedTriangleBuffer) { // front faces var offs = buffer.IndexedTriangleBuffer.VertexCount; for (int i = 0; i < 4; ++i) { buffer.PositionNormalIndexedTriangleBuffer.AddTriangleVertex(p[i].X, p[i].Y, p[i].Z, normal.X, normal.Y, normal.Z); } buffer.IndexedTriangleBuffer.AddTriangleIndices(0 + offs, 1 + offs, 3 + offs); buffer.IndexedTriangleBuffer.AddTriangleIndices(2 + offs, 0 + offs, 3 + offs); // back faces offs = buffer.IndexedTriangleBuffer.VertexCount; for (int i = 0; i < 4; ++i) { buffer.PositionNormalIndexedTriangleBuffer.AddTriangleVertex(p[i].X, p[i].Y, p[i].Z, -normal.X, -normal.Y, -normal.Z); } buffer.IndexedTriangleBuffer.AddTriangleIndices(0 + offs, 3 + offs, 1 + offs); buffer.IndexedTriangleBuffer.AddTriangleIndices(2 + offs, 3 + offs, 0 + offs); } else { throw new NotImplementedException(); } } else { throw new NotImplementedException(); } }
/// <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 } }