2

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?

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
dharmatech
  • 8,979
  • 8
  • 42
  • 88
  • How are you going to position elements in that Canvas? The `Top`, `Left`. `Right` and `Bottom` properties would loose their meaning. – Clemens Oct 26 '12 at 06:58

3 Answers3

11

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:

enter image description here

dharmatech
  • 8,979
  • 8
  • 42
  • 88
  • 1
    Note that canvas.ActualHeight can be (and is) zero right after InitializeComponent(), so `0 / x = 0` and you have set your ScaleTransform to {0;0}. And that can be confusing, becouse canvas visualy disappear... Solution: [force Measure && Arrange](http://stackoverflow.com/questions/1695101/why-are-actualwidth-and-actualheight-0-0-in-this-case#answer-1695518) – Jan 'splite' K. Aug 15 '13 at 19:49
1

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.

ravuya
  • 8,586
  • 4
  • 30
  • 33
1

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);
Clemens
  • 123,504
  • 12
  • 155
  • 268
  • Hi Clemens. I ended up doing something along these lines. I added an [answer](http://stackoverflow.com/questions/13079610/equivalent-of-glortho-in-wpf/13093518#13093518). – dharmatech Oct 26 '12 at 19:55