adsorb.ts 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. import { Line } from "konva/lib/shapes/Line";
  2. import { Container } from "../packages";
  3. import {
  4. createLineByDire,
  5. getLine2Angle,
  6. getLineDire,
  7. getLineDist,
  8. getLineProjection,
  9. getRotateDire,
  10. getVerticaLineDire,
  11. } from "./math";
  12. import { MathUtils } from "three";
  13. type AdsorbBaseProps = { tree: Container; position: number[] };
  14. export type AdsorbPointProps = AdsorbBaseProps & {
  15. refPointName?: string;
  16. radius?: number;
  17. exclusionIds?: string[];
  18. };
  19. /**
  20. * 参考点吸附
  21. */
  22. export const getAdsorbPointPosition = ({
  23. tree,
  24. position,
  25. refPointName = "adsord-point",
  26. exclusionIds = [],
  27. radius = 10,
  28. }: AdsorbPointProps) => {
  29. const refPositions = tree.stage
  30. .find(`.${refPointName}`)
  31. .filter((point) => !exclusionIds.includes(point.id()))
  32. .map((ref) => {
  33. const refPos = ref.position();
  34. return [refPos.x, refPos.y];
  35. });
  36. return getAdsorbPointPositionRaw({ refPositions, radius, position });
  37. };
  38. export const getAdsorbPointPositionRaw = ({
  39. position,
  40. radius = 10,
  41. refPositions,
  42. }: {
  43. refPositions: number[][];
  44. position: number[];
  45. radius: number;
  46. }) => {
  47. let adsorbPosition: number[] | null = null;
  48. let minDis = Number.MAX_VALUE;
  49. for (const refPosition of refPositions) {
  50. const dis = getLineDist(refPosition, position);
  51. if (dis < radius && dis < minDis) {
  52. minDis = dis;
  53. adsorbPosition = refPosition;
  54. }
  55. }
  56. return adsorbPosition;
  57. };
  58. type AdsorbLineProps = AdsorbBaseProps & {
  59. refLineName?: "adsord-line";
  60. exclusionIds?: string[];
  61. radiusInner?: number;
  62. angle?: number;
  63. };
  64. /**
  65. * 参考线吸附
  66. */
  67. export const getAdsorbLinePosition = ({
  68. tree,
  69. position,
  70. refLineName = "adsord-line",
  71. exclusionIds = [],
  72. angle = 2,
  73. radiusInner = 500,
  74. }: AdsorbLineProps) => {
  75. const refLines = tree.stage
  76. .find<Line>(`.${refLineName}`)
  77. .filter((line) => {
  78. if (exclusionIds.includes(line.id())) {
  79. return false;
  80. }
  81. const points = line.points();
  82. return (
  83. getLineDist([points[0], points[1]], position) < radiusInner ||
  84. getLineDist([points[2], points[3]], position) < radiusInner
  85. );
  86. })
  87. .map((ref) => {
  88. return [...ref.points(), ref.id()] as any;
  89. });
  90. return getAdsorbLinePositionRaw({ position, refLines, angle });
  91. };
  92. const adsorbLineAngles = [0, 90, 180, 270, 360];
  93. export const getAdsorbLinePositionRaw = ({
  94. position,
  95. refLines,
  96. angle = 3,
  97. }: {
  98. position: number[];
  99. refLineName?: "adsord-line";
  100. refLines: number[][];
  101. angle?: number;
  102. }) => {
  103. let adsorbAngle: number | null = null;
  104. let adsorbLine: number[] | null = null;
  105. let isAdsorb = false;
  106. for (const refLine of refLines) {
  107. const points = [
  108. [refLine[0], refLine[1]],
  109. [refLine[2], refLine[3]],
  110. ];
  111. const cAngles = points.map((start) => {
  112. let angle = MathUtils.radToDeg(
  113. getLine2Angle(refLine, [...start, ...position])
  114. );
  115. return (angle + 360) % 360;
  116. });
  117. let i = 0,
  118. j = 0;
  119. for (i = 0; i < cAngles.length; i++) {
  120. for (j = 0; j < adsorbLineAngles.length; j++) {
  121. const adAngle = adsorbLineAngles[j];
  122. if (cAngles[i] >= adAngle - angle && cAngles[i] <= adAngle + angle) {
  123. break;
  124. }
  125. }
  126. if (j !== adsorbLineAngles.length) {
  127. break;
  128. }
  129. }
  130. if (j !== adsorbLineAngles.length || i !== cAngles.length) {
  131. adsorbLine = refLine;
  132. adsorbAngle = adsorbLineAngles[j];
  133. const rdire = getRotateDire(
  134. getLineDire(adsorbLine),
  135. MathUtils.degToRad(adsorbAngle)
  136. );
  137. const start =
  138. getLineDist([adsorbLine[0], adsorbLine[1]], position) >
  139. getLineDist([adsorbLine[2], adsorbLine[3]], position)
  140. ? [adsorbLine[2], adsorbLine[3]]
  141. : [adsorbLine[0], adsorbLine[1]];
  142. adsorbLine = createLineByDire(rdire, start, 10);
  143. position = getLineProjection(adsorbLine, position).point;
  144. isAdsorb = true;
  145. }
  146. }
  147. if (isAdsorb) {
  148. return position;
  149. }
  150. };
  151. export const getAdsorbCrossPosition = ({
  152. position,
  153. angle,
  154. points,
  155. }: AdsorbSelfLinesProps) => {
  156. if (!points?.length) return;
  157. if (points.length === 1) {
  158. const point = points[0];
  159. const refLines = [
  160. [point[0] - 1, point[1], point[0] + 1, point[1]],
  161. [point[0], point[1] - 1, point[0], point[1] + 1],
  162. ];
  163. return getAdsorbLinePositionRaw({ position, refLines, angle });
  164. }
  165. };
  166. export type AdsorbSelfLinesProps = {
  167. points?: number[][];
  168. angle?: number;
  169. position: number[];
  170. };
  171. export const getAdsorbSelfLinesPosition = ({
  172. position,
  173. points,
  174. angle,
  175. }: AdsorbSelfLinesProps) => {
  176. if (!points?.length) return;
  177. // 当前位置始终在第一个点
  178. const last = points.slice(0, 2).flatMap((a) => a);
  179. const first = points
  180. .slice(points.length - 2, points.length)
  181. .flatMap((a) => a);
  182. const vlines = [
  183. last,
  184. // createLineByDire(getVerticaLineDire(last), [last[2], last[3]], 10),
  185. createLineByDire(getVerticaLineDire(first), [first[2], first[3]], 10),
  186. ];
  187. // 垂直最后一段
  188. return (
  189. getAdsorbLinePositionRaw({
  190. position,
  191. refLines: vlines,
  192. angle,
  193. }) || position
  194. );
  195. };
  196. export type AdsorbProps = Omit<
  197. AdsorbPointProps & AdsorbLineProps & AdsorbSelfLinesProps,
  198. "position"
  199. > & {
  200. pixel?: number[];
  201. position?: number[];
  202. radiusInner?: number;
  203. pointsArray?: number[][][];
  204. };
  205. export const getAdsorbPosition = (props: AdsorbProps) => {
  206. const position = props.position || props.tree.getRealFromStage(props.pixel);
  207. let refPosition: number[];
  208. if ((refPosition = getAdsorbPointPosition({ ...props, position }))) {
  209. return refPosition;
  210. } else if ((refPosition = getAdsorbCrossPosition({ ...props, position }))) {
  211. return refPosition;
  212. } else if (
  213. (refPosition = getAdsorbSelfLinesPosition({ ...props, position }))
  214. ) {
  215. return refPosition;
  216. }
  217. if (props.pointsArray) {
  218. for (let i = 0; i < props.pointsArray.length; i++) {
  219. if (
  220. (refPosition = getAdsorbSelfLinesPosition({
  221. ...props,
  222. position,
  223. points: props.pointsArray[i],
  224. }))
  225. ) {
  226. return refPosition;
  227. }
  228. }
  229. }
  230. // if ((refPosition = getAdsorbLinePosition({ ...props, position }))) {
  231. // return refPosition;
  232. // }
  233. return position;
  234. };