public object Deserialize(object o, Altaxo.Serialization.Xml.IXmlDeserializationInfo info, object parent) { RectangleD3D s = null != o ? (RectangleD3D)o : new RectangleD3D(); s._x = info.GetDouble("X"); s._y = info.GetDouble("Y"); s._z = info.GetDouble("Z"); s._sizeX = info.GetDouble("SizeX"); s._sizeY = info.GetDouble("SizeY"); s._sizeZ = info.GetDouble("SizeZ"); return(s); }
/// <summary> /// Creates a new rectangle that includes all the provided points. /// </summary> /// <param name="points">The points that the rectangle should include.</param> /// <returns>The rectangle that includes all the provided points.</returns> /// <exception cref="System.ArgumentException">Enumeration is empty!</exception> public static RectangleD3D NewRectangleIncludingAllPoints(IEnumerable <PointD3D> points) { var en = points.GetEnumerator(); if (!en.MoveNext()) { throw new ArgumentException("Enumeration is empty!", nameof(points)); } var result = new RectangleD3D(en.Current, VectorD3D.Empty); while (en.MoveNext()) { result.ExpandToInclude(en.Current); } return(result); }
/// <summary> /// Determines whether the grip is hit by the current mouse position. /// </summary> /// <param name="mousePosition">The mouse position (hit ray).</param> /// <returns></returns> public bool IsGripHit(HitTestPointData mousePosition) { var vec = new VectorD3D(_gripRadius, _gripRadius, _gripRadius); var rect = new RectangleD3D(_gripCenter - vec, 2 * vec); double z; return mousePosition.IsHit(rect, out z); }
/// <summary> /// Gets the rectangle for the background to draw. Used both by <see cref="Measure"/> as well as <see cref="Draw"/>. /// </summary> /// <param name="itemRectangle">The item rectangle.</param> /// <returns></returns> private RectangleD3D GetRectangleToDraw(RectangleD3D itemRectangle) { return new RectangleD3D( itemRectangle.X - _padding.Left, itemRectangle.Y - _padding.Bottom, itemRectangle.Z - _resultingDistance - itemRectangle.SizeZ, itemRectangle.SizeX + _padding.Left + _padding.Right, itemRectangle.SizeY + _padding.Top + _padding.Bottom, _resultingThickness ); }
internal LineShapeObjectOutline(Matrix4x3 transformation, RectangleD3D bounds) { _transformation = transformation; _bounds = bounds; }
protected override IGripManipulationHandle[] GetGrips(IHitTestObject hitTest, GripKind gripKind) { var list = new List<IGripManipulationHandle>(); /* const double gripNominalSize = 10; // 10 Points nominal size on the screen if ((GripKind.Resize & gripKind) != 0) { double gripSize = gripNominalSize / pageScale; // 10 Points, but we have to consider the current pageScale for (int i = 1; i < _gripRelPositions.Length; i++) { PointD2D outVec, pos; if (1 == i % 2) GetCornerOutVector(_gripRelPositions[i], hitTest, out outVec, out pos); else GetMiddleRayOutVector(_gripRelPositions[i], hitTest, out outVec, out pos); outVec *= (gripSize / outVec.VectorLength); PointD2D altVec = outVec.Get90DegreeRotated(); PointD2D ptStart = pos; list.Add(new ResizeGripHandle(hitTest, _gripRelPositions[i], new MatrixD2D(outVec.X, outVec.Y, altVec.X, altVec.Y, ptStart.X, ptStart.Y))); } } */ /* if ((GripKind.Rotate & gripKind) != 0) { double gripSize = 10 / pageScale; // Rotation grips for (int i = 1; i < _gripRelPositions.Length; i += 2) { PointD2D outVec, pos; GetCornerOutVector(_gripRelPositions[i], hitTest, out outVec, out pos); outVec *= (gripSize / outVec.VectorLength); PointD2D altVec = outVec.Get90DegreeRotated(); PointD2D ptStart = pos; list.Add(new RotationGripHandle(hitTest, _gripRelPositions[i], new MatrixD2D(outVec.X, outVec.Y, altVec.X, altVec.Y, ptStart.X, ptStart.Y))); } } */ /* if ((GripKind.Rescale & gripKind) != 0) { double gripSize = 10 / pageScale; // 10 Points, but we have to consider the current pageScale for (int i = 1; i < _gripRelPositions.Length; i++) { PointD2D outVec, pos; if (1 == i % 2) GetCornerOutVector(_gripRelPositions[i], hitTest, out outVec, out pos); else GetMiddleRayOutVector(_gripRelPositions[i], hitTest, out outVec, out pos); outVec *= (gripSize / outVec.VectorLength); PointD2D altVec = outVec.Get90DegreeRotated(); PointD2D ptStart = pos; list.Add(new RescaleGripHandle(hitTest, _gripRelPositions[i], new MatrixD2D(outVec.X, outVec.Y, altVec.X, altVec.Y, ptStart.X, ptStart.Y))); } } */ /* if ((GripKind.Shear & gripKind) != 0) { double gripSize = 10 / pageScale; // 10 Points, but we have to consider the current pageScale for (int i = 2; i < _gripRelPositions.Length; i += 2) { PointD2D outVec, pos; GetEdgeOutVector(_gripRelPositions[i], hitTest, out outVec, out pos); outVec *= (gripSize / outVec.VectorLength); PointD2D altVec = outVec.Get90DegreeRotated(); PointD2D ptStart = pos; list.Add(new ShearGripHandle(hitTest, _gripRelPositions[i], new MatrixD2D(outVec.X, outVec.Y, altVec.X, altVec.Y, ptStart.X, ptStart.Y))); } } */ if ((GripKind.Move & gripKind) != 0) { var bounds = this.Bounds; var wn = PolylineMath3D.GetWestNorthVectors(bounds.Size); var transformation = Matrix4x3.NewFromBasisVectorsAndLocation(wn.Item1, wn.Item2, bounds.Size.Normalized, PointD3D.Empty); transformation.AppendTransform(_transformation); transformation.AppendTransform(hitTest.Transformation); double t1 = 0.55 * _linePen.Thickness1; double t2 = 0.55 * _linePen.Thickness2; var rect = new RectangleD3D(-t1, -t2, 0, 2 * t1, 2 * t2, bounds.Size.Length); var objectOutline = new RectangularObjectOutline(rect, transformation); list.Add(new MovementGripHandle(hitTest, objectOutline, null)); } return list.ToArray(); }
protected void MeasureBackground(IGraphicsContext3D g, double itemSizeX, double itemSizeY, double itemSizeZ) { var fontInfo = FontManager3D.Instance.GetFontInformation(_font); double widthOfOne_n = Glyph.MeasureString("n", _font).X; double widthOfThree_M = Glyph.MeasureString("MMM", _font).X; if (this._background != null) { _cachedTextPadding = new Margin2D( 0.25 * widthOfOne_n, fontInfo.cyDescent, 0.25 * widthOfOne_n, fontInfo.cyDescent ); } else { _cachedTextPadding = new Margin2D(0, 0, 0, 0); } var paddedTextSize = new VectorD3D((itemSizeX + _cachedTextPadding.Left + _cachedTextPadding.Right), (itemSizeY + _cachedTextPadding.Bottom + _cachedTextPadding.Top), itemSizeZ); var textRectangle = new RectangleD3D(PointD3D.Empty, paddedTextSize); // the origin of the padded text rectangle is always 0 if (this._background != null) { var backgroundRect = this._background.Measure(textRectangle); _cachedExtendedTextBounds = backgroundRect.WithRectangleIncluded(textRectangle); // _cachedExtendedTextBounds.WithOffset(textRectangle.X - backgroundRect.X, textRectangle.Y - backgroundRect.Y, 0); ((ItemLocationDirectAutoSize)_location).SetSizeInAutoSizeMode(_cachedExtendedTextBounds.Size, false); this._cachedTextOffset = new PointD3D(textRectangle.X - backgroundRect.X + _cachedTextPadding.Left, textRectangle.Y - backgroundRect.Y + _cachedTextPadding.Bottom, 0); } else { ((ItemLocationDirectAutoSize)_location).SetSizeInAutoSizeMode(paddedTextSize, false); _cachedExtendedTextBounds = textRectangle; _cachedTextOffset = PointD3D.Empty; } }
public void Paint(IGraphicsContext3D g, IPlotArea layer, CSPlaneID plane, int axisnumber) { if (!_showGrid) return; Scale axis = layer.Scales[axisnumber]; TickSpacing ticking = layer.Scales[axisnumber].TickSpacing; RectangleD3D layerRect = new RectangleD3D(PointD3D.Empty, layer.Size); if (_showZeroOnly) { Altaxo.Data.AltaxoVariant var = new Altaxo.Data.AltaxoVariant(0.0); double rel = axis.PhysicalVariantToNormal(var); //_majorPen.SetEnvironment(layerRect, BrushX.GetEffectiveMaximumResolution(g, 1)); if (rel >= 0 && rel <= 1) { Logical3D logV = new Logical3D(); logV.SetR(plane.PerpendicularAxisNumber, plane.LogicalValue); logV.SetR(axisnumber, rel); var thirdAxisNumber = Logical3D.GetPerpendicularAxisNumber(plane.PerpendicularAxisNumber, axisnumber); var line = layer.CoordinateSystem.GetIsoline(logV.WithR(thirdAxisNumber, 0), logV.WithR(thirdAxisNumber, 1)); g.DrawLine(MajorPen, line); } } else { double[] ticks; if (_showMinor) { //_minorPen.SetEnvironment(layerRect, BrushX.GetEffectiveMaximumResolution(g, 1)); ticks = ticking.GetMinorTicksNormal(axis); for (int i = 0; i < ticks.Length; ++i) { Logical3D logV = new Logical3D(); logV.SetR(plane.PerpendicularAxisNumber, plane.LogicalValue); logV.SetR(axisnumber, ticks[i]); var thirdAxisNumber = Logical3D.GetPerpendicularAxisNumber(plane.PerpendicularAxisNumber, axisnumber); var line = layer.CoordinateSystem.GetIsoline(logV.WithR(thirdAxisNumber, 0), logV.WithR(thirdAxisNumber, 1)); g.DrawLine(MinorPen, line); } } //MajorPen.SetEnvironment(layerRect, BrushX.GetEffectiveMaximumResolution(g, 1)); ticks = ticking.GetMajorTicksNormal(axis); for (int i = 0; i < ticks.Length; ++i) { Logical3D logV = new Logical3D(); logV.SetR(plane.PerpendicularAxisNumber, plane.LogicalValue); logV.SetR(axisnumber, ticks[i]); var thirdAxisNumber = Logical3D.GetPerpendicularAxisNumber(plane.PerpendicularAxisNumber, axisnumber); var line = layer.CoordinateSystem.GetIsoline(logV.WithR(thirdAxisNumber, 0), logV.WithR(thirdAxisNumber, 1)); g.DrawLine(MajorPen, line); } } }
public RectangularObjectOutline(RectangleD3D rectangle, Matrix4x3 transformation) { _rectangle = rectangle; _transformation = transformation; }
/// <summary> /// Gets the principal coordinate system that results of the camera facing a layer. The plane of the layer that best faced the camera is used for the calculations. /// The normal of that layer is returned as z-axis, the vector that best matches the up-vector of the camera is becoming the y-axis, /// and the x-axis results from the z-axis and the y-axis. /// </summary> /// <param name="camera">The camera.</param> /// <param name="activeLayer">The active layer of the graph document.</param> /// <param name="transformation">Matrix that contains the principal axes as described above. The axes coordinates are in the coordinates of the layer provided in the argument <paramref name="activeLayer"/>.</param> /// <exception cref="InvalidProgramException">There should always be a plane of a rectangle that can be hit!</exception> public static void GetCoordinateSystemBasedOnLayerPlaneFacingTheCamera(CameraBase camera, HostLayer activeLayer, out Matrix3x3 transformation) { PointD3D hitposition = new PointD3D(0.5, 0.5, 1); // this hit position is arbitrary, every other position should work similarly var activeLayerTransformation = activeLayer.TransformationFromRootToHere(); 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 xaxis = xaxis.Normalized; yaxis = yaxis.Normalized; zaxis = zaxis.Normalized; transformation = new Matrix3x3( xaxis.X, yaxis.X, zaxis.X, xaxis.Y, yaxis.Y, zaxis.Y, xaxis.Z, yaxis.Z, zaxis.Z ); }
/// <summary> /// Make the views to look at the root layer center. The scale is choosen so that the size of the plot will be maximal. /// </summary> /// <param name="toEyeVector">The To-Eye vector (vector from the target to the camera position).</param> /// <param name="cameraUpVector">The camera up vector.</param> /// <exception cref="System.NotImplementedException"></exception> public void ViewToRootLayerCenter(VectorD3D toEyeVector, VectorD3D cameraUpVector) { double aspectRatio = 1; if (null != _view) { var viewportSize = _view.ViewportSizeInPoints; aspectRatio = viewportSize.Y / viewportSize.X; } if ((VectorD3D.CrossProduct(toEyeVector, cameraUpVector).IsEmpty)) throw new ArgumentOutOfRangeException(nameof(cameraUpVector) + " is either empty or is parallel to the eyeVector"); var upVector = Math3D.GetNormalizedVectorOrthogonalToVector(cameraUpVector, toEyeVector); var targetPosition = (PointD3D)(0.5 * Doc.RootLayer.Size); var cameraDistance = 10 * Doc.RootLayer.Size.Length; var eyePosition = cameraDistance * toEyeVector.Normalized + targetPosition; var newCamera = Doc.Camera.WithUpEyeTarget(upVector, eyePosition, targetPosition); if (newCamera is OrthographicCamera) { var orthoCamera = (OrthographicCamera)newCamera.WithWidthAtZNear(1); var mx = orthoCamera.GetViewProjectionMatrix(aspectRatio); // to get the resulting scale, we transform all vertices of the root layer (the destination range would be -1..1, but now is not in range -1..1) // then we search for the maximum of the absulute value of x and y. This is our scale. double absmax = 0; foreach (var p in new RectangleD3D(Doc.RootLayer.Position, Doc.RootLayer.Size).Vertices) { var ps = mx.Transform(p); absmax = Math.Max(absmax, Math.Abs(ps.X)); absmax = Math.Max(absmax, Math.Abs(ps.Y)); } newCamera = orthoCamera.WithWidthAtZNear(absmax); } else if (newCamera is PerspectiveCamera) { var perspCamera = (PerspectiveCamera)newCamera; // use worst case var diameter = Doc.RootLayer.Size.Length; // diagonal of the root layer = radius of sphere in the worst case double minWidthHeight = Math.Min(perspCamera.WidthAtZNear, perspCamera.WidthAtZNear * aspectRatio); var distanceWorstCase = diameter * perspCamera.ZNear / minWidthHeight; if (distanceWorstCase > perspCamera.Distance) perspCamera = (PerspectiveCamera)perspCamera.WithDistanceByChangingEyePosition(distanceWorstCase); var rootRect = new RectangleD3D(PointD3D.Empty, Doc.RootLayer.Size); for (int i = 0; i < 5; ++i) // 5 iterations to get the right distance { var viewProj = perspCamera.GetViewProjectionMatrix(aspectRatio); var screenRect = RectangleD2D.NewRectangleIncludingAllPoints(rootRect.Vertices.Select(v => viewProj.Transform(v).PointD2DWithoutZ)); double maxScreen = 0; foreach (var v in screenRect.Vertices) { maxScreen = Math.Max(maxScreen, Math.Abs(v.X)); maxScreen = Math.Max(maxScreen, Math.Abs(v.Y)); } // now we could decrease the camera distance by maxScreen, but this is only an estimation perspCamera = (PerspectiveCamera)perspCamera.WithDistanceByChangingEyePosition(perspCamera.Distance * maxScreen); } newCamera = perspCamera; } else { throw new NotImplementedException("Unknown camera type"); } Doc.Camera = AdjustZNearZFar(newCamera); }
public void DrawRootLayerMarkers(IOverlayContext3D gc) { var buf = gc.PositionColorIndexedTriangleBuffers; VectorD3D size = Doc.RootLayer.Size; double markerLen = Math.Max(size.X, Math.Max(size.Y, size.Z)) / 20.0; double markerLenBy2 = markerLen / 2; double markerThicknessBy2 = markerLenBy2 / 5.0; RectangleD3D rect = new RectangleD3D(PointD3D.Empty, size); if (_cachedResultingRootLayerMarkersVisibility.HasFlag(RootLayerMarkersVisibility.Arrows)) { foreach (var pos in rect.Vertices) { var posX = pos.WithXPlus(pos.X == 0 ? markerLenBy2 : -markerLenBy2); DrawMarkerX(buf, posX, markerLenBy2, markerThicknessBy2); var posY = pos.WithYPlus(pos.Y == 0 ? markerLenBy2 : -markerLenBy2); DrawMarkerY(buf, posY, markerLenBy2, markerThicknessBy2); var posZ = pos.WithZPlus(pos.Z == 0 ? markerLenBy2 : -markerLenBy2); DrawMarkerZ(buf, posZ, markerLenBy2, markerThicknessBy2); } } if (_cachedResultingRootLayerMarkersVisibility.HasFlag(RootLayerMarkersVisibility.Lines)) { var lineBuffer = gc.PositionColorLineListBuffer; foreach (var line in rect.Edges) { lineBuffer.AddLine(line.P0.X, line.P0.Y, line.P0.Z, line.P1.X, line.P1.Y, line.P1.Z, 1, 0, 0, 1); } } }
/// <summary> /// Draws the specified background /// </summary> /// <param name="g">The drawing context.</param> /// <param name="itemRectangle">Position and size of the item for which this background is intended. For text, this is the position and size of the text rectangle, already with a margin around. /// This parameter should have the same size as was used in the previous call to <see cref="Measure(RectangleD3D)" /></param> /// <param name="material">The material to use for this background.</param> /// <exception cref="NotImplementedException"></exception> public void Draw(IGraphicsContext3D g, RectangleD3D itemRectangle, IMaterial material) { var rectangleToDraw = GetRectangleToDraw(itemRectangle); var buffers = g.GetPositionNormalIndexedTriangleBuffer(material); if (null != buffers.PositionNormalColorIndexedTriangleBuffer) { var c = material.Color.Color; var voffs = buffers.PositionNormalColorIndexedTriangleBuffer.VertexCount; Altaxo.Drawing.D3D.SolidCube.Add( rectangleToDraw.X, rectangleToDraw.Y, rectangleToDraw.Z, rectangleToDraw.SizeX, rectangleToDraw.SizeY, rectangleToDraw.SizeZ, (point, normal) => buffers.PositionNormalColorIndexedTriangleBuffer.AddTriangleVertex(point.X, point.Y, point.Z, normal.X, normal.Y, normal.Z, c.ScR, c.ScG, c.ScB, c.ScA), (i1, i2, i3) => buffers.IndexedTriangleBuffer.AddTriangleIndices(i1 + voffs, i2 + voffs, i3 + voffs), ref voffs); } else if (null != buffers.PositionNormalIndexedTriangleBuffer) { var voffs = buffers.PositionNormalIndexedTriangleBuffer.VertexCount; Altaxo.Drawing.D3D.SolidCube.Add( rectangleToDraw.X, rectangleToDraw.Y, rectangleToDraw.Z, rectangleToDraw.SizeX, rectangleToDraw.SizeY, rectangleToDraw.SizeZ, (point, normal) => buffers.PositionNormalIndexedTriangleBuffer.AddTriangleVertex(point.X, point.Y, point.Z, normal.X, normal.Y, normal.Z), (i1, i2, i3) => buffers.IndexedTriangleBuffer.AddTriangleIndices(i1 + voffs, i2 + voffs, i3 + voffs), ref voffs); } else { throw new NotImplementedException(); } }
/// <summary> /// Draws the specified background /// </summary> /// <param name="g">The drawing context.</param> /// <param name="itemRectangle">Position and size of the item for which this background is intended. For text, this is the position and size of the text rectangle, already with a margin around. /// This parameter should have the same size as was used in the previous call to <see cref="Measure(RectangleD3D)" /></param> /// <exception cref="NotImplementedException"></exception> public void Draw(IGraphicsContext3D g, RectangleD3D itemRectangle) { Draw(g, itemRectangle, _material); }
/// <summary> /// Saves the project item as image to the provided stream. /// </summary> /// <param name="item">The item to export, for instance an item of type <see cref="Altaxo.Graph.Gdi.GraphDocument"/> or <see cref="Altaxo.Graph.Graph3D.GraphDocument"/>.</param> /// <param name="options">The export options.</param> /// <param name="toStream">The stream to save the image to.</param> public void ExportAsImageToStream(Altaxo.Main.IProjectItem item, Altaxo.Graph.Gdi.GraphExportOptions options, System.IO.Stream toStream) { if (item == null) throw new ArgumentNullException(nameof(item)); if (!(item is Altaxo.Graph.Graph3D.GraphDocument)) throw new ArgumentException(string.Format("Expected item of type {0}, but it is of type {1}", typeof(Altaxo.Graph.Graph3D.GraphDocument), item.GetType())); var doc = (Altaxo.Graph.Graph3D.GraphDocument)item; double sourceDpi = options.SourceDpiResolution; var exporter = new D3D10BitmapExporter(); var scene = new Viewing.D3D10Scene(); var g = new D3D10GraphicsContext(); doc.Paint(g); var matrix = doc.Camera.LookAtRHMatrix; var rect = new RectangleD3D(PointD3D.Empty, doc.RootLayer.Size); var bounds = RectangleD3D.NewRectangleIncludingAllPoints(rect.Vertices.Select(x => matrix.Transform(x))); int pixelsX = (int)Math.Ceiling(sourceDpi * bounds.SizeX / 72.0); pixelsX = (int)(4 * Math.Ceiling((pixelsX + 3) / 4.0)); int pixelsY = (int)(sourceDpi * bounds.SizeY / 72.0); double aspectRatio = pixelsY / (double)pixelsX; var sceneCamera = doc.Camera; if (sceneCamera is OrthographicCamera) { var orthoCamera = (OrthographicCamera)sceneCamera; orthoCamera = (OrthographicCamera)orthoCamera.WithWidthAtZNear(bounds.SizeX); double offsX = -(1 + 2 * bounds.X / bounds.SizeX); double offsY = -(1 + 2 * bounds.Y / bounds.SizeY); sceneCamera = orthoCamera.WithScreenOffset(new PointD2D(offsX, offsY)); } else if (sceneCamera is PerspectiveCamera) { var viewProj = sceneCamera.GetViewProjectionMatrix(1); // here we transform the points with AspectRatio=1, in order to get the AspectRatio of the ScreenBounds var screenBounds = RectangleD3D.NewRectangleIncludingAllPoints(rect.Vertices.Select(x => viewProj.Transform(x))); aspectRatio = screenBounds.SizeY / screenBounds.SizeX; // this is the aspectRatio of our image viewProj = sceneCamera.GetViewProjectionMatrix(aspectRatio); // now we get the transform with our aspectRatio determined above screenBounds = RectangleD3D.NewRectangleIncludingAllPoints(rect.Vertices.Select(x => viewProj.Transform(x))); // this are our actual screenBounds, of course in relative screen coordinates, thus the ratio of sizeX and sizeY should now be 1 double scaleFactor = 2 / screenBounds.SizeX; // since SizeX and SizeY should now be the same, we could have used SizeY alternatively double offsX = -(1 + scaleFactor * screenBounds.X); double offsY = -(1 + scaleFactor * screenBounds.Y); pixelsY = (int)(4 * Math.Ceiling((aspectRatio * pixelsX + 3) / 4.0)); // now calculate the size of the image in y direction from the aspectRatio var perspCamera = (PerspectiveCamera)sceneCamera; sceneCamera = perspCamera.WithWidthAtZNear(perspCamera.WidthAtZNear / scaleFactor); sceneCamera = sceneCamera.WithScreenOffset(new PointD2D(offsX, offsY)); } else { throw new NotImplementedException(); } scene.SetCamera(sceneCamera); scene.SetLighting(doc.Lighting); scene.SetDrawing(g); exporter.Export(pixelsX, pixelsY, scene, options, toStream); }
/// <summary> /// Creates a new rectangle that includes all the provided points. /// </summary> /// <param name="points">The points that the rectangle should include.</param> /// <returns>The rectangle that includes all the provided points.</returns> /// <exception cref="System.ArgumentException">Enumeration is empty!</exception> public static RectangleD3D NewRectangleIncludingAllPoints(IEnumerable<PointD3D> points) { var en = points.GetEnumerator(); if (!en.MoveNext()) throw new ArgumentException("Enumeration is empty!", nameof(points)); var result = new RectangleD3D(en.Current, VectorD3D.Empty); while (en.MoveNext()) { result.ExpandToInclude(en.Current); } return result; }
/// <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)); }
/// <summary> /// Returns a rectangle that is based on the current rectangle, but was expanded to include all vertex points of the provided rectangle <paramref name="r"/>. /// </summary> /// <param name="r">The rectangle, whose vertices have to be included.</param> /// <returns>A rectangle that is based on the current rectangle, but was expanded to include all vertex points of the provided rectangle <paramref name="r"/>.</returns> public RectangleD3D WithRectangleIncluded(RectangleD3D r) { return WithPointsIncluded(r.Vertices); }
public RectangleTransformedD3D(RectangleD3D rectangle, Matrix4x3 transformation) { _rectangle = rectangle; _transformation = transformation; }
/// <summary> /// Determines whether the specified 3D-rectangle r is hit by a ray given by x=0, y=0, z>0. /// </summary> /// <param name="r">The rectangle r.</param> /// <param name="z">If there was a hit, this is the z coordinate of the hit.</param> /// <returns>True if the rectangle is hit by a ray given by x=0, y=0, z>0.</returns> public bool IsHit(RectangleD3D r, out double z) { return IsRectangleHitByRay(r, _hitTransformation, out z); }
/// <summary> /// Returns a rectangle that is based on the current rectangle, but was expanded to include all vertex points of the provided rectangle <paramref name="r"/>. /// </summary> /// <param name="r">The rectangle, whose vertices have to be included.</param> /// <returns>A rectangle that is based on the current rectangle, but was expanded to include all vertex points of the provided rectangle <paramref name="r"/>.</returns> public RectangleD3D WithRectangleIncluded(RectangleD3D r) { return(WithPointsIncluded(r.Vertices)); }
/// <summary> /// Determines whether the specified 3D-rectangle r is hit by a ray given by x=0, y=0, z>0. /// </summary> /// <param name="r">The rectangle r.</param> /// <param name="rectangleToWorldTransformation">An additional transformation that transformes the given rectangle into the same coordinates as the hit data.</param> /// <param name="z">If there was a hit, this is the z coordinate of the hit.</param> /// <returns>True if the rectangle is hit by a ray given by x=0, y=0, z>0.</returns> public bool IsHit(RectangleD3D r, Matrix4x3 rectangleToWorldTransformation, out double z) { return IsRectangleHitByRay(r, _hitTransformation.WithPrependedTransformation(rectangleToWorldTransformation), out z); }
/// <summary> /// Gets the absolute enclosing rectangle, taking into account ScaleX, ScaleY, Rotation and Shear (SSRS). /// </summary> /// <returns>The enclosing rectangle in absolute values.</returns> public RectangleD3D GetAbsoluteEnclosingRectangle() { Matrix4x3 m = Matrix4x3.NewScalingShearingRotationDegreesTranslation( ScaleX, ScaleY, ScaleZ, ShearX, ShearY, ShearZ, RotationX, RotationY, RotationZ, AbsolutePivotPositionX, AbsolutePivotPositionY, AbsolutePivotPositionZ); m.TranslatePrepend(AbsoluteVectorPivotToLeftUpper.X, AbsoluteVectorPivotToLeftUpper.Y, AbsoluteVectorPivotToLeftUpper.Z); var r = new RectangleD3D(PointD3D.Empty, this.AbsoluteSize); return RectangleD3D.NewRectangleIncludingAllPoints(r.Vertices.Select(p => m.Transform(p))); }
/// <summary> /// Determines whether the specified 3D-rectangle r is hit by a ray given by the provided transformation matrix that would transform /// the hit ray in a ray at x=0, y=0, and z=-Infinity .. +Infinity. /// </summary> /// <param name="r">The rectangle r.</param> /// <param name="rayTransformation">The hit ray transformation.</param> /// <param name="z">If there was a hit, this is the z coordinate of the hit (otherwise, NaN is returned).</param> /// <returns>True if the rectangle is hit by a ray given by the provided hit ray matrix.</returns> public static bool IsRectangleHitByRay(RectangleD3D r, Matrix4x4 rayTransformation, out double z) { PointD3D[] vertices = new PointD3D[8]; int i = 0; foreach (var v in r.Vertices) vertices[i++] = rayTransformation.Transform(v); foreach (var ti in r.TriangleIndices) { if (HitTestWithAlreadyTransformedPoints(vertices[ti.Item1], vertices[ti.Item2], vertices[ti.Item3], out z) && z >= 0) return true; } z = double.NaN; return false; }
public override IGripManipulationHandle[] GetGrips(int gripLevel) { if (gripLevel <= 1) { LineShape ls = (LineShape)_hitobject; PointD3D[] pts = new PointD3D[] { PointD3D.Empty, (PointD3D)ls.Size }; for (int i = 0; i < pts.Length; i++) { var pt = ls._transformation.Transform(pts[i]); pt = this.Transformation.Transform(pt); pts[i] = pt; } IGripManipulationHandle[] grips = new IGripManipulationHandle[gripLevel == 0 ? 1 : 3]; // Translation grips var bounds = ls.Bounds; var wn = PolylineMath3D.GetWestNorthVectors(bounds.Size); var transformation = Matrix4x3.NewFromBasisVectorsAndLocation(wn.Item1, wn.Item2, bounds.Size.Normalized, PointD3D.Empty); transformation.AppendTransform(ls._transformation); transformation.AppendTransform(this.Transformation); double t1 = 0.55 * ls._linePen.Thickness1; double t2 = 0.55 * ls._linePen.Thickness2; var rect = new RectangleD3D(-t1, -t2, 0, 2 * t1, 2 * t2, bounds.Size.Length); var objectOutline = new RectangularObjectOutline(rect, transformation); grips[0] = new MovementGripHandle(this, objectOutline, null); // PathNode grips if (gripLevel == 1) { grips[2] = grips[0]; // put the movement grip to the background, the two NodeGrips need more priority var gripRadius = Math.Max(t1, t2); grips[0] = new PathNodeGripHandle(this, new VectorD3D(0, 0, 0), pts[0], gripRadius); grips[1] = new PathNodeGripHandle(this, new VectorD3D(1, 1, 1), pts[1], gripRadius); } return grips; } else { return base.GetGrips(gripLevel); } }
/// <summary>Draws the grip in the graphics context.</summary> /// <param name="g">Graphics context.</param> public void Show(IOverlayContext3D g) { var buf = g.PositionColorLineListBuffer; var vec = new VectorD3D(_gripRadius, _gripRadius, _gripRadius); var rect = new RectangleD3D(_gripCenter - vec, 2 * vec); foreach (var line in rect.Edges) buf.AddLine(line.P0.X, line.P0.Y, line.P0.Z, line.P1.X, line.P1.Y, line.P1.Z, 1, 0, 0, 1); }
/// <summary> /// Measures the background size and position. /// </summary> /// <param name="itemRectangle">Position and size of the item for which this background is intended. For text, this is the position and size of the text rectangle, already with a margin around.</param> /// <returns> /// The position and size of the rectangle that fully includes the background (but not the item). /// </returns> public RectangleD3D Measure(RectangleD3D itemRectangle) { _resultingDistance = _customDistance.HasValue ? _customDistance.Value : itemRectangle.SizeZ / 2; _resultingThickness = _customThickness.HasValue ? _customThickness.Value : itemRectangle.SizeZ; return GetRectangleToDraw(itemRectangle); }