Curve grouped objects in fabricjs

I’m trying to create a seatmap with fabricjs. One of the features I’m stuck on is that I have seat objects that are fabricjs circles grouped in a fabric group. I want to curve the seat positions by using an html slider input.

This is what I have currently. I want to curve the seats in an arc using the slider above

This is what I’m expecting to happen

On change of the slider, I tried changing position of each seat by using for loop. But it didn’t work.

This is the code I’ve done so far. Create seats by clicking on Create Mode and dragging on canvas. Click on the group of seats to select the group and then clicking on edit mode and using the slider should curve the group.

This code might be little bit broken so use it as a reference only and don’t try on fixing it.

const editModeBtn = document.querySelector("#editModeBtn");
var canvas = new fabric.Canvas("seatingCanvas", {
  width: window.innerWidth - 50,
  height: window.innerHeight - 50,
});

var mode = "Create"; // Create, Select, Edit

var isMouseDown = false;
var startPoint, endPoint;
var snapGridSize = 25;
var selectedArea;

function drawSelectedArea(startPoint, endPoint) {
  var rect = new fabric.Rect({
    left: startPoint.x,
    top: startPoint.y,
    width: endPoint.x - startPoint.x,
    height: endPoint.y - startPoint.y,
    fill: "rgba(0, 0, 255, 0.1)",
    stroke: "rgba(0, 0, 255, 0.5)",
    strokeWidth: 1,
    selectable: false,
  });
  canvas.add(rect);
}

window.addEventListener("keydown", (e) => {
  if (mode == "Select" && e.key === "Delete") {
    removeSeatsInArea(startPoint.x, startPoint.y, endPoint.x - startPoint.x, endPoint.y - startPoint.y);
  }

  if (mode == "Select" && e.key === "ArrowRight") {
    move("right");
  }
  if (mode == "Select" && e.key === "ArrowUp") {
    move("up");
  }
  if (mode == "Select" && e.key === "ArrowLeft") {
    move("left");
  }
  if (mode == "Select" && e.key === "ArrowDown") {
    move("down");
  }
});

editModeBtn.addEventListener("click", (e) => {
  const activeGroup = canvas.getActiveObjects()[0];
  const seatsList = activeGroup.getObjects();
  seatsList.forEach((seat) => {
    console.log(seat);
    seat.selectabe = true;
    seat.left += 10;
    seat.set({ top: 0 });
  });
  canvas.renderAll();
});

canvas.on("mouse:down", function (options) {
  isMouseDown = true;
  if (mode === "Create") {
    startPoint = canvas.getPointer(options.e);
  } else if (mode === "Select") {
    startPoint = canvas.getPointer(options.e);
  }
});

canvas.on("mouse:up", function (options) {
  isMouseDown = false;

  if (mode === "Create") {
    endPoint = canvas.getPointer(options.e);
    var selectionWidth = Math.abs(endPoint.x - startPoint.x);
    var selectionHeight = Math.abs(endPoint.y - startPoint.y);
    var rows = Math.floor(selectionHeight / snapGridSize);
    var cols = Math.floor(selectionWidth / snapGridSize);
    createSeats(startPoint.x, startPoint.y, rows, cols);
    // console.log({
    //   startPoint,
    //   endPoint,
    //   selectionWidth,
    //   selectionHeight,
    //   rows,
    //   cols,
    // });
  } else if (mode === "Select" && isMouseDown) {
    endPoint = canvas.getPointer(options.e);
  }
});

canvas.on("mouse:move", function (options) {
  if (mode === "Create" && isMouseDown) {
    endPoint = canvas.getPointer(options.e);

    let circle = new fabric.Circle({
      left: startPoint,
      top: endPoint,
      radius: 25 / 2,
      fill: "#36a79a",
    });

    canvas.add(circle);

    // drawSelectedArea();
  } else if (mode === "Select" && isMouseDown) {
    endPoint = canvas.getPointer(options.e);
    // drawSelectedArea();
  }
});

canvas.on("selection:created", (e) => {
  var selectedObjects = e.selected;

  // console.log(e);

  editModeBtn.removeAttribute("disabled");

  if (e.selected && e.selected.length > 1) {
    var group = new fabric.ActiveSelection(selectedObjects, {
      canvas: canvas,
    });
    selectedObjects.push(group);
  }

  selectedObjects.forEach(function (obj) {
    obj.set({
      hasControls: false,
    });
  });

  if (mode === "Edit") {
    var selectedObjects = e.selected;
    selectedSeats = selectedObjects.filter((obj) => obj.type === "circle");
    selectedSeats.forEach((seat) => {
      seat.set({ fill: "red" });
    });
    console.log(selectedSeats);

    canvas.renderAll();
  }
});

canvas.on("selection:updated", function (e) {
  if (mode === "Edit") {
    var selectedObjects = e.selected;
    selectedSeats = selectedObjects.filter((obj) => obj.type === "circle"); // Filter out only the seats
    selectedSeats.forEach((seat) => {
      seat.set({ fill: "red" }); // Change the color of selected seats
    });
    console.log(selectedSeats);

    canvas.renderAll(); // Render the canvas to apply changes
  }
});

canvas.on("selection:cleared", function (e) {
  editModeBtn.setAttribute("disabled", "true");
});

function createSeats(x, y, rows, cols) {
  var seatSize = 25;
  var spaceBetweenSeats = 5; // Adjust this value to add space between seats
  var seatObjects = [];

  for (var i = 0; i < rows; i++) {
    var rowSeatCounter = 1; // Reset counter for each row
    for (var j = 0; j < cols; j++) {
      var seatLeft = x + j * (seatSize + spaceBetweenSeats) + seatSize / 2;
      var seatTop = y + i * (seatSize + spaceBetweenSeats) + seatSize / 2;

      var seat = new fabric.Circle({
        left: seatLeft,
        top: seatTop,
        radius: seatSize / 2,
        fill: "#36a79a",
        selectable: false,
      });

      var seatNumber = new fabric.Text(rowSeatCounter.toString(), {
        // Use rowSeatCounter for seat number
        left: seatLeft + 13,
        top: seatTop + 13,
        fontFamily: "Arial",
        fontSize: 12,
        fill: "white",
        originX: "center",
        originY: "center",
        selectable: false,
      });

      rowSeatCounter++; // Increment rowSeatCounter for each seat in the row

      var group = new fabric.Group([seat, seatNumber], {
        left: seatLeft,
        top: seatTop,
        hasBorders: false,
        hasControls: false,
      });

      seatObjects.push(group);
    }
  }

  var seatGroup = new fabric.Group(seatObjects, {
    // hasBorders: false,
    hasControls: false,
    borderColor: "#4097ec",
    padding: 5,
  });

  canvas.add(seatGroup);
  canvas.remove(selectedArea);
  selectedArea = null;
  switchMode("Select");
}

function removeSeatsInArea(x, y, width, height) {
  var activeObjects = canvas.getActiveObjects();

  console.log(activeObjects);
  activeObjects.forEach(function (object) {
    // If the object is a group
    canvas.remove(object);
    console.log(object);
  });

  selectedArea = null;
}

function move(direction) {
  let activeObjs = canvas.getActiveObjects();
  switch (direction) {
    case "right":
      activeObjs[0].group.set({ left: (activeObjs[0].group.left += 5) });
      console.log("right");
      break;
    case "left":
      activeObjs[0].group.set({ left: (activeObjs[0].group.left -= 5) });
      console.log("left");
      break;
    case "up":
      activeObjs[0].group.set({ top: (activeObjs[0].group.top -= 5) });
      console.log("top");
      break;
    case "down":
      activeObjs[0].group.set({ top: (activeObjs[0].group.top += 5) });
      console.log("down");
  }
  canvas.renderAll();
}

function switchMode(newMode) {
  mode = newMode;
  if (newMode === "Select") {
    canvas.selection = false;
  } else if (newMode === "Create") {
    canvas.selection = true;
  }

  if (selectedArea) {
    canvas.remove(selectedArea);
    selectedArea = null;
  }
}
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Seatmap</title>
    <style>
      * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
      }

      #colorselect {
        display: flex;
        gap: 3px;
      }

      #colorselect li {
        width: 20px;
        height: 20px;
        border-radius: 50%;
        list-style-type: none;
      }
    </style>
  </head>

  <body>
    <button onclick="switchMode('Create')">Create Mode</button>
    <button onclick="switchMode('Select')">Select Mode</button>
    <button onclick="switchMode('Edit')" id="editModeBtn" disabled>Edit Mode</button>

    <input type="range" min="0" max="200" value="0" id="radiusSlider" />
    <label for="radiusSlider">Radius: </label>
    <span id="radiusValue">0</span>

    <!-- <button id="undo">Undo</button>
    <button id="redo">Redo</button> -->
    <!-- <input type="color" id="favcolor" name="favcolor" value="#ff0000" /> -->
    <!-- <button id="addColor">Add Color</button> -->
    <!-- <ul id="colorselect"></ul> -->
    <!-- <input type="range" min="1" max="50" id="stretchrow" value="0" />
    <input type="range" min="0" max="50" id="stretchcol" value="0" /> -->

    <canvas id="seatingCanvas" width="800" height="600"></canvas>

    <script src="./scripts/fabric.min.js"></script>
    <script src="./scripts/app.js"></script>
  </body>
</html>