/// <summary> /// Helper method that constrains packet data within /// a specified input rectangle. /// </summary> /// <param name="data">The data to modify.</param> private void ModifyPacketData(StylusDataBase data) { // For each packet in the packet data, check whether // its x,y values fall outside of the specified rectangle. // If so, replace them with the nearest point that still // falls within the rectangle. for (int i = 0; i < data.Count; i += data.PacketPropertyCount) { // packet data always has x followed by y followed by the rest int x = data[i]; int y = data[i + 1]; // Constrain points to the input rectangle x = Math.Max(x, rectangle.Left); x = Math.Min(x, rectangle.Right); y = Math.Max(y, rectangle.Top); y = Math.Min(y, rectangle.Bottom); // If necessary, modify the x,y packet data if (x != data[i]) { data[i] = x; } if (y != data[i + 1]) { data[i + 1] = y; } } }
/// <summary> /// Modifies packet data according to the current <see cref="Transform">transform matrix</see>. /// </summary> /// <param name="data">The data to modify.</param> private void ModifyPacketData(StylusDataBase data) { // Create an array of points, one for each packet. Point[] points = new Point[data.Count / data.PacketPropertyCount]; int i, j; // Declare outside the loop for the Debug.Assert below. // Convert the X and Y coordinates from the packet data into Points. for (i = 0, j = 0; i < data.Count; i += data.PacketPropertyCount, j++) { // X and Y are always the first two elements in a packet. points[j] = new Point(data[i], data[i + 1]); } // We should have exactly as many points as there were packets in the array. Debug.Assert(i == data.Count); Debug.Assert(j == points.Length); // Apply the transformation to the points. this.m_InverseTransform.TransformPoints(points); // Store the modified X and Y coordinates back into the data. // The new values will be seen by all subsequent IStylus(A)SyncPlugins. for (i = 0, j = 0; i < data.Count; i += data.PacketPropertyCount, j++) { data[i] = points[j].X; data[i + 1] = points[j].Y; } }
IEnumerable <TouchEventArgs> GetEvents(StylusDataBase data) { for (int i = 0; i < data.Count; i += data.PacketPropertyCount) { float x = data[i]; float y = data[i + 1]; if (data.Stylus.Name != "Mouse") // TODO: hack { yield return new TouchEventArgs { Index = data.Stylus.Id, X = Math.Floor(x * _dpiX / 2540.0F), Y = Math.Floor(y * _dpiY / 2540.0F) } } ; } } }
private void Interpret(StylusDataBase data, int index) { Point offset = attachedControl.PointToScreen(new Point(0, 0)); PointF relativePosition = HimetricToPointF(data[index], data[index + 1]); PointF position = subject.ScreenToDocument(new PointF(relativePosition.X + offset.X, relativePosition.Y + offset.Y)); float pressure = (data.PacketPropertyCount > 3 ? data[index + 2] : 255.0f) / 255.0f; int status = data[index + data.PacketPropertyCount - 1]; MouseButtons button = StatusToMouseButtons(status); bool didMouseMove = false; if (lastbutton != button) { //if a button was previously down, MouseUp it. if (lastbutton != MouseButtons.None) { subject.PerformDocumentMouseMove(button, 1, position.X, position.Y, 0, pressure); subject.PerformDocumentMouseUp(lastbutton, 1, position.X, position.Y, 0, pressure); didMouseMove = true; } //if a new button was pushed, MouseDown it. if (button != MouseButtons.None) { subject.PerformDocumentMouseDown(button, 1, position.X, position.Y, 0, pressure); subject.PerformDocumentMouseMove(button, 1, position.X, position.Y, 0, pressure); didMouseMove = true; } } if (!didMouseMove) { //regardless of the button states, send a new MouseMove subject.PerformDocumentMouseMove(button, 1, position.X, position.Y, 0, pressure); } lastbutton = button; }
/// <summary> /// Erases strokes that overlap the cursor. /// </summary> /// <param name="sender">The real time stylus associated with the notification</param> /// <param name="data">The notification data</param> /// <seealso cref="EraserPlugin.StylusDown"/> /// <seealso cref="EraserPlugin.Packets"/> /// <seealso cref="EraserPlugin.StylusUp"/> private void HandlePackets(RealTimeStylus sender, StylusDataBase data) { using (Synchronizer.Lock(this)) { // Ignore the strokes if the eraser stylus is not selected, // and if the stylus is not inverted. if (this.Eraser == null && !data.Stylus.Inverted) return; // Ignore if a touch input if (m_TouchSupported) { try { if (sender.GetTabletFromTabletContextId(data.Stylus.TabletContextId).DeviceKind == TabletDeviceKind.Touch) return; } catch { m_TouchSupported = false; } } // Ignore the strokes if no ink sheet is selected. InkSheetModel sheet = this.InkSheetModel; if (sheet == null) return; // Convert the X and Y coordinates of the data, // which are defined to be at offsets [i] and [i+1], // to an array of Points. Debug.Assert(data.Count % data.PacketPropertyCount == 0); Point[] points = new Point[data.Count / data.PacketPropertyCount]; for (int i = 0, j = 0, il = data.Count, inc = data.PacketPropertyCount; i < il; i += inc, j++) points[j] = new Point(data[i], data[i + 1]); // Convert the ink points to pixels so we can // ignore the ones that are outside the slide view area. // This is done all at once to conserve resources used by the Graphics object. Point[] pixels = points; using (Synchronizer.Lock(this.m_Display.SyncRoot)) using (Graphics g = this.m_Display.CreateGraphics()) this.m_Renderer.InkSpaceToPixel(g, ref pixels); // Prevent anyone else from accessing the ink concurrently. using (Synchronizer.Lock(this.InkSheetModel.Ink.Strokes.SyncRoot)) { // Iterate through each point through which the cursor has passed. for(int i = 0, il = points.Length; i < il; i++) { // Don't erase anything when the cursor is outside of the // slide viewing area. This prevents users from accidentally // erasing strokes they can't see, especially when using the // slide zoom feature. if (!this.m_DisplayBounds.Contains(pixels[i])) continue; // Find all strokes within some radius from the cursor. Strokes erased = sheet.Ink.HitTest(points[i], ERASER_RADIUS); // If any strokes were found, erase them. if (erased.Count > 0) { // Get the list of stroke IDs in order to send an event int[] ids = new int[erased.Count]; for (int j = 0; j < ids.Length; j++) ids[j] = erased[j].Id; // We must first warn listeners that the strokes are about to // be deleted, because after they're deleted, no information // about them can be recovered. This is used to send // network events and to store undo information. sheet.OnInkDeleting(new StrokesEventArgs(ids)); // Delete the erased strokes. sheet.Ink.DeleteStrokes(erased); // Inform listeners that the strokes have actually been deleted. // This causes slide displays to refresh. sheet.OnInkDeleted(new StrokesEventArgs(ids)); } } } } }
private void HandlePackets(StylusDataBase data) { using(Synchronizer.Lock(this)) { GraphicsPath boundary = ((GraphicsPath) this.m_SelectionBoundaryTable[data.Stylus.Id]); ArrayList collected = ((ArrayList) this.m_SelectionPointsTable[data.Stylus.Id]); Debug.Assert((boundary == null) == (collected == null)); if(boundary == null) return; using(Graphics g = this.m_Display.CreateGraphics()) { g.IntersectClip(this.m_DisplayBounds); Debug.Assert(data.Count % data.PacketPropertyCount == 0); Point[] points = new Point[data.Count / data.PacketPropertyCount]; for(int i = 0, j = 0; i < data.Count; i += data.PacketPropertyCount, j++) { // Create a Point using the X and Y coordinates of the data, which are defined to be at offsets [i] and [i+1]. points[j] = new Point(data[i], data[i+1]); } // Add the points to the list of collected points, *before* they are converted to pixel coordinates. collected.AddRange(points); this.m_Renderer.InkSpaceToPixel(g, ref points); PointF previous; int start; if(this.m_SelectionPreviousPointTable.ContainsKey(data.Stylus.Id)) { previous = ((PointF) this.m_SelectionPreviousPointTable[data.Stylus.Id]); start = 0; } else { m_SelectionPreviousPointTable[data.Stylus.Id] = previous = (boundary.PointCount > 0) ? Point.Round(boundary.PathPoints[boundary.PointCount - 1]) : points.Length > 0 ? points[0] : Point.Empty; start = (boundary.PointCount > 0) ? 0 : 1; } RectangleF bounds = new RectangleF(previous, SizeF.Empty); for(int i = start; i < points.Length; i++) { PointF point = points[i]; boundary.AddLine(previous, (previous = point)); ExpandRectangle(ref bounds, previous); } if(points.Length > 0) this.m_SelectionPreviousPointTable[data.Stylus.Id] = previous; int inflate = ((int) Math.Ceiling(SelectionBoundaryPenOuter.Width)); bounds.Inflate(inflate, inflate); g.IntersectClip(bounds); DrawSelectionBoundary(g, boundary); } } }
/// <summary> /// Modifies packet data according to the current <see cref="Transform">transform matrix</see>. /// </summary> /// <param name="data">The data to modify.</param> private void ModifyPacketData(StylusDataBase data) { // Create an array of points, one for each packet. Point[] points = new Point[data.Count / data.PacketPropertyCount]; int i, j; // Declare outside the loop for the Debug.Assert below. // Convert the X and Y coordinates from the packet data into Points. for(i = 0, j = 0; i < data.Count; i += data.PacketPropertyCount, j++) { // X and Y are always the first two elements in a packet. points[j] = new Point(data[i], data[i+1]); } // We should have exactly as many points as there were packets in the array. Debug.Assert(i == data.Count); Debug.Assert(j == points.Length); // Apply the transformation to the points. this.m_InverseTransform.TransformPoints(points); // Store the modified X and Y coordinates back into the data. // The new values will be seen by all subsequent IStylus(A)SyncPlugins. for(i = 0, j = 0; i < data.Count; i += data.PacketPropertyCount, j++) { data[i] = points[j].X; data[i+1] = points[j].Y; } }
/// <summary> /// Erases strokes that overlap the cursor. /// </summary> /// <param name="sender">The real time stylus associated with the notification</param> /// <param name="data">The notification data</param> /// <seealso cref="EraserPlugin.StylusDown"/> /// <seealso cref="EraserPlugin.Packets"/> /// <seealso cref="EraserPlugin.StylusUp"/> private void HandlePackets(RealTimeStylus sender, StylusDataBase data) { using (Synchronizer.Lock(this)) { // Ignore the strokes if the eraser stylus is not selected, // and if the stylus is not inverted. if (this.Eraser == null && !data.Stylus.Inverted) { return; } // Ignore if a touch input if (m_TouchSupported) { try { if (sender.GetTabletFromTabletContextId(data.Stylus.TabletContextId).DeviceKind == TabletDeviceKind.Touch) { return; } } catch { m_TouchSupported = false; } } // Ignore the strokes if no ink sheet is selected. InkSheetModel sheet = this.InkSheetModel; if (sheet == null) { return; } // Convert the X and Y coordinates of the data, // which are defined to be at offsets [i] and [i+1], // to an array of Points. Debug.Assert(data.Count % data.PacketPropertyCount == 0); Point[] points = new Point[data.Count / data.PacketPropertyCount]; for (int i = 0, j = 0, il = data.Count, inc = data.PacketPropertyCount; i < il; i += inc, j++) { points[j] = new Point(data[i], data[i + 1]); } // Convert the ink points to pixels so we can // ignore the ones that are outside the slide view area. // This is done all at once to conserve resources used by the Graphics object. Point[] pixels = points; using (Synchronizer.Lock(this.m_Display.SyncRoot)) using (Graphics g = this.m_Display.CreateGraphics()) this.m_Renderer.InkSpaceToPixel(g, ref pixels); // Prevent anyone else from accessing the ink concurrently. using (Synchronizer.Lock(this.InkSheetModel.Ink.Strokes.SyncRoot)) { // Iterate through each point through which the cursor has passed. for (int i = 0, il = points.Length; i < il; i++) { // Don't erase anything when the cursor is outside of the // slide viewing area. This prevents users from accidentally // erasing strokes they can't see, especially when using the // slide zoom feature. if (!this.m_DisplayBounds.Contains(pixels[i])) { continue; } // Find all strokes within some radius from the cursor. Strokes erased = sheet.Ink.HitTest(points[i], ERASER_RADIUS); // If any strokes were found, erase them. if (erased.Count > 0) { // Get the list of stroke IDs in order to send an event int[] ids = new int[erased.Count]; for (int j = 0; j < ids.Length; j++) { ids[j] = erased[j].Id; } // We must first warn listeners that the strokes are about to // be deleted, because after they're deleted, no information // about them can be recovered. This is used to send // network events and to store undo information. sheet.OnInkDeleting(new StrokesEventArgs(ids)); // Delete the erased strokes. sheet.Ink.DeleteStrokes(erased); // Inform listeners that the strokes have actually been deleted. // This causes slide displays to refresh. sheet.OnInkDeleted(new StrokesEventArgs(ids)); } } } } }
/// <summary> /// Helper method that constrains packet data within /// a specified input rectangle. /// </summary> /// <param name="data">The data to modify.</param> private void ModifyPacketData(StylusDataBase data) { // For each packet in the packet data, check whether // its x,y values fall outside of the specified rectangle. // If so, replace them with the nearest point that still // falls within the rectangle. for (int i = 0; i < data.Count ; i += data.PacketPropertyCount) { // packet data always has x followed by y followed by the rest int x = data[i]; int y = data[i+1]; // Constrain points to the input rectangle x = Math.Max(x, rectangle.Left); x = Math.Min(x, rectangle.Right); y = Math.Max(y, rectangle.Top); y = Math.Min(y, rectangle.Bottom); // If necessary, modify the x,y packet data if (x != data[i]) { data[i] = x; } if (y != data[i+1]) { data[i+1] = y; } } }
private void HandlePackets(StylusDataBase data) { using (Synchronizer.Lock(this)) { GraphicsPath boundary = ((GraphicsPath)this.m_SelectionBoundaryTable[data.Stylus.Id]); ArrayList collected = ((ArrayList)this.m_SelectionPointsTable[data.Stylus.Id]); Debug.Assert((boundary == null) == (collected == null)); if (boundary == null) { return; } using (Graphics g = this.m_Display.CreateGraphics()) { g.IntersectClip(this.m_DisplayBounds); Debug.Assert(data.Count % data.PacketPropertyCount == 0); Point[] points = new Point[data.Count / data.PacketPropertyCount]; for (int i = 0, j = 0; i < data.Count; i += data.PacketPropertyCount, j++) { // Create a Point using the X and Y coordinates of the data, which are defined to be at offsets [i] and [i+1]. points[j] = new Point(data[i], data[i + 1]); } // Add the points to the list of collected points, *before* they are converted to pixel coordinates. collected.AddRange(points); this.m_Renderer.InkSpaceToPixel(g, ref points); PointF previous; int start; if (this.m_SelectionPreviousPointTable.ContainsKey(data.Stylus.Id)) { previous = ((PointF)this.m_SelectionPreviousPointTable[data.Stylus.Id]); start = 0; } else { m_SelectionPreviousPointTable[data.Stylus.Id] = previous = (boundary.PointCount > 0) ? Point.Round(boundary.PathPoints[boundary.PointCount - 1]) : points.Length > 0 ? points[0] : Point.Empty; start = (boundary.PointCount > 0) ? 0 : 1; } RectangleF bounds = new RectangleF(previous, SizeF.Empty); for (int i = start; i < points.Length; i++) { PointF point = points[i]; boundary.AddLine(previous, (previous = point)); ExpandRectangle(ref bounds, previous); } if (points.Length > 0) { this.m_SelectionPreviousPointTable[data.Stylus.Id] = previous; } int inflate = ((int)Math.Ceiling(SelectionBoundaryPenOuter.Width)); bounds.Inflate(inflate, inflate); g.IntersectClip(bounds); DrawSelectionBoundary(g, boundary); } } }