3 votes

Comment puis-je calculer correctement une coordonnée à partir d'un plan transformé ?

J'ai un contexte sur lequel je dessine plusieurs rectangles. Le contexte a ensuite subi une transformation qui le transforme en une illusion 3D d'une carte flottante.

Matrix Example

J'essaie de créer une formule pour calculer la coordonnée sur laquelle le curseur se trouve, sans utiliser Path2Ds. En effet, je dois pouvoir calculer la coordonnée sur laquelle le curseur se trouve même si la tuile n'est pas dessinée, mais qu'elle se trouve sur la grille.

La matrice de transformation a...

  • horizontal

    • l'échelonnement des 1.0
    • inclinaison de 0.5
    • le déplacement de (columns * 32) (nombre de colonnes : 6)
  • vertical

    • l'échelonnement des 0.5
    • inclinaison de -1.0
    • le déplacement de 0

Avec l'aide de Position réelle de la souris dans le canevas Cependant, lorsque la souris se déplace vers le bas et la gauche, la colonne diminue, bien qu'elle soit sur la même colonne. Quand on va vers le bas et la droite, la ligne diminue aussi, bien qu'elle soit sur la même ligne.

const rows = 10;
const columns = 6;

const $coordinate = $("#coordinate");

const $canvas = $("#canvas");

canvas.width = (rows * 32) + (columns * 32);
canvas.height = (rows * 16) + (columns * 16);

const context = $canvas[0].getContext("2d");

context.imageSmoothingEnabled = false;

context.save();

context.fillStyle = "white";

context.setTransform(1, 0.5, -1, 0.5, (columns * 32), 0);

// (a) horizontal scaling: 1
// (b) horizontal skewing: 0.5
// (c) vertical skewing: -1
// (d) vertical scaling: 0.5
// (e) horizontal moving: (columns * 32)
// (f) vertical moving: 0

const matrix = {
    vertical: {
    scaling: 1.0,
    skewing: 0.5,

    moving: (columns * 32)
  },

  horizontal: {
    scaling: 0.5,
    skewing: -1,

    moving: 0
  }
};

for(let row = 0; row < rows; row++) {
  for(let column = 0; column < columns; column++) {
    context.rect(row * 32, column * 32, 31.5, 31.5);
  }
}

context.fill();

$canvas.mousemove(function(e) {
    const position = {
    left: e.pageX - $canvas.offset().left,
    top: e.pageY - $canvas.offset().top
  };

  const innerPosition = {
    left: position.left * matrix.horizontal.scaling + position.top * matrix.vertical.skewing + matrix.horizontal.moving, 
    top: position.left * matrix.horizontal.skewing + position.top * matrix.vertical.scaling + matrix.vertical.moving
  };

    const coordinate = {
    row: Math.trunc(innerPosition.top / 32),
    column: Math.trunc(innerPosition.left / 32)
  };

  $coordinate.html(coordinate.row + "x" + coordinate.column);
});

#canvas {
  background: green;
}

#coordinate {
  position: absolute;

  font-family: Arial, sans-serif;

  font-size: 16px;

  left: 12px;
  top: 12px;
}

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<canvas id="canvas"></canvas>

<div id="coordinate">0x0</div>

Je n'utilise aucun framework (à l'exception de JQuery, mais sans rapport avec la question). Comment puis-je calculer les coordonnées exactes ?

3voto

Alnitak Points 143355

Votre appel à setTransform(a, b, c, d, e, f) crée un 3x3 matrice de transformation affine :

|  a  c  e  |
|  b  d  f  |
|  0  0  1  |

qui, étant donné vos valeurs actuelles de (1, 0.5, -1, 0.5, n, 0), a une valeur de inverse matrice de :

|  0.5     1  -n/2  | 
| -0.5     1  +n/2  |
|    0     0     1  |

En appliquant cette matrice de transformation aux coordonnées de votre souris (qui doivent être exprimées sous forme de matrice 1x3 [x, y, 1] doit fournir les coordonnées de grille souhaitées :

const rows = 10;
const columns = 6;

const $coordinate = $("#coordinate");

const $canvas = $("#canvas");

canvas.width = (rows * 32) + (columns * 32);
canvas.height = (rows * 16) + (columns * 16);

const context = $canvas[0].getContext("2d");

context.imageSmoothingEnabled = false;

context.save();

context.fillStyle = "white";

context.setTransform(1, 0.5, -1, 0.5, (columns * 32), 0);

// (a) horizontal scaling: 1
// (b) horizontal skewing: 0.5
// (c) vertical skewing: -1
// (d) vertical scaling: 0.5
// (e) horizontal moving: (columns * 32)
// (f) vertical moving: 0

for(let row = 0; row < rows; row++) {
  for(let column = 0; column < columns; column++) {
    context.rect(row * 32, column * 32, 31.5, 31.5);
  }
}

context.fill();

$canvas.mousemove(function(e) {
    const position = {
    left: e.pageX - $canvas.offset().left,
    top: e.pageY - $canvas.offset().top
  };

  const innerPosition = {
    left: position.left * 0.5 + position.top - (columns * 32) / 2,
    top: position.left * -0.5 + position.top + (columns * 32) / 2
  };

    const coordinate = {
    row: Math.floor(innerPosition.top / 32),
    column: Math.floor(innerPosition.left / 32)
  };

  $coordinate.html(coordinate.row + "x" + coordinate.column);
});

#canvas {
  background: green;
}

#coordinate {
  position: absolute;

  font-family: Arial, sans-serif;

  font-size: 16px;

  left: 12px;
  top: 12px;
}

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<canvas id="canvas"></canvas>

<div id="coordinate">0x0</div>

-1voto

Lebster Points 229

Juste une idée : Pourquoi ne pas essayer de créer un plan d'étage dans votre html (avec des divs), de le styliser avec CSS pour qu'il corresponde à l'emplacement du plan d'étage de la toile, puis de le cacher. De cette façon, vous pouvez détecter le survol des divs avec jquery, et utiliser les coordnitates des divs pour savoir quel rectangle doit être modifié sur la toile ? Je suis sur mon téléphone en ce moment, mais dans 5 minutes je peux faire une démonstration rapide sur jsfiddle.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X