This example uses OpenLayers v10.9.0. The latest is v.
Edit

#### Scale and Rotate using Modify Interaction

draw 17 edit 13 modify 11 vector 86 scale 4 rotate 3

#####

Geometry type

Example of using the Modify interaction to scale and rotate geometries.

Example of using the `ol/interaction/Modify` interaction to scale and rotate geometries. Custom style functions produce and display a scaled and rotated version of the original geometry based on the position of a vertex being modified. This is set as the final geometry at the end of the interaction. By default the `ol/geom/Geometry` scale and rotate methods use the center of the geometry extent as anchor. For irregular shapes the extent changes as the geometry is rotated and using its center as anchor could produce different results if rotation was stopped and resumed. To avoid that an anchor point which is fixed relative to the geometry is used - for `ol/geom/Polygon` the centroid of the vertices, and the midpoint for `ol/geom/LineString`. Only outer vertices (more than 1/3 the maximum distance from the anchor) are used to scale and rotate as precise scaling close to the anchor would be difficult. For the convenience of the user the style function highlights the anchor and available vertices. The `ol/interaction/Translate` interaction is also available to reposition geometries. The Modify and Translate interactions have mutually exclusive `condition` options set so they can be available together. Use `Ctrl+Drag` (`Command+Drag` on Mac) to use the Translate interaction.

##### main.js

    import Map from 'ol/Map.js';
    import View from 'ol/View.js';
    import {
      never,
      platformModifierKeyOnly,
      primaryAction,
    } from 'ol/events/condition.js';
    import {getCenter, getHeight, getWidth} from 'ol/extent.js';
    import MultiPoint from 'ol/geom/MultiPoint.js';
    import Point from 'ol/geom/Point.js';
    import Draw from 'ol/interaction/Draw.js';
    import Modify from 'ol/interaction/Modify.js';
    import Translate from 'ol/interaction/Translate.js';
    import TileLayer from 'ol/layer/Tile.js';
    import VectorLayer from 'ol/layer/Vector.js';
    import OSM from 'ol/source/OSM.js';
    import VectorSource from 'ol/source/Vector.js';
    import CircleStyle from 'ol/style/Circle.js';
    import Fill from 'ol/style/Fill.js';
    import Stroke from 'ol/style/Stroke.js';
    import Style from 'ol/style/Style.js';

    const raster = new TileLayer({
      source: new OSM(),
    });

    const source = new VectorSource();

    const style = new Style({
      geometry: function (feature) {
        const modifyGeometry = feature.get('modifyGeometry');
        return modifyGeometry ? modifyGeometry.geometry : feature.getGeometry();
      },
      fill: new Fill({
        color: 'rgba(255, 255, 255, 0.2)',
      }),
      stroke: new Stroke({
        color: '#ffcc33',
        width: 2,
      }),
      image: new CircleStyle({
        radius: 7,
        fill: new Fill({
          color: '#ffcc33',
        }),
      }),
    });

    function calculateCenter(geometry) {
      let center, coordinates, minRadius;
      const type = geometry.getType();
      if (type === 'Polygon') {
        let x = 0;
        let y = 0;
        let i = 0;
        coordinates = geometry.getCoordinates()[0].slice(1);
        coordinates.forEach(function (coordinate) {
          x += coordinate[0];
          y += coordinate[1];
          i++;
        });
        center = [x / i, y / i];
      } else if (type === 'LineString') {
        center = geometry.getCoordinateAt(0.5);
        coordinates = geometry.getCoordinates();
      } else {
        center = getCenter(geometry.getExtent());
      }
      let sqDistances;
      if (coordinates) {
        sqDistances = coordinates.map(function (coordinate) {
          const dx = coordinate[0] - center[0];
          const dy = coordinate[1] - center[1];
          return dx * dx + dy * dy;
        });
        minRadius = Math.sqrt(Math.max.apply(Math, sqDistances)) / 3;
      } else {
        minRadius =
          Math.max(
            getWidth(geometry.getExtent()),
            getHeight(geometry.getExtent()),
          ) / 3;
      }
      return {
        center: center,
        coordinates: coordinates,
        minRadius: minRadius,
        sqDistances: sqDistances,
      };
    }

    const vector = new VectorLayer({
      source: source,
      style: function (feature) {
        const styles = [style];
        const modifyGeometry = feature.get('modifyGeometry');
        const geometry = modifyGeometry
          ? modifyGeometry.geometry
          : feature.getGeometry();
        const result = calculateCenter(geometry);
        const center = result.center;
        if (center) {
          styles.push(
            new Style({
              geometry: new Point(center),
              image: new CircleStyle({
                radius: 4,
                fill: new Fill({
                  color: '#ff3333',
                }),
              }),
            }),
          );
          const coordinates = result.coordinates;
          if (coordinates) {
            const minRadius = result.minRadius;
            const sqDistances = result.sqDistances;
            const rsq = minRadius * minRadius;
            const points = coordinates.filter(function (coordinate, index) {
              return sqDistances[index] > rsq;
            });
            styles.push(
              new Style({
                geometry: new MultiPoint(points),
                image: new CircleStyle({
                  radius: 4,
                  fill: new Fill({
                    color: '#33cc33',
                  }),
                }),
              }),
            );
          }
        }
        return styles;
      },
    });

    const map = new Map({
      layers: [raster, vector],
      target: 'map',
      view: new View({
        center: [-11000000, 4600000],
        zoom: 4,
      }),
    });

    const defaultStyle = new Modify({source: source})
      .getOverlay()
      .getStyleFunction();

    const modify = new Modify({
      source: source,
      condition: function (event) {
        return primaryAction(event) && !platformModifierKeyOnly(event);
      },
      deleteCondition: never,
      insertVertexCondition: never,
      style: function (feature, resolution) {
        feature.get('features').forEach(function (modifyFeature) {
          const modifyGeometry = modifyFeature.get('modifyGeometry');
          if (modifyGeometry) {
            const point = feature.getGeometry().getCoordinates();
            let modifyPoint = modifyGeometry.point;
            if (!modifyPoint) {
              // save the initial geometry and vertex position
              modifyPoint = point;
              modifyGeometry.point = modifyPoint;
              modifyGeometry.geometry0 = modifyGeometry.geometry;
              // get anchor and minimum radius of vertices to be used
              const result = calculateCenter(modifyGeometry.geometry0);
              modifyGeometry.center = result.center;
              modifyGeometry.minRadius = result.minRadius;
            }

            const center = modifyGeometry.center;
            const minRadius = modifyGeometry.minRadius;
            let dx, dy;
            dx = modifyPoint[0] - center[0];
            dy = modifyPoint[1] - center[1];
            const initialRadius = Math.sqrt(dx * dx + dy * dy);
            if (initialRadius > minRadius) {
              const initialAngle = Math.atan2(dy, dx);
              dx = point[0] - center[0];
              dy = point[1] - center[1];
              const currentRadius = Math.sqrt(dx * dx + dy * dy);
              if (currentRadius > 0) {
                const currentAngle = Math.atan2(dy, dx);
                const geometry = modifyGeometry.geometry0.clone();
                geometry.scale(currentRadius / initialRadius, undefined, center);
                geometry.rotate(currentAngle - initialAngle, center);
                modifyGeometry.geometry = geometry;
              }
            }
          }
        });
        return defaultStyle(feature, resolution);
      },
    });

    modify.on('modifystart', function (event) {
      event.features.forEach(function (feature) {
        feature.set(
          'modifyGeometry',
          {geometry: feature.getGeometry().clone()},
          true,
        );
      });
    });

    modify.on('modifyend', function (event) {
      event.features.forEach(function (feature) {
        const modifyGeometry = feature.get('modifyGeometry');
        if (modifyGeometry) {
          feature.setGeometry(modifyGeometry.geometry);
          feature.unset('modifyGeometry', true);
        }
      });
    });

    map.addInteraction(modify);
    map.addInteraction(
      new Translate({
        condition: function (event) {
          return primaryAction(event) && platformModifierKeyOnly(event);
        },
        layers: [vector],
      }),
    );

    let draw; // global so we can remove it later
    const typeSelect = document.getElementById('type');

    function addInteractions() {
      draw = new Draw({
        source: source,
        type: typeSelect.value,
      });
      map.addInteraction(draw);
    }

    /**
     * Handle change event.
     */
    typeSelect.onchange = function () {
      map.removeInteraction(draw);
      addInteractions();
    };

    addInteractions();

##### index.html

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8">
        <title>Scale and Rotate using Modify Interaction</title>
        <link rel="stylesheet" href="node_modules/ol/ol.css">
        <style>
          .map {
            width: 100%;
            height: 400px;
          }
        </style>
      </head>
      <body>
        <div id="map" class="map"></div>
        <form>
          <label for="type">Geometry type &nbsp;</label>
          <select id="type">
            <option value="Point">Point</option>
            <option value="LineString">LineString</option>
            <option value="Polygon" selected>Polygon</option>
            <option value="Circle">Circle</option>
          </select>
        </form>

        <script type="module" src="main.js"></script>
      </body>
    </html>

##### package.json

    {
      "name": "modify-scale-and-rotate",
      "dependencies": {
        "ol": "10.9.0"
      },
      "devDependencies": {
        "vite": "^3.2.3"
      },
      "scripts": {
        "start": "vite",
        "build": "vite build"
      }
    }