From 37410dc416bced8b59d7696508f89626d4ef1ffa Mon Sep 17 00:00:00 2001 From: RoxaneBurri Date: Tue, 14 Mar 2023 11:27:13 +0100 Subject: [PATCH] feat: issue 4 circle center and radius --- src/App.tsx | 3 ++- src/utils.test.ts | 35 +++++++++++++++++++++++++++++++++++ src/utils.ts | 47 +++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 80 insertions(+), 5 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 845d8b5..ede5d48 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -2,6 +2,7 @@ import "./App.css"; import { futurPosition, getBoundingRect, + getTheCircle, placeWordOnOuterCircle, Word, } from "./utils"; @@ -58,7 +59,7 @@ const Wordcloud = () => { // move the word const futureWord = futurPosition( // put the word in random place around the parent - placeWordOnOuterCircle(rect), + placeWordOnOuterCircle(rect, placedElements), placedElements, 3 ); diff --git a/src/utils.test.ts b/src/utils.test.ts index 9b24d46..e1c3abe 100644 --- a/src/utils.test.ts +++ b/src/utils.test.ts @@ -5,6 +5,8 @@ import { allCollision, Rectangle, getMoveDirection, + getTheCircle, + getDistance, } from "./utils"; const origin: Coordinate = { @@ -127,3 +129,36 @@ describe("All collision", () => { ).toBe(true); }); }); + +describe("Get distance", () => { + it("Naive test", () => { + expect( + getDistance({ x: 1, y: 1 }, { x: 1, y: 1, width: 4, height: 4 }) + ).toEqual(0); + }); +}); + +describe("Get the circle", () => { + it("Center of mass", () => { + expect(getTheCircle([{ x: 1, y: 1, width: 4, height: 4 }])).toEqual({ + x: 1, + y: 1, + radius: 250, + }); + }); + + it("Center of mass", () => { + expect( + getTheCircle([ + { x: 1, y: 1, width: 4, height: 4 }, + { x: 3, y: 4, width: 4, height: 4 }, + { x: 2, y: 2, width: 4, height: 4 }, + { x: -2, y: 1, width: 4, height: 4 }, + ]) + ).toEqual({ + x: 1, + y: 2, + radius: 250, + }); + }); +}); diff --git a/src/utils.ts b/src/utils.ts index 7d7e4b3..473550b 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -25,6 +25,12 @@ export type Coordinate = { y: number; }; +export type Circle = { + x: number; + y: number; + radius: number; +}; + export const getBoundingRect = ( id: string, tagName: "svg" | "text" = "text" @@ -57,15 +63,48 @@ export const setFirstWordInCenterOfParent = (w: Word, p: string): Rectangle => { return { x: 0, y: 0, width: 50, height: 20 }; }; +export const getDistance = (point: Coordinate, word: Rectangle): number => { + return Math.sqrt((point.x - word.x) ** 2 + (point.y - word.y) ** 2); +}; + +export const getTheCircle = (passRect: Rectangle[]): Circle => { + const centerMass: Coordinate = passRect.reduce( + (acc, word) => { + const sum = { + x: acc.x + word.x, + y: acc.y + word.y, + }; + return sum; + }, + { x: 0, y: 0 } + ); + centerMass.x /= passRect.length; + centerMass.y /= passRect.length; + + const distance: number[] = passRect.map((word) => + getDistance(centerMass, word) + ); + + const radius = + Math.max(...distance) < Math.max(CONTAINER_HEIGHT, CONTAINER_WIDTH) / 2 + ? Math.max(CONTAINER_HEIGHT, CONTAINER_WIDTH) / 2 + : Math.max(...distance); + + return { x: centerMass.x, y: centerMass.y, radius }; +}; + // This function put the word in a random place -export const placeWordOnOuterCircle = (w: Rectangle): Rectangle => { +export const placeWordOnOuterCircle = ( + w: Rectangle, + passRect: Rectangle[] +): Rectangle => { // Chose the parent face const angle = Math.random() * 360; - const radius = Math.max(CONTAINER_HEIGHT, CONTAINER_WIDTH) / 2; + const circle = getTheCircle(passRect); const newPosition = { ...w, - x: radius * Math.cos(angle) + CENTER_X, - y: radius * Math.sin(angle) + CENTER_Y, + x: circle.radius * Math.cos(angle) + circle.x, + y: circle.radius * Math.sin(angle) + circle.y, }; return newPosition; };