/// <summary> /// Subclasses should override this method to get notified when the handle stops /// getting dragged. /// </summary> /// <param name="sender">The source of this handle drag event.</param> /// <param name="point">The drag position relative to the handle.</param> /// <param name="e">A PInputEventArgs that contains the event data.</param> public virtual void OnEndHandleDrag(object sender, PointFx point, PInputEventArgs e) { if (EndHandleDrag != null) { EndHandleDrag(sender, point, e); } }
public override PointFx GetViewPosition(RectangleFx bounds) { PointFx pos = PointFx.Empty; if (camera != null) { // First we compute the union of all the layers RectangleFx layerBounds = camera.UnionOfLayerFullBounds; // Then we put the bounds into camera coordinates and // union the camera bounds layerBounds = camera.ViewToLocal(layerBounds); layerBounds = RectangleFxtensions.Union(layerBounds, bounds); // Rather than finding the distance from the upper left corner // of the window to the upper left corner of the document - // we instead find the distance from the lower right corner // of the window to the upper left corner of the document // THEN we measure the offset from the lower right corner // of the document int x = (int)(layerBounds.Width - (bounds.X + bounds.Width - layerBounds.X) + 0.5); int y = (int)(layerBounds.Height - (bounds.Y + bounds.Height - layerBounds.Y) + 0.5); // Make sure the value isn't greater than the maximum x = (int)Math.Min(x, layerBounds.Width - 1); y = (int)Math.Min(y, layerBounds.Height - 1); pos = new PointFx(x, y); } return(pos); }
/// <summary> /// Force this handle to relocate itself using its locator. /// </summary> public virtual void RelocateHandle() { if (locator != null) { RectangleFx b = Bounds; PointFx aPoint = locator.LocatePoint; if (locator is PNodeLocator) { PNode located = ((PNodeLocator)locator).Node; PNode parent = Parent; aPoint = located.LocalToGlobal(aPoint); aPoint = GlobalToLocal(aPoint); if (parent != located && parent is PCamera) { aPoint = ((PCamera)parent).ViewToLocal(aPoint); } } float newCenterX = aPoint.X; float newCenterY = aPoint.Y; PointFx bCenter = PUtil.CenterOfRectangle(b); if (newCenterX != bCenter.X || newCenterY != bCenter.Y) { CenterBoundsOnPoint(newCenterX, newCenterY); } } }
/// <summary> /// Compares two nodes and returns a value indicating whether the first node's /// distance to the point is less than, equal to or greater than the second /// node's distance to the specified point. /// </summary> /// <param name="o1">The first node to compare.</param> /// <param name="o2">The second node to compare.</param> /// <returns> /// Less than 0, if o1's distance to the point is less than o2's distance to the /// point; 0 if o1's distance to the point equals 02's distance to the point; /// greater than 0 if o1's distance to the point is greater than o2's distance to /// the point. /// </returns> public int Compare(object o1, object o2) { PNode each1 = (PNode)o1; PNode each2 = (PNode)o2; PointFx each1Center = PUtil.CenterOfRectangle(each1.GlobalFullBounds); PointFx each2Center = PUtil.CenterOfRectangle(each2.GlobalFullBounds); if (!NODE_TO_GLOBAL_NODE_CENTER_MAPPING.Contains(each1)) { NODE_TO_GLOBAL_NODE_CENTER_MAPPING.Add(each1, each1Center); } if (!NODE_TO_GLOBAL_NODE_CENTER_MAPPING.Contains(each2)) { NODE_TO_GLOBAL_NODE_CENTER_MAPPING.Add(each2, each2Center); } float distance1 = PUtil.DistanceBetweenPoints(point, each1Center); float distance2 = PUtil.DistanceBetweenPoints(point, each2Center); if (distance1 < distance2) { return(-1); } else if (distance1 == distance2) { return(0); } else { return(1); } }
/// <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> /// Sets the view position of the scrollDirector. /// </summary> /// <param name="point">The new position.</param> /// <param name="animate">Indicates whether or not to animate the transition.</param> protected virtual void SetViewPosition(PointFx point, bool animate) { if (view == null) { return; } float oldX = 0, oldY = 0, x = point.X, y = point.Y; PointFx vp = this.ViewPosition; oldX = vp.X; oldY = vp.Y; // Send the scroll director the exact view position and let it // interpret it as needed float newX = x; float newY = y; if ((oldX != newX) || (oldY != newY)) { if (animate) { scroll = new ScrollActivity(new Point((int)ViewPosition.X, (int)ViewPosition.Y), new Point((int)point.X, (int)point.Y), scrollDirector, animateScrollDuration); Canvas.Root.AddActivity(scroll); } else { scrollDirector.SetViewPosition(newX, newY); } } }
/// <summary> /// Gets the nearest neighbor in the specified direction. /// </summary> /// <param name="aDirection"> /// The direction in which to find the nearest neighbor. /// </param> /// <returns>The nearest neighbor in the specified direction.</returns> public virtual PNode GetNeighborInDirection(Direction aDirection) { if (focusNode == null) { return(null); } NODE_TO_GLOBAL_NODE_CENTER_MAPPING.Clear(); PointFx highlightCenter = PUtil.CenterOfRectangle(focusNode.GlobalFullBounds); NODE_TO_GLOBAL_NODE_CENTER_MAPPING.Add(focusNode, highlightCenter); PNodeList l = GetNeighbors(); SortNodesByDistanceFromPoint(l, highlightCenter); foreach (PNode each in l) { if (NodeIsNeighborInDirection(each, aDirection)) { return(each); } } return(null); }
/// <summary> /// Expands the given rectangle to include the given point. /// </summary> /// <param name="rect">The rectangle to expand.</param> /// <param name="p">The point to include.</param> /// <returns>The expanded rectangle.</returns> public static RectangleFx AddPointToRect(RectangleFx rect, PointFx p) { float px = p.X; float py = p.Y; float rx = rect.X; float ry = rect.Y; float rw = rect.Width; float rh = rect.Height; if (rect == RectangleFx.Empty) { //rect.X = px; //rect.Y = py; //rect.Width = 0; //rect.Height = 0; rect = new RectangleFx(px, py, 0, 0); } else { float x1 = (rx <= px) ? rx : px; float y1 = (ry <= py) ? ry : py; float x2 = ((rx + rw) >= px) ? (rx + rw) : px; float y2 = ((ry + rh) >= py) ? (ry + rh) : py; //rect.X = x1; //rect.Y = y1; //rect.Width = x2 - x1; //rect.Height = y2 - y1; rect = new RectangleFx(x1, y1, x2 - x1, y2 - y1); } return(rect); }
protected override void OnStartDrag(object sender, PInputEventArgs e) { base.OnStartDrag(sender, e); draggedNode = e.PickedNode; draggedNode.MoveToFront(); nodeStartPosition = draggedNode.Offset; }
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(); }
//**************************************************************** // Event Handling - Methods for handling events // // The dispatch manager updates the focus nodes based on the // incoming events, and dispatches those events to the appropriate // focus nodes. //**************************************************************** /// <summary> /// Create a new PInputEvent based on the next windows event and dispatch it to Piccolo. /// </summary> public virtual void ProcessInput() { if (nextInput == null) { return; } PInputEventArgs e = new PInputEventArgs(this, nextInput, nextType); //The EventArgs object for a Click event does not provide the position, so //we just ignore it here. if (e.IsMouseEvent || e.IsDragDropEvent) { lastCanvasPosition = currentCanvasPosition; if (e.IsMouseEvent) { currentCanvasPosition = new PointFx(((MouseEventArgs)nextInput).X, ((MouseEventArgs)nextInput).Y); } else { Point pt = new Point((int)((DragEventArgs)nextInput).X, (int)((DragEventArgs)nextInput).Y); Point tPt = nextWindowsSource.PointToClient(pt); currentCanvasPosition = new PointFx(tPt.X, tPt.Y); } PPickPath pickPath = nextInputSource.Pick(currentCanvasPosition.X, currentCanvasPosition.Y, 1); MouseOver = pickPath; } nextInput = null; nextInputSource = null; Dispatch(e); }
/// <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> /// Overridden. Notifies the node whose bounds this handle is locating itself on that /// the resize bounds sequence is finisheed. /// </summary> /// <param name="sender">The source of this handle drag event.</param> /// <param name="point">The drag position relative to the handle.</param> /// <param name="e">A PInputEventArgs that contains the event data.</param> public override void OnEndHandleDrag(object sender, PointFx point, PInputEventArgs e) { base.OnEndHandleDrag(sender, point, e); PBoundsLocator l = (PBoundsLocator)Locator; l.Node.EndResizeBounds(); }
public void UpdateLink() { PointFx p1 = PUtil.CenterOfRectangle(node1.FullBounds); PointFx p2 = PUtil.CenterOfRectangle(node2.FullBounds); link.Reset(); link.AddLine(p1.X, p1.Y, p2.X, p2.Y); }
/// <summary> /// Overridden. See <see cref="PPathActivity.SetRelativeTargetValue(float,int,int)"> /// PPathActivity.SetRelativeTargetValue</see>. /// </summary> public override void SetRelativeTargetValue(float zeroToOne, int startKnot, int endKnot) { PointFx start = GetPosition(startKnot); PointFx end = GetPosition(endKnot); target.SetPosition(start.X + (zeroToOne * (end.X - start.X)), start.Y + (zeroToOne * (end.Y - start.Y))); }
protected override void OnStartDrag(object sender, PInputEventArgs e) { base.OnStartDrag(sender, e); squiggle = new PPath(); lastPoint = e.Position; squiggle.Pen = new Pen(Brushes.Black, (float)(1 / e.Camera.ViewScale)); layer.AddChild(squiggle); }
public override void OnMouseDrag(object sender, PInputEventArgs e) { base.OnMouseDrag(sender, e); // update the drag point location. dragPoint = e.Position; // update the rectangle shape. UpdateRectangle(); }
/// <summary> /// Sets the initial press point and press node for the selection. /// </summary> /// <param name="e">A PInputEventArgs that contains the event data.</param> protected virtual void InitializeSelection(PInputEventArgs e) { canvasPressPt = e.CanvasPosition; presspt = e.Position; pressNode = e.Path.PickedNode; if (pressNode is PCamera) { pressNode = null; } }
public void UpdateSquiggle(PInputEventArgs e) { PointFx p2 = e.Position; if (p.X != p2.X || p.Y != p2.Y) { squiggle.AddLine(p.X, p.Y, p2.X, p2.Y); } p = p2; }
protected void UpdateSquiggle(PInputEventArgs e) { PointFx p = e.Position; if (p.X != lastPoint.X || p.Y != lastPoint.Y) { squiggle.AddLine(lastPoint.X, lastPoint.Y, p.X, p.Y); } lastPoint = p; }
public void UpdateToolTip(PInputEventArgs e) { PNode n = e.InputManager.MouseOver.PickedNode; String tooltipString = (String)n.Tag; PointFx p = e.CanvasPosition; p = e.Path.CanvasToLocal(p, Canvas.Camera); tooltipNode.Text = tooltipString; tooltipNode.SetOffset(p.X + 8, p.Y - 8); }
/// <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); }
protected override void OnStartDrag(object sender, PInputEventArgs e) { base.OnStartDrag(sender, e); p = e.Position; squiggle = new PPath(); // Add squiggles to the first layer of the bottom camera. In the case of the // lens these squiggles will be added to the layer that is only visible by the lens, // In the case of the canvas camera the squiggles will be added to the shared layer // viewed by both the canvas camera and the lens. e.Camera.GetLayer(0).AddChild(squiggle); }
/// <summary> /// Scrolls the view according to the given ScrollEventArgs. /// </summary> /// <param name="scrollBar">The source of the ScrollEvent.</param> /// <param name="e">A ScrollEventArgs containing the event data.</param> /// <param name="animateFirst"> /// A boolean value indicating whether or not to animate the first scroll. /// </param> protected virtual void Scroll(ScrollBar scrollBar, ScrollEventArgs e, bool animateFirst) { // If we are animating, terminate any previous scroll activities. if (animateScrolls) { TerminatePreviousScroll(scrollBar, e); } if (e.Type != ScrollEventType.EndScroll) { PointFx viewPosition; if (scrollBar is VScrollBar) { viewPosition = new PointFx(ViewPosition.X, e.NewValue); } else { viewPosition = new PointFx(e.NewValue, ViewPosition.Y); } // At the start of a scroll sequence, we will animate the scroll if // animateScrolls is set to true. if (startOfScrollSequence && animateFirst) { if (e.Type != ScrollEventType.ThumbTrack) { SetViewPosition(viewPosition, true); e.NewValue = scrollBar.Value; } startOfScrollSequence = false; } else { if (!Canvas.Interacting) { Canvas.Interacting = true; } // Otherwise, just set the position directly. SetViewPosition(viewPosition, false); } } else { // Reset some flags at the end of a scroll sequence. startOfScrollSequence = true; if (Canvas.Interacting) { Canvas.Interacting = false; } } }
public override void SetViewPosition(float x, float y) { if (camera != null) { // If a scroll is in progress - we ignore new scrolls - // if we didn't, since the scrollbars depend on the camera location // we can end up with an infinite loo if (!scrollInProgress) { scrollInProgress = true; // Get the union of all the layers' bounds RectangleFx layerBounds = camera.UnionOfLayerFullBounds; Matrix matrix = camera.ViewMatrix; layerBounds = MatrixExtensions.Transform(matrix, layerBounds); // Union the camera view bounds RectangleFx viewBounds = camera.Bounds; layerBounds = RectangleFxtensions.Union(layerBounds, viewBounds); // Now find the new view position in view coordinates - // This is basically the distance from the lower right // corner of the window to the upper left corner of the // document // We then measure the offset from the lower right corner // of the document PointFx newPoint = new PointFx( layerBounds.X + layerBounds.Width - (x + viewBounds.Width), layerBounds.Y + layerBounds.Height - (y + viewBounds.Height)); // Now transform the new view position into global coords newPoint = camera.LocalToView(newPoint); // Compute the new matrix values to put the camera at the // correct location float[] elements = MatrixExtensions.GetElements(matrix); elements[4] = -(elements[0] * newPoint.X + elements[1] * newPoint.Y); elements[5] = -(elements[2] * newPoint.X + elements[3] * newPoint.Y); matrix = MatrixExtensions.SetElements(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5]); // Now actually set the camera's transform camera.ViewMatrix = matrix; scrollInProgress = false; } } }
/// <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); }
protected override void OnDrag(object sender, PInputEventArgs e) { base.OnDrag(sender, e); PointFx start = canvas.Camera.LocalToView((PointFx)MousePressedCanvasPoint); PointFx current = e.GetPositionRelativeTo(canvas.Layer); PointFx dest = PointFx.Empty; dest.X = nodeStartPosition.X + (current.X - start.X); dest.Y = nodeStartPosition.Y + (current.Y - start.Y); dest.X = dest.X - (dest.X % gridSpacing); dest.Y = dest.Y - (dest.Y % gridSpacing); draggedNode.SetOffset(dest.X, dest.Y); }
/// <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); }
public override void OnMouseDown(object sender, PInputEventArgs e) { base.OnMouseDown(sender, e); PLayer layer = canvas.Layer; // Initialize the locations. pressPoint = e.Position; dragPoint = pressPoint; // create a new rectangle and add it to the canvas layer so that // we can see it. rectangle = new PPath(); rectangle.Pen = new Pen(Brushes.Black, (float)(1 / e.Camera.ViewScale)); layer.AddChild(rectangle); // update the rectangle shape. UpdateRectangle(); }
//**************************************************************** // 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)); }