internal GeneralTransform2DTo3D(GeneralTransform transform2D, Viewport2DVisual3D containingVisual3D, GeneralTransform3D transform3D) { Visual child = containingVisual3D.Visual; Debug.Assert(child != null, "Going from 2D to 3D containingVisual3D.Visual should not be null"); _transform3D = (GeneralTransform3D)transform3D.GetCurrentValueAsFrozen(); // we also need to go one more level up to handle a transform being placed // on the Viewport2DVisual3D's child GeneralTransformGroup transformGroup = new GeneralTransformGroup(); transformGroup.Children.Add((GeneralTransform)transform2D.GetCurrentValueAsFrozen()); transformGroup.Children.Add((GeneralTransform)child.TransformToOuterSpace().GetCurrentValueAsFrozen()); transformGroup.Freeze(); _transform2D = transformGroup; _positions = containingVisual3D.InternalPositionsCache; _textureCoords = containingVisual3D.InternalTextureCoordinatesCache; _triIndices = containingVisual3D.InternalTriangleIndicesCache; _childBounds = child.CalculateSubgraphRenderBoundsOuterSpace(); }
/// <summary> /// Constructor /// </summary> /// <param name="visual3D">The Visual3D that contains the 2D visual</param> /// <param name="fromVisual">The visual on the Visual3D</param> internal GeneralTransform2DTo3DTo2D(Viewport2DVisual3D visual3D, Visual fromVisual) { IsInverse = false; // get a copy of the geometry information - we store our own model to reuse hit // test code on the GeometryModel3D _geometry = new MeshGeometry3D(); _geometry.Positions = visual3D.InternalPositionsCache; _geometry.TextureCoordinates = visual3D.InternalTextureCoordinatesCache; _geometry.TriangleIndices = visual3D.InternalTriangleIndicesCache; _geometry.Freeze(); Visual visual3Dchild = visual3D.Visual; // Special case - Setting CacheMode on V2DV3D causes an internal switch from using a VisualBrush // to using a BitmapCacheBrush. It also introduces an extra 2D Visual in the Visual tree above // the V2DV3D.Visual, but this extra node has no effect on transforms and can safely be ignored. // The transform returned will be identical to the one created for calling TransformTo* with // the V2DV3D.Visual itself. Visual descendentVisual = (fromVisual == visual3Dchild._parent) ? visual3Dchild : fromVisual; // get a copy of the size of the visual brush and the rect on the // visual that the transform is going to/from _visualBrushBounds = visual3Dchild.CalculateSubgraphRenderBoundsOuterSpace(); _visualBounds = descendentVisual.CalculateSubgraphRenderBoundsInnerSpace(); // get the transform that will let us go from the fromVisual to its last 2D // parent before it reaches the 3D part of the graph (i.e. visual3D.Child) GeneralTransformGroup transformGroup = new GeneralTransformGroup(); transformGroup.Children.Add(descendentVisual.TransformToAncestor(visual3Dchild)); transformGroup.Children.Add(visual3Dchild.TransformToOuterSpace()); transformGroup.Freeze(); _transform2D = transformGroup; // store the inverse as well _transform2DInverse = (GeneralTransform)_transform2D.Inverse; if (_transform2DInverse != null) { _transform2DInverse.Freeze(); } // make a copy of the camera and other values on the Viewport3D Viewport3DVisual viewport3D = (Viewport3DVisual)VisualTreeHelper.GetContainingVisual2D(visual3D); _camera = viewport3D.Camera; if (_camera != null) { _camera = (Camera)viewport3D.Camera.GetCurrentValueAsFrozen(); } _viewSize = viewport3D.Viewport.Size; _boundingRect = viewport3D.ComputeSubgraphBounds3D(); _objectToViewport = visual3D.TransformToAncestor(viewport3D); // if the transform was not possible, it could be null - check before freezing if (_objectToViewport != null) { _objectToViewport.Freeze(); } // store the needed transformations for the various operations _worldTransformation = M3DUtil.GetWorldTransformationMatrix(visual3D); _validEdgesCache = null; }
internal void InvokeStylusPluginCollection(RawStylusInputReport inputReport) { // Find PenContexts object for this inputReport. StylusPlugInCollection pic = null; // lock to make sure only one event is processed at a time and no changes to state can // be made until we finish routing this event. lock(__rtiLock) { switch (inputReport.Actions) { case RawStylusActions.Down: case RawStylusActions.Move: case RawStylusActions.Up: // Figure out current target plugincollection. pic = TargetPlugInCollection(inputReport); break; default: return; // Nothing to do unless one of the above events } StylusPlugInCollection currentPic = inputReport.StylusDevice.CurrentNonVerifiedTarget; // Fire Leave event if we need to. if (currentPic != null && currentPic != pic) { // Create new RawStylusInput to send GeneralTransformGroup transformTabletToView = new GeneralTransformGroup(); transformTabletToView.Children.Add(new MatrixTransform(_stylusLogic.GetTabletToViewTransform(inputReport.StylusDevice.TabletDevice))); // this gives matrix in measured units (not device) transformTabletToView.Children.Add(currentPic.ViewToElement); // Make it relative to the element. transformTabletToView.Freeze(); // Must be frozen for multi-threaded access. RawStylusInput tempRawStylusInput = new RawStylusInput(inputReport, transformTabletToView, currentPic); currentPic.FireEnterLeave(false, tempRawStylusInput, false); inputReport.StylusDevice.CurrentNonVerifiedTarget = null; } if (pic != null) { // NOTE: PenContext info will not change (it gets rebuilt instead so keeping ref is fine) // The transformTabletToView matrix and plugincollection rects though can change based // off of layout events which is why we need to lock this. GeneralTransformGroup transformTabletToView = new GeneralTransformGroup(); transformTabletToView.Children.Add(new MatrixTransform(_stylusLogic.GetTabletToViewTransform(inputReport.StylusDevice.TabletDevice))); // this gives matrix in measured units (not device) transformTabletToView.Children.Add(pic.ViewToElement); // Make it relative to the element. transformTabletToView.Freeze(); // Must be frozen for multi-threaded access. RawStylusInput rawStylusInput = new RawStylusInput(inputReport, transformTabletToView, pic); inputReport.RawStylusInput = rawStylusInput; if (pic != currentPic) { inputReport.StylusDevice.CurrentNonVerifiedTarget = pic; pic.FireEnterLeave(true, rawStylusInput, false); } // We are on the pen thread, just call directly. pic.FireRawStylusInput(rawStylusInput); } } // lock(__rtiLock) }
/// <summary> /// The returned matrix can be used to transform coordinates from this Visual to /// the specified Visual. /// Returns null if no such transform exists due to a non-invertible Transform. /// </summary> /// <exception cref="ArgumentNullException">If visual is null.</exception> /// <exception cref="InvalidOperationException">If the Visuals are not connected.</exception> public GeneralTransform TransformToVisual(Visual visual) { DependencyObject ancestor = FindCommonVisualAncestor(visual); Visual ancestorAsVisual = ancestor as Visual; if (ancestorAsVisual == null) { throw new System.InvalidOperationException(SR.Get(SRID.Visual_NoCommonAncestor)); } GeneralTransform g0; Matrix m0; bool isSimple0 = this.TrySimpleTransformToAncestor(ancestorAsVisual, false, out g0, out m0); GeneralTransform g1; Matrix m1; bool isSimple1 = visual.TrySimpleTransformToAncestor(ancestorAsVisual, true, out g1, out m1); // combine the transforms // if both transforms are simple Matrix transforms, just multiply them and // return the result. if (isSimple0 && isSimple1) { MatrixUtil.MultiplyMatrix(ref m0, ref m1); MatrixTransform m = new MatrixTransform(m0); m.Freeze(); return m; } // Handle the case where 0 is simple and 1 is complex. if (isSimple0) { g0 = new MatrixTransform(m0); g0.Freeze(); } else if (isSimple1) { g1 = new MatrixTransform(m1); g1.Freeze(); } // If inverse was requested, TrySimpleTransformToAncestor can return null // add the transform only if it is not null if (g1 != null) { GeneralTransformGroup group = new GeneralTransformGroup(); group.Children.Add(g0); group.Children.Add(g1); group.Freeze(); return group; } return g0; }
private void VerifyStylusPlugInCollectionTarget(RawStylusInputReport rawStylusInputReport) { switch (rawStylusInputReport.Actions) { case RawStylusActions.Down: case RawStylusActions.Move: case RawStylusActions.Up: break; default: return; // do nothing if not Down, Move or Up. } RawStylusInput originalRSI = rawStylusInputReport.RawStylusInput; // See if we have a plugin for the target of this input. StylusPlugInCollection targetPIC = null; StylusPlugInCollection targetRtiPIC = (originalRSI != null) ? originalRSI.Target : null; bool updateEventPoints = false; // Make sure we use UIElement for target if non NULL and hit ContentElement. UIElement newTarget = InputElement.GetContainingUIElement(rawStylusInputReport.StylusDevice.DirectlyOver as DependencyObject) as UIElement; if (newTarget != null) { targetPIC = rawStylusInputReport.PenContext.Contexts.FindPlugInCollection(newTarget); } // Make sure any lock() calls do not reenter on us. using(Dispatcher.DisableProcessing()) { // See if we hit the wrong PlugInCollection on the pen thread and clean things up if we did. if (targetRtiPIC != null && targetRtiPIC != targetPIC && originalRSI != null) { // Fire custom data not confirmed events for both pre and post since bad target... foreach (RawStylusInputCustomData customData in originalRSI.CustomDataList) { customData.Owner.FireCustomData(customData.Data, rawStylusInputReport.Actions, false); } updateEventPoints = originalRSI.StylusPointsModified; // Clear RawStylusInput data. rawStylusInputReport.RawStylusInput = null; } // See if we need to build up an RSI to send to the plugincollection (due to a mistarget). bool sendRawStylusInput = false; if (targetPIC != null && rawStylusInputReport.RawStylusInput == null) { // NOTE: PenContext info will not change (it gets rebuilt instead so keeping ref is fine) // The transformTabletToView matrix and plugincollection rects though can change based // off of layout events which is why we need to lock this. GeneralTransformGroup transformTabletToView = new GeneralTransformGroup(); transformTabletToView.Children.Add(new MatrixTransform(GetTabletToViewTransform(rawStylusInputReport.StylusDevice.TabletDevice))); // this gives matrix in measured units (not device) transformTabletToView.Children.Add(targetPIC.ViewToElement); // Make it relative to the element. transformTabletToView.Freeze(); // Must be frozen for multi-threaded access. RawStylusInput rawStylusInput = new RawStylusInput(rawStylusInputReport, transformTabletToView, targetPIC); rawStylusInputReport.RawStylusInput = rawStylusInput; sendRawStylusInput = true; } // Now fire the confirmed enter/leave events as necessary. StylusPlugInCollection currentTarget = rawStylusInputReport.StylusDevice.CurrentVerifiedTarget; if (targetPIC != currentTarget) { if (currentTarget != null) { // Fire leave event. If we never had a plugin for this event then create a temp one. if (originalRSI == null) { GeneralTransformGroup transformTabletToView = new GeneralTransformGroup(); transformTabletToView.Children.Add(new MatrixTransform(GetTabletToViewTransform(rawStylusInputReport.StylusDevice.TabletDevice))); // this gives matrix in measured units (not device) transformTabletToView.Children.Add(currentTarget.ViewToElement); // Make it relative to the element. transformTabletToView.Freeze(); // Must be frozen for multi-threaded access. originalRSI = new RawStylusInput(rawStylusInputReport, transformTabletToView, currentTarget); } currentTarget.FireEnterLeave(false, originalRSI, true); } if (targetPIC != null) { // Fire Enter event targetPIC.FireEnterLeave(true, rawStylusInputReport.RawStylusInput, true); } // Update the verified target. rawStylusInputReport.StylusDevice.CurrentVerifiedTarget = targetPIC; } // Now fire RawStylusInput if needed to the right plugincollection. if (sendRawStylusInput) { // We are on the pen thread, just call directly. targetPIC.FireRawStylusInput(rawStylusInputReport.RawStylusInput); updateEventPoints = (updateEventPoints || rawStylusInputReport.RawStylusInput.StylusPointsModified); } // Now fire PrePreviewCustomData events. if (targetPIC != null) { // Send custom data pre event foreach (RawStylusInputCustomData customData in rawStylusInputReport.RawStylusInput.CustomDataList) { customData.Owner.FireCustomData(customData.Data, rawStylusInputReport.Actions, true); } } // VerifyRawTarget might resend to correct plugins or may have hit the wrong plugincollection. The StylusPackets // may be overriden in those plugins so we need to call UpdateEventStylusPoints to update things. if (updateEventPoints) { rawStylusInputReport.StylusDevice.UpdateEventStylusPoints(rawStylusInputReport, true); } } }