2

The origin (0,0) of an HTML canvas is the upper left (see image below), where positive x coordinates go to the right, and positive y coordinates go down.

enter image description here

However, the origin in mathematics is the "center," and positive y goes up via the Cartesian coordinate system (see below).

enter image description here

I played with offset and scale of the stage in KonvaJS to reflect a Cartesian system with X-max of 10, Y-max of 10, X-min of -10 and Y-min of -10. However, my current solution has the quadrants flipped upside down (See below). Any suggestions?

enter image description here

Current JavaScript approach: JSFiddle

var Width = 500;
var Height = 500;
var minX = -12; var maxX = 12;
var minY  = -12; var maxY  = 12;
var rangeX  = maxX - minX;
var rangeY  = maxY - minY;
var scaleX  = Width / rangeX;
var scaleY = Height  / rangeY;

var stage = new Konva.Stage({
container: 'container',
width: Width,
height: Height,
scaleX: scaleX,
scaleY: scaleY,
offset: {
 x: -12,
 y: -12
}   
});  

var layer = new Konva.Layer();
stage.add(layer);

var rect = new Konva.Rect({
  x: -12,
  y: -12,
  width: 24,
  height: 24,    

  stroke: 'black',
  strokeWidth: 0.1
});  
layer.add(rect);

var position = new Konva.Text(
{
 fontSize: 0.7,
 x: -11,
 y: -11   
});
 
layer.add(position);

var t0 = new Konva.Text(
{
fontSize: 1,
x: 0,
y: 0,
text: 'Center'

});   
var t1 = new Konva.Text(
{
fontSize: 0.7,
x: 5,
y: 5,
text: 'Top\nRight',
fill: 'blue'
});   
var t2 = new Konva.Text(
{
fontSize: 0.7,
x: -5,
y: 5,
text: 'Top\nLeft',
fill: 'blue'
});   
var t3 = new Konva.Text(
{
fontSize: 0.7,
x: 5,
y: -5,
text: 'Bottom\nRight',
fill: 'blue'
});   
var t4 = new Konva.Text(
{
fontSize: 0.7,
x: -5,
y: -5,
text: 'Bottom\nLeft',
fill: 'blue'   
});   
layer.add(t0); 
layer.add(t1);
layer.add(t2);
layer.add(t3);
layer.add(t4); 

 var yaxis = new Konva.Arrow({
  points: [0, 11, 0, -11],
  pointerLength: 0.3,
  pointerWidth: 0.2,
  pointerAtBeginning: true,
  fill: 'green',
  stroke: 'green',
  strokeWidth: 0.1
});
layer.add(yaxis);

var xaxis = new Konva.Arrow({
  points: [11, 0, -11,0],
  pointerLength: 0.3,
  pointerWidth: 0.2,
  pointerAtBeginning: true,
  fill: 'green',
  stroke: 'green',
  strokeWidth: 0.1
});
layer.add(xaxis);

var circle = new Konva.Circle({
  x: 0,
  y: 5,
  radius: 0.5,
  fill: 'purple',
  stroke: 'purple',
  strokeWidth: 0,
  draggable: true
});  

layer.add(circle);

layer.draw();

function updateText(e) {
position.text('(' + Math.round(e.target.x()) + ', ' + Math.round(e.target.y()) + ')');
layer.batchDraw();
}

circle.on('dragmove', updateText);  
  
<script src="https://unpkg.com/konva@2.4.2/konva.min.js"></script>
<body>
 <div id="container"></div>
</body>

Update:

As suggested by Andrew Morton, setting:

scaleY = -Height / rangeY;

Has the right idea, but it required a change in the stage offset to:

      offset: {
          x: -12,
          y: 12           
      }

Which resulted in having the correct quadrants but all text has been inverted (See below). Any other suggestions?

enter image description here

lucidgold
  • 4,432
  • 5
  • 31
  • 51
  • Y "increases" to the "south" on a canvas element. Looks like you might have overlooked that. – Randy Casburn Jan 30 '19 at 16:21
  • @RandyCasburn: I believe I have it correct. – lucidgold Jan 30 '19 at 16:47
  • In Windows GDI, Postscript, and similar, there are 'world transforms' which allow you to apply whatever matrix transform to the drawing space. The drawing engine then runs the transform on any point you provide, so you can just get on and set out your points without having to run your own conversions. I have also seen an implementation of a topDown setting that flipped the Y axis, which is really all you need. Maybe Konva has similar capabilities? – Vanquished Wombat Jan 30 '19 at 19:07

3 Answers3

3

As y is upside down, you need

var scaleY = -Height / rangeY

But apparently that results in the text being inverted too. You can correct for that in the Text constructor with the properties

scaleY: -1,
verticalAlign: 'top'

Ref: Konva.Text

Andrew Morton
  • 24,203
  • 9
  • 60
  • 84
2

You could convert your cartesian (postive/negative x and y) points to positions that make sense to canvas by just working out the midpoint in your canvas and doing some basic arithmetic:

var getCanvasCartesianPoint = function (x, y) {
  var midWidth = width / 2;
  var midHeight = height / 2;
  var cartesianX = midWidth + x;
  var cartesianY = midHeight + y;

  return {x: cartesianX, y: cartesianY};
}

So when you call getCanvasCartesianPoint(0,0) and your width is, say 250, the function would return {x: 125, y: 125} which is in the middle of your canvas.

See this codepen for a demonstration - https://codepen.io/joshdavenport/pen/NobERq

Josh Davenport-Smith
  • 5,456
  • 2
  • 29
  • 40
  • Excellent, almost there. I would like to also factor in the Minimum and Maximum values of X and Y via scale? – lucidgold Jan 30 '19 at 17:48
0

You just need to multiply Y by -1.

function updateText(e) {
  position.text('(' + Math.round(e.target.x()) + ', ' + Math.round(e.target.y())*-1 + ')');
  layer.batchDraw();
}
  • Welcome to Stackoverflow. This question is asked more than 3 years ago and it has an accepted answer. Please add some details about the reason you are adding a new answer. – MD Zand Dec 01 '22 at 14:23
  • Just to note, layer.batchDraw() is not required with recent Konva versions (it is 2022 now). Konva carries out batch drawing automatically, economically and efficiently. No stage.draw(), no layer.draw(), etc are needed. – Vanquished Wombat Dec 06 '22 at 09:44