projectstorm / react-diagrams

a super simple, no-nonsense diagramming library written in react that just works
https://projectstorm.cloud/react-diagrams
MIT License
8.58k stars 1.17k forks source link

Fix circular dependencies in geometry #821

Closed vadimshvetsov closed 2 years ago

vadimshvetsov commented 3 years ago

Checklist

What?

Fix circular dependencies in geometry without merging everything within single file as did in #437

Why?

Because circular dependencies must be handled. Also closes #533 , #437

How?

By using single master (index) export

Feel good image:

LOL

vadimshvetsov commented 3 years ago

@dylanvorster is this PR has a chance to get in?

danykina commented 2 years ago

@dylanvorster this fix also fixes the production build error with ViteJS

jarlef commented 2 years ago

Is this project dead? Why are important fixes like these ignored?

jarlef commented 2 years ago

This PR does not resolve issues in ViteJS since it does not fix the circular dependency between Polygon.ts and Rectangle.ts. It just proxies the circular dependency through index.ts. The only way of handling this issue is by moving Reactangle class into Polyon file since it creates instanses of Rectangle. To make a quick fix for this (since it does not seem like the maintainers care to merge PRs) you can use patch-package (https://www.npmjs.com/package/patch-package) in your own project and override the dist files.

Basically add a copy of Reactangle class in Polygon.js and remove the import to Rectangle.js

node_modules/@projectstorm/geometry/dist/Polygon.js

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Polygon = void 0;
const Point_1 = require("./Point");
const _ = require("lodash");
class Polygon {
    constructor(points = []) {
        this.points = points;
    }
    serialize() {
        return _.map(this.points, (point) => {
            return [point.x, point.y];
        });
    }
    deserialize(data) {
        this.points = _.map(data, (point) => {
            return new Point_1.Point(point[0], point[1]);
        });
    }
    scale(x, y, origin) {
        let matrix = Point_1.Point.createScaleMatrix(x, y, origin);
        _.forEach(this.points, (point) => {
            point.transform(matrix);
        });
    }
    transform(matrix) {
        _.forEach(this.points, (point) => {
            point.transform(matrix);
        });
    }
    setPoints(points) {
        this.points = points;
    }
    getPoints() {
        return this.points;
    }
    rotate(degrees) {
        this.transform(Point_1.Point.createRotateMatrix(degrees / (180 / Math.PI), this.getOrigin()));
    }
    translate(offsetX, offsetY) {
        _.forEach(this.points, (point) => {
            point.translate(offsetX, offsetY);
        });
    }
    doClone(ob) {
        this.points = _.map(ob.points, (point) => {
            return point.clone();
        });
    }
    clone() {
        let ob = Object.create(this);
        ob.doClone(this);
        return ob;
    }
    getOrigin() {
        if (this.points.length === 0) {
            return null;
        }
        let dimensions = this.getBoundingBox();
        return Point_1.Point.middlePoint(dimensions.getTopLeft(), dimensions.getBottomRight());
    }
    static boundingBoxFromPolygons(polygons) {
        return Polygon.boundingBoxFromPoints(_.flatMap(polygons, (polygon) => {
            return polygon.getPoints();
        }));
    }
    static boundingBoxFromPoints(points) {
        if (points.length === 0) {
            return new Rectangle(0, 0, 0, 0);
        }
        let minX = points[0].x;
        let maxX = points[0].x;
        let minY = points[0].y;
        let maxY = points[0].y;
        for (let i = 1; i < points.length; i++) {
            if (points[i].x < minX) {
                minX = points[i].x;
            }
            if (points[i].x > maxX) {
                maxX = points[i].x;
            }
            if (points[i].y < minY) {
                minY = points[i].y;
            }
            if (points[i].y > maxY) {
                maxY = points[i].y;
            }
        }
        return new Rectangle(new Point_1.Point(minX, minY), new Point_1.Point(maxX, minY), new Point_1.Point(maxX, maxY), new Point_1.Point(minX, maxY));
    }
    getBoundingBox() {
        let minX = this.points[0].x;
        let maxX = this.points[0].x;
        let minY = this.points[0].y;
        let maxY = this.points[0].y;
        for (let i = 1; i < this.points.length; i++) {
            if (this.points[i].x < minX) {
                minX = this.points[i].x;
            }
            if (this.points[i].x > maxX) {
                maxX = this.points[i].x;
            }
            if (this.points[i].y < minY) {
                minY = this.points[i].y;
            }
            if (this.points[i].y > maxY) {
                maxY = this.points[i].y;
            }
        }
        return new Rectangle(new Point_1.Point(minX, minY), new Point_1.Point(maxX, minY), new Point_1.Point(maxX, maxY), new Point_1.Point(minX, maxY));
    }
}
exports.Polygon = Polygon;

class Rectangle extends Polygon {
    constructor(a = 0, b = 0, c = 0, d = 0) {
        if (a instanceof Point_1.Point && b instanceof Point_1.Point && c instanceof Point_1.Point && d instanceof Point_1.Point) {
            super([a, b, c, d]);
        }
        else if (a instanceof Point_1.Point) {
            super([a, new Point_1.Point(a.x + b, a.y), new Point_1.Point(a.x + b, a.y + c), new Point_1.Point(a.x, a.y + c)]);
        }
        else {
            super(Rectangle.pointsFromBounds(a, b, c, d));
        }
    }
    static pointsFromBounds(x, y, width, height) {
        return [new Point_1.Point(x, y), new Point_1.Point(x + width, y), new Point_1.Point(x + width, y + height), new Point_1.Point(x, y + height)];
    }
    updateDimensions(x, y, width, height) {
        this.points = Rectangle.pointsFromBounds(x, y, width, height);
    }
    setPoints(points) {
        if (points.length !== 4) {
            throw 'Rectangles must always have 4 points';
        }
        super.setPoints(points);
    }
    containsPoint(point) {
        const tl = this.getTopLeft();
        const br = this.getBottomRight();
        return point.x >= tl.x && point.x <= br.x && point.y >= tl.y && point.y <= br.y;
    }
    getWidth() {
        return Math.sqrt(Math.pow(this.getTopLeft().x - this.getTopRight().x, 2) + Math.pow(this.getTopLeft().y - this.getTopRight().y, 2));
    }
    getHeight() {
        return Math.sqrt(Math.pow(this.getBottomLeft().x - this.getTopLeft().x, 2) +
            Math.pow(this.getBottomLeft().y - this.getTopLeft().y, 2));
    }
    getTopMiddle() {
        return Point_1.Point.middlePoint(this.getTopLeft(), this.getTopRight());
    }
    getBottomMiddle() {
        return Point_1.Point.middlePoint(this.getBottomLeft(), this.getBottomRight());
    }
    getLeftMiddle() {
        return Point_1.Point.middlePoint(this.getBottomLeft(), this.getTopLeft());
    }
    getRightMiddle() {
        return Point_1.Point.middlePoint(this.getBottomRight(), this.getTopRight());
    }
    getTopLeft() {
        return this.points[0];
    }
    getTopRight() {
        return this.points[1];
    }
    getBottomRight() {
        return this.points[2];
    }
    getBottomLeft() {
        return this.points[3];
    }
}

//# sourceMappingURL=Polygon.js.map
dylanvorster commented 2 years ago

I don't consider this an issue since when you run build in the root, everything works correctly. I have also integrated the library into various different projects and have not run into this. the demo project further demonstrates how you can use the compiled libraries to build and compile successfully.