/// <summary> /// Subclasses should override this method to get notified as the handle is dragged. /// </summary> /// <param name="sender">The source of this handle drag event.</param> /// <param name="size">The drag delta relative to the handle.</param> /// <param name="e">A PInputEventArgs that contains the event data.</param> public virtual void OnHandleDrag(object sender, SizeFx size, PInputEventArgs e) { if (HandleDrag != null) { HandleDrag(sender, size, e); } }
public void DragHandleTwoHandler(object sender, SizeFx localDelta, PInputEventArgs e) { SizeFx parentDelta = LocalToParent(localDelta); PointTwo = new PointFx(PointTwo.X + parentDelta.Width, PointTwo.Y + parentDelta.Height); ((PHandle)sender).RelocateHandle(); }
/// <summary> /// Animates the camera's view to keep the focus node on the screen and at 100 /// percent scale with minimal view movement. /// </summary> /// <param name="aCamera">The camera whose view will be animated.</param> /// <param name="aFocusNode">The focus node to animate to.</param> /// <param name="duration">The length of the animation.</param> /// <returns> /// The activity that animates the camera's view to the focus node. /// </returns> public virtual PActivity DirectCameraViewToFocus(PCamera aCamera, PNode aFocusNode, int duration) { Matrix originalViewMatrix = aCamera.ViewMatrix; // Scale the canvas to include SizeFx s = new SizeFx(1, 0); s = focusNode.GlobalToLocal(s); float scaleFactor = s.Width / aCamera.ViewScale; PointFx scalePoint = PUtil.CenterOfRectangle(focusNode.GlobalFullBounds); if (scaleFactor != 1) { aCamera.ScaleViewBy(scaleFactor, scalePoint.X, scalePoint.Y); } // Pan the canvas to include the view bounds with minimal canvas // movement. aCamera.AnimateViewToPanToBounds(focusNode.GlobalFullBounds, 0); // Get rid of any white space. The canvas may be panned and // zoomed in to do this. But make sure not stay constrained by max // magnification. //FillViewWhiteSpace(aCamera); Matrix resultingMatrix = aCamera.ViewMatrix; aCamera.ViewMatrix = originalViewMatrix; // Animate the canvas so that it ends up with the given // view transform. return(AnimateCameraViewMatrixTo(aCamera, resultingMatrix, duration)); }
/// <summary> /// Applies a previously set constraint to the camera's view matrix. /// </summary> public virtual void ApplyViewConstraints() { if (viewConstraint == CameraViewConstraint.None) { return; } RectangleFx layerBounds = GlobalToLocal(UnionOfLayerFullBounds); SizeFx constraintDelta = new SizeFx(0, 0); switch (viewConstraint) { case CameraViewConstraint.All: constraintDelta = PUtil.DeltaRequiredToContain(ViewBounds, layerBounds); break; case CameraViewConstraint.Center: PointFx layerLocation = PUtil.CenterOfRectangle(layerBounds); //layerBounds.Width = 0; //layerBounds.Height = 0; layerBounds = new RectangleFx(layerLocation.X, layerLocation.Y, 0, 0); constraintDelta = PUtil.DeltaRequiredToContain(ViewBounds, layerBounds); break; } this.viewMatrix = MatrixExtensions.TranslateBy(viewMatrix, -constraintDelta.Width, -constraintDelta.Height); }
/// <summary> /// Override this method to change the way bounds are computed. For example /// this is where you can control how lines are wrapped. /// </summary> public virtual void RecomputeBounds() { if (text != null && (ConstrainWidthToTextWidth || ConstrainHeightToTextHeight)) { float textWidth; float textHeight; if (ConstrainWidthToTextWidth) { textWidth = Font.MeasureString(Text).X; textHeight = Font.MeasureString(Text).Y; } else { textWidth = Width; SizeFx layoutSize = new SizeFx(textWidth, float.PositiveInfinity); //textHeight = GRAPHICS.MeasureString(Text, Font, layoutSize.ToSizeF(), stringFormat).Height; textHeight = Font.MeasureString(Text).Y; } float newWidth = Width; float newHeight = Height; if (ConstrainWidthToTextWidth) { newWidth = textWidth; } if (ConstrainHeightToTextHeight) { newHeight = textHeight; } base.SetBounds(X, Y, newWidth, newHeight); } }
/// <summary> /// Overridden. See <see cref="PDragSequenceEventHandler.OnDrag"> /// PDragSequenceEventHandler.OnDrag</see>. /// </summary> protected override void OnDrag(object sender, PInputEventArgs e) { base.OnDrag(sender, e); SizeFx s = e.GetDeltaRelativeTo(draggedNode); s = draggedNode.LocalToParent(s); draggedNode.OffsetBy(s.Width, s.Height); }
protected void aNode_MouseDrag(object sender, PInputEventArgs e) { PNode aNode = (PNode)sender; SizeFx delta = e.GetDeltaRelativeTo(aNode); aNode.TranslateBy(delta.Width, delta.Height); PrintEventCoords(e); e.Handled = true; }
/// <summary> /// Overridden. See <see cref="PDragSequenceEventHandler.OnDrag"> /// PDragSequenceEventHandler.OnDrag</see>. /// </summary> protected override void OnDrag(object sender, PInputEventArgs e) { base.OnDrag(sender, e); SizeFx aDelta = e.GetDeltaRelativeTo(handle); if (aDelta.Width != 0 || aDelta.Height != 0) { handle.OnHandleDrag(sender, aDelta, e); } }
/// <summary> /// Pans the camera as the mouse is dragged. /// </summary> /// <param name="e">A PInputEventArgs that contains the event data.</param> protected virtual void Pan(PInputEventArgs e) { PCamera c = e.Camera; Vector2 l = new Vector2(e.Position.X, e.Position.Y); if (c.ViewBounds.Contains(l)) { SizeFx s = e.Delta; c.TranslateViewBy(s.Width, s.Height); } }
/// <summary> /// Return the delta required to center the rectangle b2 in the rectangle b1. /// </summary> /// <param name="b1">The first rectangle.</param> /// <param name="b2">The second rectangle.</param> /// <returns>The delta required to center b2 in b1.</returns> public static SizeFx DeltaRequiredToCenter(RectangleFx b1, RectangleFx b2) { SizeFx result = SizeFx.Empty; PointFx sc = CenterOfRectangle(b1); PointFx tc = CenterOfRectangle(b2); float xDelta = sc.X - tc.X; float yDelta = sc.Y - tc.Y; result.Width = xDelta; result.Height = yDelta; return(result); }
/// <summary> /// Return the delta required for the rectangle b1 to contain the rectangle b2. /// </summary> /// <param name="b1">The first rectangle.</param> /// <param name="b2">The second rectangle.</param> /// <returns>The delta required for b1 to contain b2.</returns> public static SizeFx DeltaRequiredToContain(RectangleFx b1, RectangleFx b2) { SizeFx result = SizeFx.Empty; if (!b1.Contains(b2)) { float b1MaxX = Math.Max(b1.X, b1.Right); float b1MinX = Math.Min(b1.X, b1.Right); float b1MaxY = Math.Max(b1.Y, b1.Bottom); float b1MinY = Math.Min(b1.Y, b1.Bottom); float b2MaxX = Math.Max(b2.X, b2.Right); float b2MinX = Math.Min(b2.X, b2.Right); float b2MaxY = Math.Max(b2.Y, b2.Bottom); float b2MinY = Math.Min(b2.Y, b2.Bottom); if (!(b2MaxX > b1MaxX && b2MinX < b1MinX)) { if (b2MaxX > b1MaxX || b2MinX < b1MinX) { float difMaxX = b2MaxX - b1MaxX; float difMinX = b2MinX - b1MinX; if (Math.Abs(difMaxX) < Math.Abs(difMinX)) { result.Width = difMaxX; } else { result.Width = difMinX; } } } if (!(b2MaxY > b1MaxY && b2MinY < b1MinY)) { if (b2MaxY > b1MaxY || b2MinY < b1MinY) { float difMaxY = b2MaxY - b1MaxY; float difMinY = b2MinY - b1MinY; if (Math.Abs(difMaxY) < Math.Abs(difMinY)) { result.Height = difMaxY; } else { result.Height = difMinY; } } } } return(result); }
/// <summary> /// Drags the nodes in a standard selection. /// </summary> /// <param name="e">A PInputEventArgs that contains the event data.</param> /// <remarks> /// <b>Notes to Inheritors:</b> Subclasses can override this method to be notified /// while a standard selection is being dragged. /// <para> /// Overriding methods must still call <c>base.DragStandardSelection()</c> for correct /// behavior. /// </para> /// </remarks> protected virtual void DragStandardSelection(PInputEventArgs e) { // There was a press node, so drag selection SizeFx s = e.CanvasDelta; s = e.TopCamera.LocalToView(s); ICollection sel = selection.Keys; foreach (PNode node in sel) { s = node.Parent.GlobalToLocal(s); node.OffsetBy(s.Width, s.Height); } }
/// <summary> /// Animates the camera's view to keep the control node on the screen and at 100 /// percent scale with minimal view movement. /// </summary> /// <param name="aCamera">The camera whose view will be animated.</param> /// <param name="aControlNode">The control node to animate to.</param> /// <param name="path">The pick path through which the control node was picked.</param> /// <param name="duration">The length of the animation.</param> /// <returns> /// The activity that animates the camera's view to the control node. /// </returns> public virtual PActivity DirectCameraViewToControl(PCamera aCamera, PControl aControlNode, PPickPath path, int duration) { Matrix originalViewMatrix = aCamera.ViewMatrix; // Scale the canvas to include SizeFx s = new SizeFx(1, 0); s = aControlNode.GlobalToLocal(s); float scaleFactor = s.Width / aCamera.ViewScale; PointFx scalePoint = PUtil.CenterOfRectangle(aControlNode.GlobalFullBounds); if (scaleFactor != 1) { aCamera.ScaleViewBy(scaleFactor, scalePoint.X, scalePoint.Y); } // Pan the canvas to include the view bounds with minimal canvas // movement. aCamera.AnimateViewToPanToBounds(aControlNode.GlobalFullBounds, 0); // Get rid of any white space. The canvas may be panned and // zoomed in to do this. But make sure not stay constrained by max // magnification. //FillViewWhiteSpace(aCamera); Matrix resultingMatrix = aCamera.ViewMatrix; aCamera.ViewMatrix = originalViewMatrix; PControl controlNode = (PControl)aControlNode; // Animate the canvas so that it ends up with the given // view transform. PActivity animateCameraViewActivity = AnimateCameraViewMatrixTo(aCamera, resultingMatrix, duration); aCamera.Root.WaitForActivities(); Matrix pathTransform = path.GetPathTransformTo(controlNode); PointFx point = new PointFx(controlNode.X, controlNode.Y); PointFx pf = MatrixExtensions.Transform(pathTransform, point); controlNode.ControlLocation = new System.Drawing.Point((int)pf.X, (int)pf.Y); controlNode.CurrentCanvas = path.TopCamera.Canvas; controlNode.Editing = true; return(animateCameraViewActivity); }
/// <summary> /// Caches the information necessary to animate from the current view bounds to the /// specified centerBounds /// </summary> /// <param name="centerBounds">The bounds to center the view on.</param> /// <param name="scaleToFit"> /// Indicates whether the camera should scale it's view when necessary to fully fit /// the given bounds within the camera's view bounds. /// </param> /// <returns>The new view matrix to center the specified bounds.</returns> private Matrix CacheViewBounds(RectangleFx centerBounds, bool scaleToFit) { RectangleFx viewBounds = ViewBounds; // Initialize the image to the union of the current and destination bounds RectangleFx imageBounds = viewBounds; imageBounds = RectangleFxtensions.Union(imageBounds, centerBounds); AnimateViewToCenterBounds(imageBounds, scaleToFit, 0); imageAnimateBounds = ViewBounds; // Now create the actual cache image that we will use to animate fast System.Drawing.Image buffer = PaintBuffer; Color fBrush = Color.White; if (Brush != Color.Transparent) { fBrush = Brush; } ToImage(buffer, fBrush); // Do this after the painting above! imageAnimate = true; // Return the bounds to the previous viewbounds AnimateViewToCenterBounds(viewBounds, scaleToFit, 0); // The code below is just copied from animateViewToCenterBounds to create the // correct transform to center the specified bounds SizeFx delta = PUtil.DeltaRequiredToCenter(viewBounds, centerBounds); Matrix newMatrix = ViewMatrix; newMatrix = MatrixExtensions.TranslateBy(newMatrix, delta.Width, delta.Height); if (scaleToFit) { float s = Math.Min(viewBounds.Width / centerBounds.Width, viewBounds.Height / centerBounds.Height); PointFx center = PUtil.CenterOfRectangle(centerBounds); newMatrix = MatrixExtensions.ScaleBy(newMatrix, s, center.X, center.Y); } return(newMatrix); }
//**************************************************************** // Animation - Methods to animate the camera's view. //**************************************************************** /// <summary> /// Animate the camera's view from its current matrix when the activity starts /// to a new matrix that centers the given bounds in the camera layers' coordinate /// system into the camera's view bounds. /// </summary> /// <param name="centerBounds">The bounds to center the view on.</param> /// <param name="shouldScaleToFit"> /// Indicates whether the camera should scale it's view when necessary to fully fit /// the given bounds within the camera's view bounds. /// </param> /// <param name="duration">The amount of time that the animation should take.</param> /// <returns> /// The newly scheduled activity, if the duration is greater than 0; else null. /// </returns> /// <remarks> /// If the duration is 0 then the view will be transformed immediately, and null will /// be returned. Else a new PTransformActivity will get returned that is set to /// animate the camera’s view matrix to the new bounds. If shouldScaleToFit is true, /// then the camera will also scale its view so that the given bounds fit fully within /// the camera's view bounds, else the camera will maintain its original scale. /// </remarks> public virtual PTransformActivity AnimateViewToCenterBounds(RectangleFx centerBounds, bool shouldScaleToFit, long duration) { SizeFx delta = PUtil.DeltaRequiredToCenter(ViewBounds, centerBounds); Matrix newMatrix = ViewMatrix; newMatrix = MatrixExtensions.TranslateBy(newMatrix, delta.Width, delta.Height); if (shouldScaleToFit) { float s = Math.Min(ViewBounds.Width / centerBounds.Width, ViewBounds.Height / centerBounds.Height); if (s != float.PositiveInfinity && s != 0) { PointFx c = PUtil.CenterOfRectangle(centerBounds); MatrixExtensions.ScaleBy(newMatrix, s, c.X, c.Y); } } return(AnimateViewToMatrix(newMatrix, duration)); }
/// <summary> /// Pan the camera's view from its current matrix when the activity starts to a new /// matrix so that the view bounds will contain (if possible, intersect if not /// possible) the new bounds in the camera layers' coordinate system. /// </summary> /// <param name="panToBounds">The bounds to pan the view to.</param> /// <param name="duration">The amount of time that the animation should take.</param> /// <returns> /// The newly scheduled activity, if the duration is greater than 0; else null. /// </returns> /// <remarks> /// If the duration is 0 then the view will be transformed immediately, and null will /// be returned. Else a new PTransformActivity will get returned that is set to /// animate the camera’s view matrix to the new bounds. /// </remarks> public virtual PTransformActivity AnimateViewToPanToBounds(RectangleFx panToBounds, long duration) { SizeFx delta = PUtil.DeltaRequiredToContain(ViewBounds, panToBounds); if (delta.Width != 0 || delta.Height != 0) { if (duration == 0) { TranslateViewBy(-delta.Width, -delta.Height); } else { Matrix m = ViewMatrix; m = MatrixExtensions.TranslateBy(m, -delta.Width, -delta.Height); return(AnimateViewToMatrix(m, duration)); } } return(null); }
/// <summary> /// Overridden. Do auto-panning even when the mouse is not moving. /// </summary> /// <param name="sender">The source of the drag event.</param> /// <param name="e">A PInputEventArgs that contains the event data.</param> protected override void OnDragActivityStep(object sender, PInputEventArgs e) { base.OnDragActivityStep(sender, e); if (!autopan) { return; } PCamera c = e.Camera; RectangleFx b = c.Bounds; PointFx l = e.GetPositionRelativeTo(c); PUtil.OutCode outcode = PUtil.RectangleOutCode(l, b); SizeFx delta = SizeFx.Empty; if ((outcode & PUtil.OutCode.Top) != 0) { delta.Height = ValidatePanningDelta(-1.0f - (0.5f * Math.Abs(l.Y - b.Y))); } else if ((outcode & PUtil.OutCode.Bottom) != 0) { delta.Height = ValidatePanningDelta(1.0f + (0.5f * Math.Abs(l.Y - (b.Y + b.Height)))); } if ((outcode & PUtil.OutCode.Right) != 0) { delta.Width = ValidatePanningDelta(1.0f + (0.5f * Math.Abs(l.X - (b.X + b.Width)))); } else if ((outcode & PUtil.OutCode.Left) != 0) { delta.Width = ValidatePanningDelta(-1.0f - (0.5f * Math.Abs(l.X - b.X))); } delta = c.LocalToView(delta); if (delta.Width != 0 || delta.Height != 0) { c.TranslateViewBy(delta.Width, delta.Height); } }
/// <summary> /// Gets the size of the view based on the specified camera bounds. /// </summary> /// <param name="bounds">The view bounds for which the view size will be computed.</param> /// <returns>The view size.</returns> public virtual SizeFx GetViewSize(RectangleFx bounds) { SizeFx size = SizeFx.Empty; if (camera != null) { // First we compute the union of all the layers RectangleFx layerBounds = UnionOfLayerFullBounds; // Then we put the bounds into camera coordinates and // union the camera bounds if (!layerBounds.IsEmpty) { layerBounds = camera.ViewToLocal(layerBounds); } layerBounds = RectangleFxtensions.Union(layerBounds, bounds); size = new SizeFx((int)(layerBounds.Width + 0.5), (int)(layerBounds.Height + 0.5)); } return(size); }
/// <summary> /// Transform the size from the camera's local coordinate system to the camera's /// view coordinate system. /// </summary> /// <param name="size"> /// The size in the camera's local coordinate system to be transformed. /// </param> /// <returns>The size in the camera's view coordinate system.</returns> public virtual SizeFx LocalToView(SizeFx size) { return(MatrixExtensions.InverseTransform(viewMatrix, size)); }
public override void OnHandleDrag(object sender, SizeFx size, PInputEventArgs e) { base.OnHandleDrag(sender, size, e); size = LocalToParent(size); Parent.TranslateBy(size.Width, size.Height); }
/// <summary> /// Return the delta between the last and current mouse positions relative to a /// given node on the pick path. /// </summary> /// <param name="nodeOnPath"> /// The returned delta will be in the local coordinate system of this node. /// </param> /// <returns> /// The delta between the last and current mouse positions relative to a given /// node on the pick path. /// </returns> public virtual SizeFx GetDeltaRelativeTo(PNode nodeOnPath) { SizeFx r = CanvasDelta; return(pickPath.CanvasToLocal(r, nodeOnPath)); }
/// <summary> /// Transform the size from the camera's view coordinate system to the camera's /// local coordinate system. /// </summary> /// <param name="size"> /// The size in the camera's view coordinate system to be transformed. /// </param> /// <returns>The size in the camera's local coordinate system.</returns> public virtual SizeFx ViewToLocal(SizeFx size) { return(MatrixExtensions.Transform(viewMatrix, size)); }
protected override void OnDrag(object sender, PInputEventArgs e) { SizeFx dim = e.Delta; bev.viewedCanvas.Camera.TranslateViewBy(0 - dim.Width, 0 - dim.Height); }
/// <summary> /// Override this method to customize the image cache creation process. For /// example if you want to create a shadow effect you would do that here. Fill /// in the cacheOffsetRef if needed to make your image cache line up with the /// nodes children. /// </summary> /// <param name="cacheOffsetRef"> /// Set this value to apply an offset to the image cache. /// </param> /// <returns>The newly created image cache.</returns> public virtual Image CreateImageCache(ref SizeFx cacheOffsetRef) { return(ToImage()); }
/// <summary> /// Convert the given size from canvas coordinates, down the pick path (and through any /// camera view transforms applied to the path) to the local coordinates of the given node. /// </summary> /// <param name="canvasSize">The size in canvas coordinates.</param> /// <param name="nodeOnPath"> /// The node for which the local coordinates will be computed. /// </param> /// <returns>The size in the local coordinates of the given node.</returns> public virtual SizeFx CanvasToLocal(SizeFx canvasSize, PNode nodeOnPath) { Matrix path = GetPathTransformTo(nodeOnPath); return(MatrixExtensions.InverseTransform(path, canvasSize)); }
/// <summary> /// Overridden. Determines if this bounds handle or any of it's siblings need to be /// flipped. /// </summary> /// <param name="sender">The source of this handle drag event.</param> /// <param name="size">The drag delta relative to the handle.</param> /// <param name="e">A PInputEventArgs that contains the event data.</param> /// <remarks> /// While dragging the bounds handles, the node being resized may cross over itself and /// become reversed (if it is dragged through zero-width or zero-height). In this case, /// the locators for some of the bounds handles will have to be flipped to the opposite /// side. /// </remarks> public override void OnHandleDrag(object sender, SizeFx size, PInputEventArgs e) { base.OnHandleDrag(sender, size, e); PBoundsLocator l = (PBoundsLocator)Locator; PNode n = l.Node; RectangleFx b = n.Bounds; PNode parent = Parent; if (parent != n && parent is PCamera) { size = ((PCamera)parent).LocalToView(size); } size = LocalToGlobal(size); size = n.GlobalToLocal(size); float dx = size.Width; float dy = size.Height; switch (l.Side) { case Direction.North: b = new RectangleFx(b.X, b.Y + dy, b.Width, b.Height - dy); break; case Direction.South: b = new RectangleFx(b.X, b.Y, b.Width, b.Height + dy); break; case Direction.East: b = new RectangleFx(b.X, b.Y, b.Width + dx, b.Height); break; case Direction.West: b = new RectangleFx(b.X + dx, b.Y, b.Width - dx, b.Height); break; case Direction.NorthWest: b = new RectangleFx(b.X + dx, b.Y + dy, b.Width - dx, b.Height - dy); break; case Direction.SouthWest: b = new RectangleFx(b.X + dx, b.Y, b.Width - dx, b.Height + dy); break; case Direction.NorthEast: b = new RectangleFx(b.X, b.Y + dy, b.Width + dx, b.Height - dy); break; case Direction.SouthEast: b = new RectangleFx(b.X, b.Y, b.Width + dx, b.Height + dy); break; } bool flipX = false; bool flipY = false; if (b.Width < 0) { flipX = true; //b.Width = -b.Width; //b.X -= b.Width; float width = -b.Width; float x = b.X - b.Width; b = new RectangleFx(x, b.Y, width, b.Height); } if (b.Height < 0) { flipY = true; //b.Height = -b.Height; //b.Y -= b.Height; float height = -b.Height; float y = b.Y - b.Height; b = new RectangleFx(b.X, y, b.Width, y); } if (flipX || flipY) { FlipSiblingBoundsHandles(flipX, flipY); } n.Bounds = b; }