The background to my problem is that I have a bunch of geometries (huge amount, think map over a larger area) split across multiple wpf geometry instances (originally they were PathGeometry, but to reduce memory usage I pre-process them and create StreamGeometries during load). Now what I want to do is to generate tiles from these geometries.
Basically I would like to take a larger geometry object and "cut out" a rectangle of it (my tile) so I get several smaller geometries. Something like the image below:
Notice that I want the result to be a new geometry, not a rendering. I know I can achieve the visual result by applying a clip to a UIElement or by pushing a clip to a drawingvisual.
I've tried using Geometry.Combine with one of the arguments being the clip rectangle, but I can't get it to do what I want (I typically only get the clip rect back, or an empty geometry, depending on which combine mode I use).
Alternatively, if this cannot be done using WPF, is there any other (third party is ok) general purporse geometry API for .NET that can do these kind of operations? Or maybe this can be implemented using other parts of the WPF geometry API?
Code shows the bottom right rectangle like in your "smaller tiles" visualisation:
var geometry = MyOriginalPath.Data.Clone();
var bounds = geometry.Bounds;
var rectangleGeometry = new RectangleGeometry(bounds);
var halfWidth = bounds.Width * 0.5;
var halfHeight = bounds.Height * 0.5;
var bottomQuarter = new RectangleGeometry(
new Rect(bounds.X + halfWidth, bounds.Y + halfHeight,
halfWidth, halfHeight));
var combinedGeometry = new CombinedGeometry(GeometryCombineMode.Exclude,
rectangleGeometry, bottomQuarter);
combinedGeometry = new CombinedGeometry(GeometryCombineMode.Exclude,
geometry, combinedGeometry);
MyBottomQuarterPath.Data = combinedGeometry;
Regards Dave
Related
I am wondering if someone has managed to override the default behaviour of the WPF shape rendering when applying a scaletransform to the shape. The default behaviour transforms the entire shape drawing, including strokes, but I would like to only scale the geometry. The difficulty is that my shapes reside in a visual hierarchy with render transforms applied on different levels (sort of like a 2D scene graph but a WPF visual tree), this I cannot change(!). I have read in different places that it might be possible to create a custom shape to cancel out the transform for the render transform and put it on the geometry instead. At the moment I have something like:
public sealed class MyPath : Shape
{
// This class has a Data property of type Geometry just like the normal Path class
protected override Geometry DefiningGeometry
{
get
{
Geometry data = Data;
if (data == null)
{
data = Geometry.Empty;
}
return data;
}
}
protected override void OnRender(DrawingContext drawingContext)
{
Transform tr = RenderedGeometry.Transform;
Geometry geomToDraw = RenderedGeometry.Clone();
geomToDraw.Transform = new MatrixTransform(tr.Value * tr.Value);
Matrix trInv = tr.Value; trInv.Invert();
drawingContext.PushTransform(new MatrixTransform(trInv));
drawingContext.DrawGeometry(Brushes.Transparent, new Pen() { Brush = Brushes.Black, Thickness = 1 }, geomToDraw);
}
}
As is clearly evident, I am quite new to this and the above code is probably completely messed up. I was trying to transfer the matrix to the geometry without changing the final resulting geometry transform, hence the tr.Value*tr.Value and trInv. But it does not work as I want it to. I know this transfer transform technique works in theory because I tried it out with constant transforms (testing to set Geometry.Transform to scale x with 4 and pushing a transform to scale x with 0.25 worked fine but the resulting shape drawing did not seem to apply stretch=fill, which I rely upon). So there must be something that I am missing with the render transforms.
The test scenario that is not working is this:
I apply a render scale transform with scaleX=4 and scaleY=1 in xaml.
The built-in Path class scales the entire drawing so that strokes 4 times wider in the x direction than in the y direction.
I want MyPath to scale the geometry only, not the strokes. <- THIS IS NOT WORKING!
What happens is: The geometry gets scaled correctly, the strokes get scaled by 4 in the x direction and by slightly less than 4 in the y direction. What is wrong? I have a feeling that I should not be working solely with RenderedGeometry.Transform but what should I use instead? I need to incorporate the render transform and the stretch=fill on the shape. My render transforms hierarchy may contain a mix of scales, rotations and translations so the solution must be general enough to handle any transform, not just axis-aligned scaling.
Note: I know it is bad to create the geometry in OnRender but I want to get it working before spending time cleaning it up.
By the way, I read this post:
Invariant stroke thickness of Path regardless of the scale
The problem as stated before is that I do have to take render transforms into consideration and I am not sure of how to adapt that solution to work with them.
If I understand the question correctly you want to cancel out the effect of the render transformation on the pen but not on the geometry.
This could be accomplished by getting the transformation of the control relative to the item from which you want to cancel the transform an using its inverse to cancel out the effect on the pen. (so for example if you have the hierarchy P1/P2/P3/UrShape, and P1,P2,P3 all have transforms on them and you want all of them to not affect your pen, you will need to obtain the transform of P1 relative to UrShape). Then you could reapply the transform to just your shape.
var brush = new SolidColorBrush(Colors.Red);
var pen = new Pen(brush, 5);
//Instead of the direct parent you could walk up the visual tree to the root from where you want to cancel the transform
var rootTransform = (MatrixTransform)this.TransformToAncestor((Visual)this.Parent);
var inverserRootTransform = (MatrixTransform)rootTransform.Inverse;
//We cancel out the transformation from the parent
drawingContext.PushTransform(inverserRootTransform);
var renderGeometry = this.Geometry.Clone();
// We apply the parent transform to the shape only, and group it with the original transform that was present on the shape
// we do this to honor any transformation that was set on the shape.
renderGeometry.Transform = new TransformGroup()
{
Children =
{
rootTransform,
this.Geometry.Transform
}
};
//We draw the shape, the pen size will have the correct size since we canceled out the transform from the parent
// but the shape now has the transformation directly on it.
drawingContext.DrawGeometry(brush, pen, renderGeometry);
drawingContext.Pop();
I'm trying to implement a function that takes a System.Drawing.Bitmap object and renders it on a WPF Canvas. The bitmap has to be cropped and joined a few times before rendering.
Environment: WPF application running on .NET 3.5 SP1
Input: System.Drawing.Bitmap object, of size 800x600 and pixel format RGB24
Goal: to display an image which is composed of two stripes of the input bitmap (on one line). The stripes are two bitmap halves - (0,0,800,300) and (0,300,800,600). Later on I want to be able to scale the image up or down.
I've already implemented a solution with GDI and Graphics.DrawImage (that renders into a Bitmap object), but I want to improve performance (this function could be called 30 times per second).
Is there a faster way to implement this with WPF, assuming I want to render the image on a WPF window?
The best solution I found so far is using WriteableBitmap, something like this:
void Init()
{
m_writeableBitmap = new WriteableBitmap(DesiredWidth, DesiredHeight, DesiredDpi, DesiredDpi, PixelFormats.Pbgra32, null);
{
void CopyPixels(System.Drawing.Bitmap frame, Rectangle source, Point destBegin)
{
var bmpData = frame.LockBits(source, ImageLockMode.ReadOnly, frame.PixelFormat);
m_writeableBitmap.Lock();
var dest = new Int32Rect(destBegin.X, destBegin.Y, bmpData.Width, bmpData.Height);
m_writeableBitmap.WritePixels(dest, bmpData.Scan0, bmpData.Stride * bmpData.Height, bmpData.Stride);
m_writeableBitmap.Unlock();
frame.UnlockBits(bmpData);
}
CopyPixels would be called twice for the use case I described in my question (two stripes).
I'm writing some WPF code involving Adorners. I'm using Josh Smith's UIElementAdorner.cs (found in the project on his Infragistics Blog). I'm adorning with a blurb of information text. I need to place my adorner smartly, so that it does not clip off the screen.
What's the best way to find out if I'm going to clip?
I'm using the following code to create and place my adorner. I have a funny feeling that basing whether or not I'll clip on the AdornerLayer isn't the best option.
var infoBubble = new InfoBubble {InformationText = #"I like cheese."};
var adornedElementRect = new Rect(Target.DesiredSize);
var layer = AdornerLayer.GetAdornerLayer(Target);
var adorner = new UiElementAdorner<Control>(Target) { Child = infoBubble };
adorner.Measure(new Size(layer.ActualWidth, layer.ActualHeight));
var adornerRect = new Rect(adorner.DesiredSize);
var top = -1*(adornerRect.Height);
var left = adornedElementRect.Width/2;
// Using layer to judge where to place the adorner
var upperLeftPoint = Target.TranslatePoint(new Point(left, top), layer);
var lowerRightPoint = Target.TranslatePoint(new Point(left + adornerRect.Width,
top + adornerRect.Height), layer);
if (upperLeftPoint.Y < 0) top -= upperLeftPoint.Y; // shift down by Y
if (lowerRightPoint.X > layer.ActualWidth)
left -= (lowerRightPoint.X - layer.ActualWidth); // shift left
Keep in mind that this code is contained in a TargetedTriggerAction that designers (aka users of Blend) are expected to use when they want information blurbs above certain UI elements. Thus, this code will know very little about the element to be adorned or its environment.
Yes, is the best answer I can discern.
According to further reading and some experimentation, when calling GetAdornerLayer we receive the lowest layer above the target control in the visual tree. This means we could get a layer below the AdornerDecorator's layer defined in a Window's template. That lower AdornerDecorator could have ClipToBounds="True" (I have no idea why, but it could).
Knowing this information, I can be relatively certain that the AdorneLayer I'm drawing into is the best bounding box for whatever I'm drawing. I could have the ability to draw outside this box (for example if ClipToBounds were False on a AdornerDecorator lower than the Window's), but I shouldn't count on that ability.
I am working on a paint like application in wpf.I want the users to be able to add some drawings over images or plain surfaces.Also i want to draw some basic shapes like line,ellipse or a rectangle.I am trying to work with an inkcanvas,where i can do freehand drawing,but i cant draw shapes like in paint.Can anyone guide me and provide some clues on how to do it.Please help me on this.Any inputs will be greatly appreciated.
There are two sorts of collections in an InkCanvas:
Strokes, which are composed of StylusPoints and defined by DrawingAttributes. That's what the Ink is, as drawn by a mouse or stylus.
The other is Children, which can contain FrameworkElements. Ellipse, for instance, is a Shape is a FrameworkElement.
Try playing around with yourCanvas.Children.Add(ellipse) and see how you go. There is certainly no reason to shy away from the InkCanvas just because you also want to use predefined shapes.
It's worth pointing out, though, that the InkCanvas's little brother, the InkPresenter, does NOT have a Children property. And Silverlight only has that one.
WPF provides a Shape class that includes prebuilt methods that you can draw shapes with. Don't use the inkcanvas and instead draw directly to a canvas.
Here http://ciintelligence.blogspot.com/2011/07/silverlight-drawing-tool-silver-draw.html
you can find better control which improved SilverDraw control with extra features:
Freatures are:
* You can draw basic shapes and also can draw using freehand pencil.
* You can erase drawing.
* You can undo and redo drawing.
* Can save drawing as jpeg in server side.
Here is a simple implementation:
public void drawCircleAp(Double EHeight, Double EWidth, InkCanvas surface)
{
Ellipse e1 = new Ellipse();
e1.Width = EWidth;
e1.Height = EHeight;
var brush = new SolidColorBrush();
brush.Color = Color.FromArgb(100, 0, 0, 0);
e1.Stroke = brush;
e1.StrokeThickness = 4;
surface.Children.Add(e1);
}
I am new to Silverlight. Just created my first application that shows deepzoom images.
Looking for some pointers how to display vector graphics in Silverligth. The graphics are all in 2D and is a series of lines (x1y1, x2y2), points (xy), basic shapes. The data is available in ASCII text files.
What is the way(s) to read the data from files and draw in SL? Do I need to convert / translate the vector objects into images (XAML) first? Where to start?
The ideal case is that all vector obects should be selectable either programmatically or by user actions.
Thanks,
Val
There is no direct drawing API to my knoweldge, but you can add the values seperately by adding various shapes to the visual tree.
The code you are looking for will likely involve the Path class and, in turn, PathFigure and PolyLineSegment (or possibly LineSegment).
Below is some code that draws a square:
PolyLineSegment segment = new PolyLineSegment();
segment.Points.Add(new Point(0, 50));
segment.Points.Add(new Point(50, 50));
segment.Points.Add(new Point(50, 0));
segment.Points.Add(new Point(0, 0));
PathFigure figure = new PathFigure()
{
StartPoint = new Point(0, 0)
};
figure.Segments.Add(segment);
PathGeometry geometry = new PathGeometry()
{
Figures.Add(pathFigure)
};
Path path = new Path()
{
Stroke = new SolidColorBrush(Colors.Black),
StrokeThickness = 2,
Data = pathGeometry
};
// To render, the Path needs to be added to the visual tree
LayoutRoot.Children.Add(path);
Edit If the data in the ASCII text files cannot change at runtime, it might be worth investigating writing a script that transforms the files into XAML so it can be compiled.
First of you have the issue of actually getting access to the files.
Getting the file content
If you have these files held somewhere serverside then you would use WebClient to fetch the file using DownloadStringAsync.
On the other hand if the user is to open a file locally then you need use the OpenFileDialog class to ask them to open the file and then use OpenText on the FileInfo object that OpenFileDialog provides to read the string data.
Parsing
Well its your format so you'll have to code that yourself.
__Generating UI elements_
You will not have to convert it to Xaml. Since you want these vector items to be individually selectable elements then you probably want to use the set of Shape types found in the System.Windows.Shapes namely, Elipse, Line, Path, Polygon, Polyline and Rectangle.
No doubt the format in question has someway to define the position of these elements relative to a fixed 0,0 point. Hence the best panel to use to display these is a Canvas.
You would read through each Vectored item, select create an instance of one of the appropriate shapes set its properties based on the data in the item. You would need to determine its correct location within a Canvas and use the Canvas.Left and Canvas.Top attached properties. The add the shape to the Children collection of the Canvas.