/// <summary> /// Traverse from the bottom right of the scene graph (top visible node) /// up the tree determining which parent nodes are occluded by their children /// nodes. /// </summary> /// <param name="n">The node to find occlusions for.</param> /// <param name="pickPath"> /// A pick path representing the bounds of <c>n</c> in parent coordinates. /// </param> /// <remarks> /// Note that this is only detecting a subset of occlusions (parent, child), /// others such as overlapping siblings or cousins are not detected. /// </remarks> public void DetectOcclusions(PNode n, PPickPath pickPath) { if (n.FullIntersects(pickPath.PickBounds)) { pickPath.PushMatrix(n.MatrixReference); int count = n.ChildrenCount; for (int i = count - 1; i >= 0; i--) { PNode each = n[i]; if (n.Occluded) { // if n has been occluded by a previous decendent then // this child must also be occluded each.Occluded = true; } else { // see if child each occludes n DetectOcclusions(each, pickPath); } } // see if n occludes it's parents if (!n.Occluded) { if (n.Intersects(pickPath.PickBounds)) { if (n.IsOpaque(pickPath.PickBounds)) { PNode p = n.Parent; while (p != null && !p.Occluded) { p.Occluded = true; } } } } pickPath.PopMatrix(n.MatrixReference); } }
/// <summary> /// Overridden. Returns true if this node or any pickable descendends are picked. /// </summary> /// <remarks> /// If a pick occurs the pickPath is modified so that this node is always returned as /// the picked node, even if it was a decendent node that initialy reported the pick. /// </remarks> /// <param name="pickPath"></param> /// <returns>True if this node or any descendents are picked; false, otherwise.</returns> public override bool FullPick(PPickPath pickPath) { if (base.FullPick(pickPath)) { PNode picked = pickPath.PickedNode; // this code won't work with internal cameras, because it doesn't pop // the cameras view transform. while (picked != this) { pickPath.PopMatrix(picked.MatrixReference); pickPath.PopNode(picked); picked = pickPath.PickedNode; } return true; } return false; }
public virtual PActivity DirectCameraViewToFocus(PCamera aCamera, PNode aFocusNode, PPickPath path, int duration) { PMatrix originalViewMatrix = aCamera.ViewMatrix; // Scale the canvas to include SizeF s = new SizeF(1, 0); s = aFocusNode.GlobalToLocal(s); float scaleFactor = s.Width / aCamera.ViewScale; PointF scalePoint = PUtil.CenterOfRectangle(aFocusNode.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(aFocusNode.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); PMatrix resultingMatrix = aCamera.ViewMatrix; aCamera.ViewMatrix = originalViewMatrix; // Animate the canvas so that it ends up with the given // view transform. PActivity animateCameraViewActivity = AnimateCameraViewMatrixTo(aCamera, resultingMatrix, duration); PControl controlNode = (PControl)aFocusNode; aCamera.Root.WaitForActivities(); controlNode.CurrentCanvas = path.TopCamera.Canvas; PointF pf = path.GetPathTransformTo(controlNode).Transform(new PointF(controlNode.X, controlNode.Y)); controlNode.ControlLocation = new Point((int)pf.X, (int)pf.Y); controlNode.Editing = true; return animateCameraViewActivity; }
/// <summary> /// Try to pick this node and all of its descendents. /// </summary> /// <param name="pickPath">The pick path to add the node to if its picked.</param> /// <returns>True if this node or one of its descendents was picked; else false.</returns> /// <remarks> /// <b>Notes to Inheritors:</b> Most subclasses should not need to override this /// method. Instead they should override <c>Pick</c> or <c>PickAfterChildren</c>. /// </remarks> public virtual bool FullPick(PPickPath pickPath) { if ((Pickable || ChildrenPickable) && FullIntersects(pickPath.PickBounds)) { pickPath.PushNode(this); pickPath.PushMatrix(matrix); bool thisPickable = Pickable && pickPath.AcceptsNode(this); if (thisPickable && Pick(pickPath)) { return true; } if (ChildrenPickable) { int count = ChildrenCount; for (int i = count - 1; i >= 0; i--) { if (children[i].FullPick(pickPath)) { return true; } } } if (thisPickable && PickAfterChildren(pickPath)) { return true; } pickPath.PopMatrix(matrix); pickPath.PopNode(this); } return false; }
/// <summary> /// Try to pick this node before its children have had a chance to be picked. /// </summary> /// <param name="pickPath">The pick path used for the pick operation.</param> /// <returns>True if this node was picked; else false.</returns> /// <remarks> /// Nodes that paint on top of their children may want to override this method to see /// if the pick path intersects that paint. /// </remarks> protected virtual bool Pick(PPickPath pickPath) { return false; }
/// <summary> /// Dispatch the given PInputEvent to the given pick path. /// </summary> /// <param name="e">A PInputEventArgs that contains the event data.</param> /// <param name="type">The type of PInputEvent being dispatched.</param> /// <param name="path">The pick path to which the PInputEvent will be dispatched.</param> public virtual void DispatchToPath(PInputEventArgs e, PInputType type, PPickPath path) { if (path != null) { //set the type and clear the handled bit since the same event object is //used to send multiple events such as mouseEntered/mouseExited and mouseMove. e.Type = type; e.Handled = false; path.ProcessEvent(e); } }
/// <summary> /// Check if the mouse has entered or exited a node during a drag and drop /// operation and, if so, dispatch the appropriate event. /// </summary> /// <param name="e">A PInputEventArgs that contains the event data.</param> public virtual void CheckForMouseDragEnteredAndExited(PInputEventArgs e) { PNode c = (mouseOver != null) ? mouseOver.PickedNode : null; PNode p = (previousMouseOver != null) ? previousMouseOver.PickedNode : null; if (c != p) { DispatchToPath(e, PInputType.DragLeave, previousMouseOver); DispatchToPath(e, PInputType.DragEnter, mouseOver); previousMouseOver = mouseOver; } }
/// <summary> /// Dispatch the given event to the appropriate focus node. /// </summary> /// <param name="e">An PInputEventArgs that contains the event data.</param> public virtual void Dispatch(PInputEventArgs e) { switch (e.Type) { case PInputType.KeyDown: DispatchToPath(e, PInputType.KeyDown, KeyboardFocus); break; case PInputType.KeyPress: DispatchToPath(e, PInputType.KeyPress, KeyboardFocus); break; case PInputType.KeyUp: DispatchToPath(e, PInputType.KeyUp, KeyboardFocus); break; case PInputType.Click: //The click event occurs before the MouseRelease so we can //dispatch to the current focused node as opposed to the //previously focused node if (MouseFocus.PickedNode == MouseOver.PickedNode) { DispatchToPath(e, PInputType.Click, MouseFocus); } break; case PInputType.DoubleClick: //The double click event occurs before the MouseRelease so we can //dispatch to the current focused node as opposed to the //previously focused node if (MouseFocus.PickedNode == MouseOver.PickedNode) { DispatchToPath(e, PInputType.DoubleClick, MouseFocus); } break; case PInputType.MouseDown: MouseFocus = MouseOver; DispatchToPath(e, PInputType.MouseDown, MouseFocus); break; case PInputType.MouseUp: CheckForMouseEnteredAndExited(e); DispatchToPath(e, PInputType.MouseUp, MouseFocus); mouseFocus = null; break; case PInputType.MouseDrag: CheckForMouseEnteredAndExited(e); DispatchToPath(e, PInputType.MouseDrag, MouseFocus); break; case PInputType.MouseMove: CheckForMouseEnteredAndExited(e); DispatchToPath(e, PInputType.MouseMove, MouseOver); break; case PInputType.MouseWheel: MouseFocus = MouseOver; DispatchToPath(e, PInputType.MouseWheel, MouseOver); break; case PInputType.DragOver: CheckForMouseDragEnteredAndExited(e); DispatchToPath(e, PInputType.DragOver, MouseOver); break; case PInputType.DragDrop: CheckForMouseDragEnteredAndExited(e); DispatchToPath(e, PInputType.DragDrop, MouseOver); break; } }
/// <summary> /// Overridden. Return false since this node should never be picked. /// </summary> /// <param name="pickPath">The pick path used for the pick operation.</param> /// <returns>False since this node should never be picked.</returns> protected override bool PickAfterChildren(PPickPath pickPath) { return false; }
//**************************************************************** // Picking - Methods for picking the camera and it's view. //**************************************************************** /// <summary> /// Generate and return a PPickPath for the point x,y specified in the local /// coordinate system of this camera. /// </summary> /// <param name="x">The x coordinate of the pick point.</param> /// <param name="y">The y coordinate of the pick point.</param> /// <param name="halo"> /// The value to use for the width and height of the rectangle used for picking. /// </param> /// <returns>A PPickPath for the given point.</returns> /// <remarks> /// Picking is done with a rectangle, halo specifies how large that rectangle /// will be. /// </remarks> public virtual PPickPath Pick(float x, float y, float halo) { RectangleF b = PUtil.InflatedRectangle(new PointF(x, y), halo, halo); PPickPath result = new PPickPath(this, b); FullPick(result); // make sure this camera is pushed. if (result.NodeStackReference.Count == 0) { result.PushNode(this); result.PushMatrix(MatrixReference); } return result; }
/// <summary> /// Constructs a new PPickPath. /// </summary> /// <param name="aCamera">The camera that originated the pick action.</param> /// <param name="aScreenPickBounds">The bounds being picked.</param> public PPickPath(PCamera aCamera, RectangleF aScreenPickBounds) { pickBoundsStack = new Stack(); bottomCamera = null; topCamera = aCamera; nodeStack = new Stack(); matrixStack = new Stack(); pickBoundsStack.Push(aScreenPickBounds); CURRENT_PICK_PATH = this; }
/// <summary> /// Overridden. Only picks this node's children if the pick bounds intersects the /// clip. /// </summary> /// <param name="pickPath">The pick path to add the node to if its picked.</param> /// <returns> /// True if this node or one of its descendents was picked; else false. /// </returns> public override bool FullPick(PPickPath pickPath) { if (Pickable && FullIntersects(pickPath.PickBounds)) { pickPath.PushNode(this); pickPath.PushMatrix(MatrixReference); if (Pick(pickPath)) { return true; } if (ChildrenPickable && PUtil.PathIntersectsRect(PathReference, pickPath.PickBounds)) { int count = ChildrenCount; for (int i = count - 1; i >= 0; i--) { PNode each = this.GetChild(i); if (each.FullPick(pickPath)) return true; } } if (PickAfterChildren(pickPath)) { return true; } pickPath.PopMatrix(MatrixReference); pickPath.PopNode(this); } return false; }
/// <summary> /// Show the interface if it isn't already being shown /// /// If it is being shown, and the user presses escape, remove it /// </summary> public override void OnKeyDown(object sender, PInputEventArgs e) { base.OnKeyDown(sender, e); //If the interface isn't shown, and should be if (IsPressed == false && Interface.Accepts(e)) { //Clear the text from the last time it was used, and show it to the user Interface.Entry.Text = ""; Camera.AddChild(Interface); //Fetch the current keyboard focus to save for later, then shift the keyboard focus to the interface if (Page.LastPage != null) { KeyFocus = Page.LastPage.ToPickPath(); } else { KeyFocus = Camera.ToPickPath(); } e.InputManager.KeyboardFocus = Interface.Entry.ToPickPath(e.Camera, Interface.Entry.Bounds); //Activate the interface IsPressed = true; Interface.Activate(sender, e); } //If the user pressed ecape, remove the interface else if (e.KeyCode == Keys.Escape) { RemoveInterface(e); } //Register the Activate button press, if appropriate if (Interface.Accepts(e)) { Interface.RegisterActivateButtonPress(sender, e); } }
/// <summary> /// Pick all the layers that the camera is looking at. /// </summary> /// <param name="pickPath">The pick path to use for the pick operation.</param> /// <returns> /// True if an object viewed by the camera was picked; else false. /// </returns> /// <remarks> /// This method is only called when the camera's view matrix and clip are /// applied to the pickPath. /// </remarks> protected virtual bool PickCameraView(PPickPath pickPath) { int count = LayerCount; for (int i = count - 1; i >= 0; i--) { PLayer each = layers[i]; if (each.FullPick(pickPath)) return true; } return false; }
/// <summary> /// Overridden. After the direct children of the camera have been given a chance /// to be picked objects viewed by the camera are given a chance to be picked. /// </summary> /// <param name="pickPath">The pick path used for the pick operation.</param> /// <returns> /// True if an object viewed by the camera was picked; else false. /// </returns> protected override bool PickAfterChildren(PPickPath pickPath) { if (Intersects(pickPath.PickBounds)) { pickPath.PushMatrix(viewMatrix); if (PickCameraView(pickPath)) { return true; } pickPath.PopMatrix(viewMatrix); return true; } return false; }
/// <summary> /// Try to pick this node after its children have had a chance to be picked. /// </summary> /// <param name="pickPath">The pick path used for the pick operation.</param> /// <returns>True if this node was picked; else false.</returns> /// <remarks> /// <b>Notes to Inheritors:</b> Most subclasses that define a different geometry will /// need to override this method. /// </remarks> protected virtual bool PickAfterChildren(PPickPath pickPath) { if (Intersects(pickPath.PickBounds)) { return true; } return false; }
/// <summary> /// Creates a pick path with the given Camera and pickbounds and adds this node. /// </summary> /// <param name="aCamera">The camera to use when creating the pick path.</param> /// <param name="pickBounds">The pick bounds to use when creating the pick path.</param> /// <returns> /// A pick path with the given camera and pickbounds that contains this node /// </returns> public virtual PPickPath ToPickPath(PCamera aCamera, RectangleF pickBounds) { PPickPath pickPath = new PPickPath(aCamera, pickBounds); pickPath.PushNode(this); return pickPath; }
/// <summary> /// Overridden. Performs picking in canvas coordinates if <see cref="PickMode">PickMode</see> /// is false. /// </summary> /// <remarks> /// Due to the implementation of the GraphicsPath object, picking in canvas coordinates /// is more accurate, but will introduce a significant performance hit. /// </remarks> protected override bool PickAfterChildren(PPickPath pickPath) { if (pickMode == PathPickMode.Fast) { return base.PickAfterChildren(pickPath); } else { return Intersects(pickPath.PickBounds, pickPath.GetPathTransformTo(this)); } }