Vector graphics in silverlight - silverlight

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.

Related

Animate a combined geometry along a path

I have WPF/VB application that animates an ellipse geometry along a path using point animation. I used PointAnimationUsingPath and a Storyboard as per this MSDN example and it works great.
I now want to show a number inside the ellipse. To do this I created a combined geometry and set my ellipse as geometry1. I then created a formattedtext(...).buildgeometry for my number and set that as geometry2. Like this:
Dim CarGeo AS New CombinedGeometry()
CarGeo.Geometry1 = New EllipseGeometry(StartPoint, 5, 5)
CarGeo.Geometry2 = New FormattedText(carIndex.ToString, System.Globalization.CultureInfo.GetCultureInfo("en-us"), Windows.FlowDirection.LeftToRight, New Typeface("Veranda"), 7, Brushes.White).BuildGeometry(New Point(StartPoint.X - 4, StartPoint.Y - 4))
The resulting geometry is exactly what I wanted.
The problem is I don't seem to be able to animate this geometry along my path because unlike an ellipse there is no center property in a combined geometry to set the targeted property to on the StoryBoard.
' Create a PointAnimationgUsingPath to move the car along the animation path.
cpAnimation = New PointAnimationUsingPath
cpAnimation.PathGeometry = pgSectorPath(intSector)
cpAnimation.Duration = timDuration
' Set the animation to target the Center property of the EllipseGeometry
Storyboard.SetTargetName(cpAnimation, "CarGeo")
Storyboard.SetTargetProperty(cpAnimation, New PropertyPath(EllipseGeometry.CenterProperty))
Is there I property in a combined geometry that I can use for the animation?
If not can I wrap the geometry in something else that can be animated?
I'm very new to WPF and have wasted way too much time searching for an answer to this. Any help would be greatly appreciated.
Just wrap the CombinedGeometry into a Path object, as in the MS example:
http://msdn.microsoft.com/en-us/library/system.windows.media.combinedgeometry(v=vs.110).aspx

WPF shape geometry scaling without affecting stroke

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();

How to improve Canvas rendering performance?

I have to draw a lot of Shape (about 1/2 hundred thousand) as [Canvas][2]'s childrens. I make this in my WPF application dividing work in two parts: first thing I create shapes by setting the properties of each of them (like Margin, Fill, Width, etc...), after I add shapes as Canvas's children.
MyCanvas.Children.Add(MyShape)
Now i want to improve the performance of the second part, because when i draw the shapes my application is blocked for a long period of time. So i tried to use the Dispatcher and its method [BeginInvoke][4] with different [priorities][5]: only if I use the Background priority the main application does not block, otherwise the application remains blocked and the "picture" is not displayed until all shapes are added to my Canvas, but if I use the Background priority obviously everything is slower. I also tried to create a new thread instead of using the Dispatcher, but there was no significant change.
How can I fix this problem, and generally improve the performance of my application when I add my shapes to Canvas?
Thanks.
Need to use Visual objects instead of Shape; in particular, as suggested, DrawingVisual: a visual object that can be used to render vector graphics. In fact, as written in the MSDN library:
DrawingVisual is a lightweight drawing class that is used to render shapes, images, or text. This class is considered lightweight because it does not provide layout, input, focus, or event handling, which improves its performance. For this reason, drawings are ideal for backgrounds and clip art.
So, for example, to create a DrawingVisual that contains a rectangle:
private DrawingVisual CreateDrawingVisualRectangle()
{
DrawingVisual drawingVisual = new DrawingVisual();
// Retrieve the DrawingContext in order to create new drawing content.
DrawingContext drawingContext = drawingVisual.RenderOpen();
// Create a rectangle and draw it in the DrawingContext.
Rect rect = new Rect(new System.Windows.Point(160, 100), new System.Windows.Size(320, 80));
drawingContext.DrawRectangle(System.Windows.Media.Brushes.LightBlue, (System.Windows.Media.Pen)null, rect);
// Persist the drawing content.
drawingContext.Close();
return drawingVisual;
}
In order to use DrawingVisual objects, you need to create a host container for the objects. The host container object must derive from the FrameworkElement class, which provides the layout and event handling support that the DrawingVisual class lacks. When you create a host container object for visual objects, you need to store the visual object references in a VisualCollection.
public class MyVisualHost : FrameworkElement
{
// Create a collection of child visual objects.
private VisualCollection _children;
public MyVisualHost()
{
_children = new VisualCollection(this);
_children.Add(CreateDrawingVisualRectangle());
// Add the event handler for MouseLeftButtonUp.
this.MouseLeftButtonUp += new System.Windows.Input.MouseButtonEventHandler(MyVisualHost_MouseLeftButtonUp);
}
}
The event handling routine can then implement hit testing by invoking the HitTest method. The method's HitTestResultCallback parameter refers to a user-defined procedure that you can use to determine the resulting action of a hit test.
Agreed that if you want to draw millions of elements, you simply can't do it in WPF. WriteableBitmapEx as mentioned is a good alternative.
See this related question which goes into depth on high performance graphics in WPF and the alternatives available.
If you simply must use Canvas, check out this ZoomableApplication2 - A million items. This is a Canvas based demo which makes heavy use of Virtualization to get reasonable performance with 1,000,000 UIElements on a Canvas.
That's a lot of UIElements and probably isn't going to give the kind of performance you're looking for. Do you need to be able to interact with each of the elements you're rendering? If not, I would highly recommend looking into using WriteableBitmap instead. If you need to draw shapes and don't want to create all that logic yourself (who would want to?), check out the WriteableBitmapEx project over on CodePlex.
This may be somewhat unrelated, and I apologize if you feel this way, but in the hopes that it can shed some light for other users, I'll share this tidbit.
We had some performance issues with a Canvas control used for capturing signatures. The capture was very jagged, and we couldn't draw curved lines as a result. It turned out to be related to a style was was generating drop-shadows on the UI elements. Disabling the drop-shadow effect solved our problem.

WPF 3D - Positioning Visual3D elements in a 3D scene using nested Model3DGroup transforms?

I have a 3D scene where my 3D models are being loaded in the code behind from XAML files.
Each model is comprised of a tree of nested Model3DGroups each of which has various transformations applied to it to position and orient the next subcomponent of the model in the tree. This model is then used as the content of a ModelVisual3D so that it can be displayed to the screen.
I want to attach a child ModelVisual3D to a 'parent' ModelVisual3D. This child ModelVisual3D needs to use all of the nested transformations of the parent ModelVisual3D.Content to correctly position and orient itself in the virtual space. For example, the first ModelVisual3D is a robot arm which has various moveable joints and I want to attach a tool on the end of this arm--another ModelVisual3D. How can I access this composite transform from the parent ModelVisual3Ds content property to allow me to position the next ModelVisual3D correctly relative to its parent?
As you have no doubt observed, when you group Model3Ds in a Model3DGroup the Transform properties of the children combine with those of the parent.
It sounds like you are asking how to compute the net transform down to a particular Model3D within a tree of Model3Ds that make up what you are calling your "model". To do this you need to know (or be able to scan and discover) the path from your root Model3DGroup down to the Model3D you want to find the transform for.
Once you have this path, all that is required is to combine the Transform properties at each level. To do this, simply construct a Transform3DGroup and add the individual transforms to it.
For example, if your robot arm has Model3D components named "UpperArm", "LowerArm", and "Hand", and you wanted to find out the position and angle of the hand you might do:
var combined = new Transform3DGroup();
combined.Children.Add(UpperArm.Transform);
combined.Children.Add(LowerArm.Transform);
combined.Children.Add(Hand.Transform);
Now you can find the (0,0,0) location on the hand as follows:
combined.Transform(new Point3D(0,0,0));
Similarly you can find other points and use them to position your other ModelVisual3D.

Output control in Bitmap

I want to implement previews/thumbnails in my project, therefor i need the graphical output of a control as bitmap. I have a third party control which loads documents and displays them. Is it possible to fetch the output of an control and store it in a bitmap object without adding it to the UI? And If how?
Edit: I probably should said that before, but i don't know if that's important. The ThirdParty Control is an OCX(ActiveX control).
in a Form do:
Bitmap myBitmap = new Bitmap(button1.Width, button1.Height);
// button1.Draw..., not 'this.Draw...'!
button1.DrawToBitmap(myBitmap, button1.DisplayRectangle);
myBitmap.Save(#"C:\test.bmp");

Resources