In OpenGL I can do this:
glOrtho(-10, 10, -10, 10, -1, 1)
to establish a coordinate system where x ranges from -10 to 10 and y ranges from -10 to 10.
Is there a way to change the coordinate system of a canvas in WPF?
In OpenGL I can do this:
glOrtho(-10, 10, -10, 10, -1, 1)
to establish a coordinate system where x ranges from -10 to 10 and y ranges from -10 to 10.
Is there a way to change the coordinate system of a canvas in WPF?
I created an extension method on Canvas that allows you to set a Cartesian coordinate system. An example invocation is:
canvas.SetCoordinateSystem(-10, 10, -10, 10)
That will set the coordinate system of canvas
so that x goes from -10 to 10 and y goes from -10 to 10.
Here's the extension method:
public static Canvas SetCoordinateSystem(this Canvas canvas, Double xMin, Double xMax, Double yMin, Double yMax)
{
var width = xMax - xMin;
var height = yMax - yMin;
var translateX = -xMin;
var translateY = height + yMin;
var group = new TransformGroup();
group.Children.Add(new TranslateTransform(translateX, -translateY));
group.Children.Add(new ScaleTransform(canvas.ActualWidth / width, canvas.ActualHeight / -height));
canvas.RenderTransform = group;
return canvas;
}
Here's a demo program which plots Sin(x) on a canvas along with the X and Y axes:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace CanvasScale
{
public static class CanvasUtils
{
public static Canvas SetCoordinateSystem(this Canvas canvas, Double xMin, Double xMax, Double yMin, Double yMax)
{
var width = xMax - xMin;
var height = yMax - yMin;
var translateX = -xMin;
var translateY = height + yMin;
var group = new TransformGroup();
group.Children.Add(new TranslateTransform(translateX, -translateY));
group.Children.Add(new ScaleTransform(canvas.ActualWidth / width, canvas.ActualHeight / -height));
canvas.RenderTransform = group;
return canvas;
}
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var canvas = new Canvas();
Content = canvas;
SizeChanged += (s, e) => canvas.SetCoordinateSystem(-10, 10, -10, 10);
canvas.Children.Add(new Line() { X1 = -10, Y1 = 0, X2 = 10, Y2 = 0, Stroke = Brushes.Black, StrokeThickness = 0.2 });
canvas.Children.Add(new Line() { X1 = 0, Y1 = -10, X2 = 0, Y2 = 10, Stroke = Brushes.Black, StrokeThickness = 0.2 });
var polyline = new Polyline()
{
Stroke = Brushes.BurlyWood,
StrokeThickness = 0.1,
Points = new PointCollection()
};
for (var x = -10.0; x <= 10.0; x += 0.1)
polyline.Points.Add(new Point(x, Math.Sin(x)));
canvas.Children.Add(polyline);
}
}
}
Here's what it looks like:
OrthographicCamera
provides an orthogonal projection instead of the more common perspective projection (PerspectiveCamera
).
To establish the 'range' of coordinates that are visible from the camera you will probably have to fiddle with the Width
and Position
properties.
As long as you do not also set the Canvas.Top
, Canvas.Left
, Canvas.Right
or Canvas.Bottom
properties, you could set the LayoutTransform or RenderTransform property of each child in the Canvas to an appropriate TransformGroup or MatrixTransform:
Rect viewport = new Rect(-10, -10, 20, 20);
double scaleX = ActualWidth / viewport.Width;
double scaleY = ActualHeight / viewport.Height;
RenderTransform = new MatrixTransform(
scaleX, 0d, 0d, scaleY, -viewport.X * scaleX, -viewport.Y * scaleY);