diff --git a/.vscode/settings.json b/.vscode/settings.json index 564d4040..77b1f82a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,6 +3,7 @@ "Anusha", "Huda", "jamesoswald", + "mosueout", "peaceiris", "peircemyheart", "radx", diff --git a/package-lock.json b/package-lock.json index 9ab15ba7..6e5e19d4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,11 +12,11 @@ "nomial": "^1.0.11" }, "devDependencies": { - "@types/node": "20.8.0", - "gts": "^5.0.1", - "typedoc": "^0.25.1", + "@types/node": "20.8.3", + "gts": "^5.2.0", + "typedoc": "^0.25.2", "typescript": "~5.2.2", - "vite": "^4.4.9", + "vite": "^4.4.11", "vitest": "^0.34.6" } }, @@ -607,9 +607,9 @@ } }, "node_modules/@eslint/js": { - "version": "8.44.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.44.0.tgz", - "integrity": "sha512-Ag+9YM4ocKQx9AarydN0KY2j0ErMHNIocPDrVo8zAE44xLTjEtz81OdR68/cydGtk6m6jDb5Za3r2useMzYmSw==", + "version": "8.50.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.50.0.tgz", + "integrity": "sha512-NCC3zz2+nvYd+Ckfh87rA47zfu2QsQpvc6k1yzTk+b9KzRj0wkGa8LSoGOXN6Zv4lRf/EIoZ80biDh9HOI+RNQ==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -837,9 +837,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.8.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.0.tgz", - "integrity": "sha512-LzcWltT83s1bthcvjBmiBvGJiiUe84NWRHkw+ZV6Fr41z2FbIzvc815dk2nQ3RAKMuN2fkenM/z3Xv2QzEpYxQ==", + "version": "20.8.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.3.tgz", + "integrity": "sha512-jxiZQFpb+NlH5kjW49vXxvxTjeeqlbsnTAdBTKpzEdPs9itay7MscYXz3Fo9VYFEsfQ6LJFitHad3faerLAjCw==", "dev": true }, "node_modules/@types/normalize-package-data": { @@ -1843,27 +1843,27 @@ } }, "node_modules/eslint": { - "version": "8.45.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.45.0.tgz", - "integrity": "sha512-pd8KSxiQpdYRfYa9Wufvdoct3ZPQQuVuU5O6scNgMuOMYuxvH0IGaYK0wUFjo4UYYQQCUndlXiMbnxopwvvTiw==", + "version": "8.50.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.50.0.tgz", + "integrity": "sha512-FOnOGSuFuFLv/Sa+FDVRZl4GGVAAFFi8LecRsI5a1tMO5HIE8nCm4ivAlzt4dT3ol/PaaGC0rJEEXQmHJBGoOg==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.4.0", - "@eslint/eslintrc": "^2.1.0", - "@eslint/js": "8.44.0", - "@humanwhocodes/config-array": "^0.11.10", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.2", + "@eslint/js": "8.50.0", + "@humanwhocodes/config-array": "^0.11.11", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", + "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.0", - "eslint-visitor-keys": "^3.4.1", - "espree": "^9.6.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -1897,9 +1897,9 @@ } }, "node_modules/eslint-config-prettier": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.8.0.tgz", - "integrity": "sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.0.0.tgz", + "integrity": "sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw==", "dev": true, "bin": { "eslint-config-prettier": "bin/cli.js" @@ -2497,16 +2497,16 @@ "dev": true }, "node_modules/gts": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/gts/-/gts-5.0.1.tgz", - "integrity": "sha512-wanVTOI5CRVHQRwn2Fpux4IUUF6f6E3WbhfR+k9PB/hGS8OsmSjo2z0gy6kgsXwqjPqKEhC2w/9jIl/VP7NXCQ==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/gts/-/gts-5.2.0.tgz", + "integrity": "sha512-25qOnePUUX7upFc4ycqWersDBq+o1X6hXUTW56JOWCxPYKJXQ1RWzqT9q+2SU3LfPKJf+4sz4Dw3VT0p96Kv6g==", "dev": true, "dependencies": { "@typescript-eslint/eslint-plugin": "5.62.0", "@typescript-eslint/parser": "5.62.0", "chalk": "^4.1.2", - "eslint": "8.45.0", - "eslint-config-prettier": "8.8.0", + "eslint": "8.50.0", + "eslint-config-prettier": "9.0.0", "eslint-plugin-node": "11.1.0", "eslint-plugin-prettier": "5.0.0", "execa": "^5.0.0", @@ -2514,7 +2514,7 @@ "json5": "^2.1.3", "meow": "^9.0.0", "ncp": "^2.0.0", - "prettier": "3.0.1", + "prettier": "3.0.3", "rimraf": "3.0.2", "write-file-atomic": "^4.0.0" }, @@ -3495,9 +3495,9 @@ } }, "node_modules/prettier": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.1.tgz", - "integrity": "sha512-fcOWSnnpCrovBsmFZIGIy9UqK2FaI7Hqax+DIO0A9UxeVoY4iweyaFjS5TavZN97Hfehph0nhsZnjlVKzEQSrQ==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", + "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", "dev": true, "bin": { "prettier": "bin/prettier.cjs" @@ -4241,9 +4241,9 @@ } }, "node_modules/typedoc": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.25.1.tgz", - "integrity": "sha512-c2ye3YUtGIadxN2O6YwPEXgrZcvhlZ6HlhWZ8jQRNzwLPn2ylhdGqdR8HbyDRyALP8J6lmSANILCkkIdNPFxqA==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.25.2.tgz", + "integrity": "sha512-286F7BeATBiWe/qC4PCOCKlSTwfnsLbC/4cZ68oGBbvAqb9vV33quEOXx7q176OXotD+JdEerdQ1OZGJ818lnA==", "dev": true, "dependencies": { "lunr": "^2.3.9", @@ -4333,9 +4333,9 @@ } }, "node_modules/vite": { - "version": "4.4.9", - "resolved": "https://registry.npmjs.org/vite/-/vite-4.4.9.tgz", - "integrity": "sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA==", + "version": "4.4.11", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.4.11.tgz", + "integrity": "sha512-ksNZJlkcU9b0lBwAGZGGaZHCMqHsc8OpgtoYhsQ4/I2v5cnpmmmqe5pM4nv/4Hn6G/2GhTdj0DhZh2e+Er1q5A==", "dev": true, "dependencies": { "esbuild": "^0.18.10", diff --git a/package.json b/package.json index 8057fc83..830bc730 100644 --- a/package.json +++ b/package.json @@ -31,11 +31,11 @@ }, "homepage": "https://github.com/James-Oswald/PeirceMyHeart#readme", "devDependencies": { - "@types/node": "20.8.0", - "gts": "^5.0.1", - "typedoc": "^0.25.1", + "@types/node": "20.8.3", + "gts": "^5.2.0", + "typedoc": "^0.25.2", "typescript": "~5.2.2", - "vite": "^4.4.9", + "vite": "^4.4.11", "vitest": "^0.34.6" }, "dependencies": { diff --git a/src/AEG/AEGTree.ts b/src/AEG/AEGTree.ts index 35509d7c..d0bc1d98 100644 --- a/src/AEG/AEGTree.ts +++ b/src/AEG/AEGTree.ts @@ -66,7 +66,6 @@ export class AEGTree { * @returns True, if the node can be inserted. Else, false */ public canInsert(incomingNode: AtomNode | CutNode): boolean { - console.log("checking can insert"); const currentCut: CutNode = this.sheet.getCurrentCut(incomingNode); for (let i = 0; i < currentCut.children.length; i++) { if (this.overlaps(incomingNode, currentCut.children[i])) { @@ -87,8 +86,6 @@ export class AEGTree { } const currentCut: CutNode = this.sheet.getCurrentCut(incomingNode); - //const originalChildren: (AtomNode | CutNode)[] = currentCut.children; - //==============CHANGEDDDD========= const originalChildren: (AtomNode | CutNode)[] = [...currentCut.children]; currentCut.children.push(incomingNode); diff --git a/src/AEG/AtomNode.ts b/src/AEG/AtomNode.ts index cf0e8d7d..5f4ad7e3 100644 --- a/src/AEG/AtomNode.ts +++ b/src/AEG/AtomNode.ts @@ -29,10 +29,10 @@ export class AtomNode { * @param rect The rectangle to be set as the boundary box of this node. * @param val The value of the proposition represented by this node. */ - public constructor(val: string, origin: Point, rect: Rectangle) { - this.rect = rect; - this.identifier = val; - this.origin = origin; + public constructor(val?: string, origin?: Point, rect?: Rectangle) { + this.rect = rect ?? new Rectangle(); + this.identifier = val ?? ""; + this.origin = origin ?? new Point(); } /** diff --git a/src/AEG/CutNode.ts b/src/AEG/CutNode.ts index fd6cd83c..686632dc 100644 --- a/src/AEG/CutNode.ts +++ b/src/AEG/CutNode.ts @@ -38,8 +38,6 @@ export class CutNode { for (let i = 0; i < this.children.length; i++) { const child: CutNode | AtomNode = this.children[i]; if (child instanceof CutNode && this.children[i].containsNode(newNode)) { - //======DEBUGGG======= - console.log("current cut: " + this.children[i]); //newNode can be placed at least one layer deeper return child.getCurrentCut(newNode); } @@ -77,7 +75,6 @@ export class CutNode { if (otherNode instanceof AtomNode) { return this.ellipse.containsShape(otherNode.rect); } else if (otherNode instanceof CutNode) { - //ELLIPSE TO BE IMPLEMENTED ACCURATELY return this.ellipse.containsShape(otherNode.ellipse as Ellipse); } else { throw Error("containsNode expected AtomNode or CutNode"); diff --git a/src/AEG/Ellipse.ts b/src/AEG/Ellipse.ts index bb872bbd..f53e2145 100644 --- a/src/AEG/Ellipse.ts +++ b/src/AEG/Ellipse.ts @@ -1,6 +1,5 @@ import {Point} from "./Point"; import {Rectangle} from "./Rectangle"; -//import {Polynomial, polynomialRoots} from "nomial"; /** * Class that defines an Ellipse. @@ -77,19 +76,12 @@ export class Ellipse { //(x, y) = new point //(h, k) = center - const p: number = Math.round( + const p: number = Math.ceil( Math.pow(otherPoint.x - this.center.x, 2) / Math.pow(this.radiusX, 2) + Math.pow(otherPoint.y - this.center.y, 2) / Math.pow(this.radiusY, 2) ); return p <= 1; - - //Method 2: scaling eclipse to check for containment - /* const scale_y = this.radiusX / this.radiusY; - const dx = otherPoint.x - this.center.x; - const dy = (otherPoint.y - this.center.y) * scale_y; - - return Math.pow(dx, 2) + Math.pow(dy, 2) <= Math.pow(this.radiusX, 2); */ } /** @@ -98,7 +90,6 @@ export class Ellipse { * @returns True, if there is an overlap. Else, false. */ public overlaps(otherShape: Rectangle | Ellipse): boolean { - //return this.boundingBox.overlaps(otherShape); if (otherShape instanceof Rectangle) { for (let i = 0; i < 4; i++) { if (this.containsPoint(otherShape.getCorners()[i])) { @@ -109,13 +100,21 @@ export class Ellipse { return false; } else { //check if the rectangular bounding boxes of the ellipse overlap - if (this.boundingBox.overlaps((otherShape as Ellipse).boundingBox)) { - console.log("ellipse boxes overlap"); - return true; + if ( + this.boundingBox.overlaps((otherShape as Ellipse).boundingBox) || + //this.boundingBox.containsShape((otherShape as Ellipse).boundingBox) || + (otherShape as Ellipse).boundingBox.containsShape(this.boundingBox) + ) { + //return true; //if there is an overlap, check if points along the ellipse curve overlap - //this can be done by checking if points along the curve of the other ellipse - //are within this ellipse - //return this.checkQuadrantOverlap(otherShape); + //this can be done by checking if points along the curve of this ellipse + //are within the other ellipse + const points: Point[] = this.getEllipsePoints(); + for (let i = 0; i < points.length; i++) { + if (otherShape.containsPoint(points[i])) { + return true; + } + } } return false; } @@ -148,35 +147,6 @@ export class Ellipse { throw Error("Invalid Shape passed to containsShape, must be a Rectangle | Ellipse"); } - /** - * Method that checks if any quadrant of another ellipse overlaps with this ellipse. - * This can be done by checking if a point on the curve of the ellipse is within this ellipse. - * @param otherEllipse The other ellipse that might be overlapping with this ellipse - * @returns True, if there is an overlap. Else, false - */ - private checkQuadrantOverlap(otherEllipse: Ellipse): boolean { - //Get the quadrant which might be overlapping with this ellipse. - //To do so, check which corner of the rectangular bounding box of the other ellipse - //is within this ellipse. - for (let i = 0; i < 4; i++) { - if (this.containsPoint(otherEllipse.boundingBox.getCorners()[i])) { - //Get the points on the curve of the ellipse in that quadrant - const points: Point[] = otherEllipse.getQuadrantPoints(i); - - console.log("has corner " + i); - //If any points along the curve are within this ellipse, the other ellipse overlaps - //with this ellipse. Return true. - for (let j = 0; j < 6; j++) { - if (this.containsPoint(points[j])) { - console.log("Has overlap"); - return true; - } - } - } - } - return false; - } - /** * An array containing the widest coordinates of the ellipse, i.e. the coordinates along the * x-axis and y-axis of the ellipse. @@ -196,54 +166,25 @@ export class Ellipse { ]; } - /** - * Method that returns the points on the curve of the ellipse in a specific quadrant - * @param quadrant The quadrant which we want the points in - * @returns An array of points along the curve of the ellipse - */ - private getQuadrantPoints(quadrant: number): Point[] { - //==========DEBUGGG========= - console.log("Getting points for: " + this + "\n" + "In quad: " + quadrant); - + private getEllipsePoints(): Point[] { const points: Point[] = []; - const quadDistance = this.radiusX; - let curve = 1; - - if (quadrant === 0) { - //top left quadrant - points[0] = this.getWidestCoordinates()[3]; - points[1] = this.getWidestCoordinates()[0]; - } else if (quadrant === 1) { - //top right quadrant - points[0] = this.getWidestCoordinates()[0]; - points[1] = this.getWidestCoordinates()[1]; - } else if (quadrant === 2) { - //bottom right quadrant - points[0] = this.getWidestCoordinates()[1]; - points[1] = this.getWidestCoordinates()[2]; + const pointDist = this.radiusX / 15; - curve = -1; - } else if (quadrant === 3) { - //bottom left quadrant - points[0] = this.getWidestCoordinates()[2]; - points[1] = this.getWidestCoordinates()[3]; - - curve = -1; - } + points[0] = this.getWidestCoordinates()[3]; + let x: number; + let y: number; - for (let i = 2; i < 6; i++) { - let x = points[0].x; - if (curve === 1) { - x = x + (i - 1) * (quadDistance / 5); + for (let i = 1; i < 64; i++) { + if (i < 33) { + x = points[i - 1].x + pointDist; + y = this.getCurvePoint(x, 1); } else { - x = x - (i - 1) * (quadDistance / 5); + x = points[i - 1].x - pointDist; + y = this.getCurvePoint(x, -1); } - const y = this.getCurvePoint(x, curve); points[i] = new Point(x, y); } - //========DEBUGGGG======== - console.log(points); return points; } diff --git a/src/AEG/Rectangle.ts b/src/AEG/Rectangle.ts index c676704c..c2445557 100644 --- a/src/AEG/Rectangle.ts +++ b/src/AEG/Rectangle.ts @@ -79,35 +79,167 @@ export class Rectangle { } /** - * Checks whether there is an overlap between this Rectangle and another shape. - * @param otherShape The other shape that may overlap this Rectangle. - * @returns True, if there is an overlap. + * Checks whether there is an overlap between this rectangle and another shape. + * @param otherShape The other shape that might be overlapping this rectangle. + * @returns True, if there is an overlap. Else, false. + * @todo This method is wrong, the ellipse overlap portion functions as a contains or overlaps + * it should be replaced to just handle overlaps and not return true on contains. -James + * @todo this is basically the exact same method that needs to be implemented for + * Ellipse.overlaps, the implementations should be merged into a single helper function, + * which is called by both methods. -James */ public overlaps(otherShape: Rectangle | Ellipse): boolean { if (otherShape instanceof Rectangle) { - const thisCorners = this.getCorners(); - const otherCorners = otherShape.getCorners(); - - //Overlap occurs if either of the corners of either shape are within the other + return this.edgesOverlap(otherShape); + } else if (otherShape instanceof Ellipse) { for (let i = 0; i < 4; i++) { - if ( - this.containsPoint(otherCorners[i]) || - otherShape.containsPoint(thisCorners[i]) - ) { + if (otherShape.containsPoint(this.getCorners()[i])) { return true; } } return false; } else { - //const ellipseBoundary = (otherShape as Ellipse).boundingBox; - //return this.overlaps(ellipseBoundary); - for (let i = 0; i < 4; i++) { - if ((otherShape as Ellipse).containsPoint(this.getCorners()[i])) { - return true; - } + throw Error("Invalid Shape passed to overlaps, must be a Rectangle | Ellipse"); + } + } + + /** + * Method that checks if any edges of this rectangle overlap with the other rectangle. + * @param otherRect The other rectangle to be checked. + * @returns True, if edges overlap. Else, false. + * @todo This algo can and should be simplified to be less than 10 lines of code. -James-Oswald + */ + private edgesOverlap(otherRect: Rectangle): boolean { + const thisCorners = this.getCorners(); + const otherCorners = otherRect.getCorners(); + + if (thisCorners[0].y <= otherCorners[0].y && thisCorners[2].y >= otherCorners[0].y) { + //The top edge of the other rectangle is within the horizontal boundaries + //of this rectangle + if (thisCorners[0].x <= otherCorners[0].x && thisCorners[1].x >= otherCorners[0].x) { + //The left edge of the other rectangle is within the vertical boundaries + //of this rectangle + return true; + } else if ( + //The left edge of this rectangle is within the vertical boundaries + //of the other rectangle + otherCorners[0].x <= thisCorners[0].x && + otherCorners[1].x >= thisCorners[0].x + ) { + return true; + } else if ( + //The right edge of the other rectangle is within the vertical boundaries + //of this rectangle + thisCorners[0].x <= otherCorners[1].x && + thisCorners[1].x >= otherCorners[1].x + ) { + return true; + } else if ( + //The right edge of this rectangle is within the vertical boundaries + //of the other rectangle + otherCorners[0].x <= thisCorners[1].x && + otherCorners[1].x >= thisCorners[1].x + ) { + return true; } + + return false; + } else if (otherCorners[0].y <= thisCorners[0].y && otherCorners[2].y >= thisCorners[0].y) { + //The top edge of this rectangle is within the horizontal boundaries + //of the other rectangle + if (thisCorners[0].x <= otherCorners[0].x && thisCorners[1].x >= otherCorners[0].x) { + //The left edge of the other rectangle is within the vertical boundaries + //of this rectangle + return true; + } else if ( + //The left edge of this rectangle is within the vertical boundaries + //of the other rectangle + otherCorners[0].x <= thisCorners[0].x && + otherCorners[1].x >= thisCorners[0].x + ) { + return true; + } else if ( + //The right edge of the other rectangle is within the vertical boundaries + //of this rectangle + thisCorners[0].x <= otherCorners[1].x && + thisCorners[1].x >= otherCorners[1].x + ) { + return true; + } else if ( + //The right edge of this rectangle is within the vertical boundaries + //of the other rectangle + otherCorners[0].x <= thisCorners[1].x && + otherCorners[1].x >= thisCorners[1].x + ) { + return true; + } + + return false; + } else if (thisCorners[0].y <= otherCorners[2].y && thisCorners[2].y >= otherCorners[2].y) { + //The bottom edge of the other rectangle is within the horizontal boundaries + //of this rectangle + if (thisCorners[0].x <= otherCorners[0].x && thisCorners[1].x >= otherCorners[0].x) { + //The left edge of the other rectangle is within the vertical boundaries + //of this rectangle + return true; + } else if ( + //The left edge of this rectangle is within the vertical boundaries + //of the other rectangle + otherCorners[0].x <= thisCorners[0].x && + otherCorners[1].x >= thisCorners[0].x + ) { + return true; + } else if ( + //The right edge of the other rectangle is within the vertical boundaries + //of this rectangle + thisCorners[0].x <= otherCorners[1].x && + thisCorners[1].x >= otherCorners[1].x + ) { + return true; + } else if ( + //The right edge of this rectangle is within the vertical boundaries + //of the other rectangle + otherCorners[0].x <= thisCorners[1].x && + otherCorners[1].x >= thisCorners[1].x + ) { + return true; + } + + return false; + } else if (otherCorners[0].y <= thisCorners[2].y && otherCorners[2].y >= thisCorners[2].y) { + //The bottom edge of this rectangle is within the horizontal boundaries + //of the other rectangle + if (thisCorners[0].x <= otherCorners[0].x && thisCorners[1].x >= otherCorners[0].x) { + //The left edge of the other rectangle is within the vertical boundaries + //of this rectangle + return true; + } else if ( + //The left edge of this rectangle is within the vertical boundaries + //of the other rectangle + otherCorners[0].x <= thisCorners[0].x && + otherCorners[1].x >= thisCorners[0].x + ) { + return true; + } else if ( + //The right edge of the other rectangle is within the vertical boundaries + //of this rectangle + thisCorners[0].x <= otherCorners[1].x && + thisCorners[1].x >= otherCorners[1].x + ) { + return true; + } else if ( + //The right edge of this rectangle is within the vertical boundaries + //of the other rectangle + otherCorners[0].x <= thisCorners[1].x && + otherCorners[1].x >= thisCorners[1].x + ) { + return true; + } + return false; } + + return false; } /** diff --git a/src/AtomCreation.ts b/src/AtomCreation.ts index 639b3a94..6a7cb51a 100644 --- a/src/AtomCreation.ts +++ b/src/AtomCreation.ts @@ -68,7 +68,7 @@ function moveAtom(event: MouseEvent) { function atomUp() { const atomMetrics: TextMetrics = ctx.measureText(atom); const newRect: Rectangle = new Rectangle( - new Point(currentPoint.x, currentPoint.y + atomMetrics.actualBoundingBoxAscent), + new Point(currentPoint.x, currentPoint.y - atomMetrics.actualBoundingBoxAscent), atomMetrics.width, atomMetrics.fontBoundingBoxDescent + atomMetrics.actualBoundingBoxAscent ); diff --git a/src/AtomMode.ts b/src/AtomMode.ts new file mode 100644 index 00000000..581404a9 --- /dev/null +++ b/src/AtomMode.ts @@ -0,0 +1,108 @@ +import {Point} from "./AEG/Point"; +import {AtomNode} from "./AEG/AtomNode"; +import {redrawCut, tree} from "./index"; +import {Rectangle} from "./AEG/Rectangle"; + +const canvas: HTMLCanvasElement = document.getElementById("canvas"); +const res: CanvasRenderingContext2D | null = canvas.getContext("2d"); +if (res === null) { + throw Error("2d rendering context not supported"); +} +const ctx: CanvasRenderingContext2D = res; +let atomMetrics: TextMetrics; + +let hasMouseDown: Boolean = false; +let hasAtom: Boolean = false; +let currentAtom: AtomNode = new AtomNode(); + +/** + * Will compare the event given with all possible events it could be. + * keypress checks to see if the key was a letter and if yes sets it to that letter. + * mousedown sets the atom down, calculates the bounding box, and checks for what color. + * mousemove will alter the origin position and the starting vertex of the bounding box. + * mouseup will add the atom to the tree if it is in a valid location. + * mosueout will end drawing early. + * @param event The event that will be used + * @param event the event that will be used + */ +export function atomHandler(event: Event) { + if (event.type === "keypress") { + const thisEvent: KeyboardEvent = event; + const regex = new RegExp(/^[A-Za-z]$/); + if (regex.test(thisEvent.key)) { + currentAtom.identifier = thisEvent.key; + hasAtom = true; + } + } else if (event.type === "mousedown" && hasAtom) { + const thisEvent: MouseEvent = event; + atomMetrics = ctx.measureText(currentAtom.identifier); + const startVertex: Point = new Point( + thisEvent.clientX, + thisEvent.clientY - atomMetrics.actualBoundingBoxAscent + ); + currentAtom.rect = new Rectangle( + startVertex, + atomMetrics.width, + atomMetrics.fontBoundingBoxDescent + atomMetrics.actualBoundingBoxAscent + ); + currentAtom.origin = new Point(thisEvent.clientX, thisEvent.clientY); + + ctx.clearRect(0, 0, canvas.width, canvas.height); + redrawCut(tree.sheet); + if (tree.canInsert(currentAtom)) { + drawAtom(currentAtom, "#00FF00"); + } else { + drawAtom(currentAtom, "#6600ff"); + } + hasMouseDown = true; + } else if (event.type === "mousemove" && hasMouseDown) { + const thisEvent: MouseEvent = event; + currentAtom.origin = new Point(thisEvent.clientX, thisEvent.clientY); + currentAtom.rect.startVertex = new Point( + thisEvent.clientX, + thisEvent.clientY - atomMetrics.actualBoundingBoxAscent + ); + + ctx.clearRect(0, 0, canvas.width, canvas.height); + redrawCut(tree.sheet); + if (tree.canInsert(currentAtom)) { + drawAtom(currentAtom, "#00FF00"); + } else { + drawAtom(currentAtom, "#FF0000"); + } + } else if (event.type === "mouseup" && hasMouseDown) { + if (tree.canInsert(currentAtom)) { + tree.insert(currentAtom); + } + currentAtom = new AtomNode(currentAtom.identifier); + ctx.clearRect(0, 0, canvas.width, canvas.height); + redrawCut(tree.sheet); + hasMouseDown = false; + console.log(tree.toString()); + } else if (event.type === "mouseout" && hasMouseDown) { + hasMouseDown = false; + currentAtom = new AtomNode(currentAtom.identifier); + ctx.clearRect(0, 0, canvas.width, canvas.height); + redrawCut(tree.sheet); + } +} + +/** + * Draws the given atomNode with the given color. + * @param thisAtom the atomnode to be drawn. + * @param color the color of the atom. + */ +function drawAtom(thisAtom: AtomNode, color: string) { + ctx.fillStyle = color; + ctx.strokeStyle = color; + const displayBox = thisAtom.rect; + ctx.beginPath(); + ctx.fillText(thisAtom.identifier, thisAtom.origin.x, thisAtom.origin.y); + ctx.rect( + displayBox.startVertex.x, + displayBox.startVertex.y, + displayBox.width, + displayBox.height + ); + ctx.stroke(); +} diff --git a/src/CutMode.ts b/src/CutMode.ts new file mode 100644 index 00000000..657299f7 --- /dev/null +++ b/src/CutMode.ts @@ -0,0 +1,116 @@ +import {Point} from "./AEG/Point"; +import {CutNode} from "./AEG/CutNode"; +import {Ellipse} from "./AEG/Ellipse"; +import {redrawCut} from "./index"; +import {tree} from "./index"; + +const canvas: HTMLCanvasElement = document.getElementById("canvas"); +const res: CanvasRenderingContext2D | null = canvas.getContext("2d"); +const showRectElm: HTMLInputElement = document.getElementById("showRect"); +const modeElm: HTMLSelectElement = document.getElementById("mode"); +if (res === null) { + throw Error("2d rendering context not supported"); +} +const ctx: CanvasRenderingContext2D = res; + +let hasMouseDown: Boolean = false; +let currentEllipse: Ellipse = new Ellipse(); +let startingPoint: Point = new Point(); + +/** + * Will compare the event given with all possible events it could be. + * mousedown events will allocate the starting point and allow the later events to take place + * mousemove will call createEllipse starting and current points, if invalid place will color it. + * mouseup will add the cut to the tree if it is in a valid place, and set hasmousedown to false. + * mosueout will end drawing early. + * @param event The event that will be used + */ +export function cutHandler(event: MouseEvent) { + let newCut: CutNode = new CutNode(); + const currentPoint: Point = new Point(); + + if (event.type === "mousedown") { + hasMouseDown = true; + startingPoint.x = event.clientX; + startingPoint.y = event.clientY; + } else if (event.type === "mousemove" && hasMouseDown) { + currentPoint.x = event.clientX; + currentPoint.y = event.clientY; + ctx.clearRect(0, 0, canvas.width, canvas.height); + redrawCut(tree.sheet); + currentEllipse = createEllipse(startingPoint, currentPoint); + newCut.ellipse = currentEllipse; + + if (tree.canInsert(newCut) && currentEllipse.radiusX > 15 && currentEllipse.radiusY > 15) { + drawEllipse(newCut, "#00FF00"); + } else { + drawEllipse(newCut, "#FF0000"); + } + } else if (event.type === "mouseup" && hasMouseDown) { + newCut = new CutNode(currentEllipse); + if (tree.canInsert(newCut) && currentEllipse.radiusX > 15 && currentEllipse.radiusY > 15) { + tree.insert(newCut); + } + hasMouseDown = false; + startingPoint = new Point(); + ctx.clearRect(0, 0, canvas.width, canvas.height); + redrawCut(tree.sheet); + console.log(tree.toString()); + } else if (event.type === "mouseout" && hasMouseDown) { + hasMouseDown = false; + startingPoint = new Point(); + ctx.clearRect(0, 0, canvas.width, canvas.height); + redrawCut(tree.sheet); + } +} + +/** + * A function to draw an ellipse between two points designated by the user. + * @param original the point where the user originally clicked + * @param current the point where the user's mouse is currently located + */ +export function createEllipse(original: Point, current: Point): Ellipse { + const center: Point = new Point( + (current.x - original.x) / 2 + original.x, + (current.y - original.y) / 2 + original.y + ); + + const sdx = original.x - current.x; + const sdy = original.y - current.y; + const dx = Math.abs(sdx); + const dy = Math.abs(sdy); + let rx, ry: number; + + if (modeElm.value === "circumscribed") { + //This inscribed ellipse solution is inspired by the discussion of radius ratios in + //https://stackoverflow.com/a/433426/6342516 + const rv: number = Math.floor(center.distance(current)); + ry = Math.floor(rv * (dy / dx)); + rx = Math.floor(rv * (dx / dy)); + } else { + rx = dx / 2; + ry = dy / 2; + } + + if (showRectElm.checked) { + ctx.beginPath(); + ctx.rect(original.x, original.y, -sdx, -sdy); + ctx.stroke(); + } + + return new Ellipse(center, rx, ry); +} + +/** + * Draws the given cut onto the canvas. + * @param thisCut The cut containing the ellipse to be drawn + * @param color the line color of the ellipse + */ +function drawEllipse(thisCut: CutNode, color: string) { + ctx.strokeStyle = color; + const ellipse: Ellipse = thisCut.ellipse; + const center: Point = ellipse.center; + ctx.beginPath(); + ctx.ellipse(center.x, center.y, ellipse.radiusX, ellipse.radiusY, 0, 0, 2 * Math.PI); + ctx.stroke(); +} diff --git a/src/index.css b/src/index.css index 9fb2ec0c..86ef95c8 100644 --- a/src/index.css +++ b/src/index.css @@ -74,3 +74,9 @@ p { left: 0px; z-index: -1; } +.no-highlight{ + user-select: none; + -moz-user-select: none; + -webkit-text-select: none; + -webkit-user-select: none; + } \ No newline at end of file diff --git a/src/index.html b/src/index.html index 91986b17..bb253d6e 100644 --- a/src/index.html +++ b/src/index.html @@ -9,7 +9,7 @@