Desarrollo del editor gráfico: algunos algoritmos geométricos simples que se utilizarán

Hola a todos, soy el hermano de la sandía del front-end.

Al desarrollar un editor gráfico, a menudo tendrás que resolver algunos problemas algorítmicos. Este artículo analiza algunos problemas de algoritmos geométricos simples que encontré al desarrollar un editor de gráficos.

Detección de colisiones rectangulares

Determine si dos rectángulos chocan (o se cruzan), es decir, los dos rectángulos tienen áreas superpuestas.

Escenarios de uso comunes:

  1. Utilice la herramienta de selección para enmarcar los gráficos (además de la intersección, la estrategia de selección de marco también puede utilizar la intersección u otros esquemas);
  2. Atravesar los gráficos, al juzgar la colisión entre el rectángulo de la ventana gráfica y el rectángulo del cuadro delimitador de gráficos, elimina las operaciones de representación de gráficos fuera de la ventana gráfica para mejorar el rendimiento.
export function isRectIntersect2(rect1: IBox2, rect2: IBox2) {
    
    
  return (
    rect1.minX <= rect2.maxX &&
    rect1.maxX >= rect2.minX &&
    rect1.minY <= rect2.maxY &&
    rect1.maxY >= rect2.minY
  );
}

Respecto a la firma de la interfaz IBox2 para el cuadro delimitador:

interface IBox2 {
    
    
  minX: number;
  minY: number;
  maxX: number;
  maxY: number;
}

Detección de contención rectangular

Este algoritmo se utiliza para juzgar si el rectángulo 1 contiene el rectángulo 2 o no.

inserte la descripción de la imagen aquí

Escenarios de uso comunes:

  1. Utilice la herramienta de selección para enmarcar los gráficos (esta vez utilizando la estrategia de inclusión);
function isRectContain2(rect1: IBox2, rect2: IBox2) {
    
    
  return (
    rect1.minX <= rect2.minX &&
    rect1.minY <= rect2.minY &&
    rect1.maxX >= rect2.maxX &&
    rect1.maxY >= rect2.maxY
  );
}

Calcular coordenadas rotadas

Rotar gráficos es una función muy básica. Calcular puntos rotados es un requisito muy común.

Escenarios de uso comunes:

  1. Calcule las coordenadas del cuadro delimitador después de la rotación y dibuje el punto de control de escala;
  2. Calcule si la posición del cursor cae en un rectángulo girado, debido a que el rectángulo girado no es un rectángulo ortogonal, es un poco complicado juzgar después del cálculo. Por lo general, rotaremos el cursor hasta el punto medio del rectángulo y luego juzgaremos si el punto está en el rectángulo.
const transformRotate = (
  x: number,
  y: number,
  radian: number,
  cx: number,
  cy: number,
) => {
    
    
  if (!radian) {
    
    
    return {
    
     x, y };
  }
  const cos = Math.cos(radian);
  const sin = Math.sin(radian);
  return {
    
    
    x: (x - cx) * cos - (y - cy) * sin + cx,
    y: (x - cx) * sin + (y - cy) * cos + cy,
  };
}

si el punto está en el rectángulo

[Error en la transferencia de la imagen del enlace externo, el sitio de origen puede tener un mecanismo de enlace antirrobo, se recomienda guardar la imagen y cargarla directamente (img-UFGSUYIJ-1689474500245)(https://fe-watermelon.oss-cn- shenzhen.aliyuncs.com/%E9%80%89%E4%B8%AD.gif)]

Escenarios de uso comunes:

  1. Se utiliza para realizar la selección de gráficos y juzgar si el gráfico rectangular o el cuadro delimitador está en la posición del cursor.
function isPointInRect(point: IPoint, rect: IRect) {
    
    
  return (
    point.x >= rect.x &&
    point.y >= rect.y &&
    point.x <= rect.x + rect.width &&
    point.y <= rect.y + rect.height
  );
}

Un gran rectángulo formado por varios rectángulos.

Cuando se seleccionan varios rectángulos, es necesario calcular el rectángulo grande formado por ellos y luego dibujar un cuadro de selección grande.

function getRectsBBox(...rects: IRect[]): IBox {
    
    
  if (rects.length === 0) {
    
    
    throw new Error('the count of rect can not be 0');
  }

  const minX = Math.min(...rects.map((rect) => rect.x));
  const minY = Math.min(...rects.map((rect) => rect.y));
  const maxX = Math.max(...rects.map((rect) => rect.x + rect.width));
  const maxY = Math.max(...rects.map((rect) => rect.y + rect.height));

  return {
    
    
    x: minX,
    y: minY,
    width: maxX - minX,
    height: maxY - minY,
  };
}

Aquí hay otra expresión que rodea el cuadro, por lo que hay una capa adicional de conversión.

interface IRect = {
    
    
  x: number;
  y: number;
  width: number;
  height: number;
}

type IBox = IRect

Calcular el ángulo vectorial

Al rotar el gráfico girando el punto de control, es necesario calcular el ángulo de movimiento mediante la fórmula del producto escalar del vector para actualizar el ángulo de rotación del gráfico.

[Error en la transferencia de la imagen del enlace externo, el sitio de origen puede tener un mecanismo de enlace antirrobo, se recomienda guardar la imagen y cargarla directamente (img-I7Jpoyx9-1689474500245)(https://fe-watermelon.oss-cn- shenzhen.aliyuncs.com/%E6%97%8B%E8%BD%AC.gif)]

La implementación del algoritmo para calcular el ángulo entre dos vectores [x - cx, y - cy]y [0, -1]:

/**
 * 求向量到右侧轴(x正半轴)的夹角
 * 范围在 [0, Math.PI * 2)
 */
export function calcVectorRadian(cx: number, cy: number, x: number, y: number) {
    
    
  const a = [x - cx, y - cy];
  const b = [0, -1];

  const dotProduct = a[0] * b[0] + a[1] * b[1];
  const d =
    Math.sqrt(a[0] * a[0] + a[1] * a[1]) * Math.sqrt(b[0] * b[0] + b[1] * b[1]);
  let radian = Math.acos(dotProduct / d);

  if (x < cx) {
    
    
    radian = Math.PI * 2 - radian;
  }
  return radian;
}

fin

Como editor de gráficos, a menudo tienes que lidiar con algoritmos geométricos, varios juicios de intersección, cálculos de centrado, escala del cursor, búsqueda de la línea de referencia más cercana, etc.

Esto tiene ciertos requisitos para la capacidad del algoritmo, se recomienda cepillar más Leetcode. Además, es un análisis de múltiples dibujos.

En el desarrollo, tenemos que analizar los requisitos nosotros mismos, combinar la implementación específica del editor gráfico, extraer los problemas del algoritmo y cooperar con la estructura de datos adecuada para resolver los problemas. Es posible que la solución no sea la solución óptima en un momento dado, pero podemos iterarla lentamente y optimizarla gradualmente.

Aunque requiere un poco de cerebro, al final es muy gratificante resolver el problema.

Soy el hermano de la sandía del front-end, bienvenido a seguirme y aprender más sobre los editores gráficos.

Supongo que te gusta

Origin blog.csdn.net/fe_watermelon/article/details/131747821
Recomendado
Clasificación