Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 30 additions & 1 deletion main/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import { NfpCache } from '../build/nfpDb.js';
import { HullPolygon } from '../build/util/HullPolygon.js';
import { Polygon } from '../build/util/polygon.js';

window.onload = function () {
const { ipcRenderer } = require('electron');
Expand Down Expand Up @@ -482,6 +483,11 @@ function toNestCoordinates(polygon, scale) {
};

function getHull(polygon) {
if (polygon instanceof Polygon) {
var hullPolygon = HullPolygon.hull(polygon);
return hullPolygon ? hullPolygon.toArray() : polygon.toArray();
}

// Convert the polygon points to proper Point objects for HullPolygon
var points = [];
for (let i = 0; i < polygon.length; i++) {
Expand All @@ -498,10 +504,33 @@ function getHull(polygon) {
return polygon;
}

return hullpoints;
return hullpoints instanceof Polygon ? hullpoints.toArray() : hullpoints;
}

function rotatePolygon(polygon, degrees) {
if (polygon instanceof Polygon) {
var rotated = polygon.rotate(degrees);
var rotatedArray = rotated.toArray();

// Preserve exact property from original points
var originalPoints = polygon.toArray();
for (let i = 0; i < rotatedArray.length && i < originalPoints.length; i++) {
if (originalPoints[i].exact) {
rotatedArray[i].exact = originalPoints[i].exact;
}
}

// Handle children if they exist
if (polygon.children && polygon.children.length > 0) {
rotatedArray.children = [];
for (let j = 0; j < polygon.children.length; j++) {
rotatedArray.children.push(rotatePolygon(polygon.children[j], degrees));
}
}

return rotatedArray;
}

var rotated = [];
var angle = degrees * Math.PI / 180;
for (let i = 0; i < polygon.length; i++) {
Expand Down
8 changes: 7 additions & 1 deletion main/deepnest.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import { Point } from '../build/util/point.js';
import { HullPolygon } from '../build/util/HullPolygon.js';
import { Polygon } from '../build/util/polygon.js';

const { simplifyPolygon: simplifyPoly } = require("@deepnest/svg-preprocessor");

Expand Down Expand Up @@ -119,6 +120,11 @@ export class DeepNest {
};

getHull(polygon) {
if (polygon instanceof Polygon) {
var hullPolygon = HullPolygon.hull(polygon);
return hullPolygon ? hullPolygon.toArray() : null;
}

var points = [];
for (let i = 0; i < polygon.length; i++) {
points.push({
Expand All @@ -131,7 +137,7 @@ export class DeepNest {
if (!hullpoints) {
return null;
}
return hullpoints;
return hullpoints instanceof Polygon ? hullpoints.toArray() : hullpoints;
};

// use RDP simplification, then selectively offset
Expand Down
5 changes: 4 additions & 1 deletion main/svgparser.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import '../build/util/domparser.js';
// Dependencies
import { Matrix } from '../build/util/matrix.js';
import { Point } from '../build/util/point.js';
import { Polygon } from '../build/util/polygon.js';

export class SvgParser {
constructor(){
Expand Down Expand Up @@ -1454,7 +1455,9 @@ export class SvgParser {
poly.pop();
}

return poly;
// Convert points to Point instances and create Polygon
var points = poly.map(p => new Point(p.x, p.y));
return new Polygon(points);
};

polygonifyPath(path){
Expand Down
49 changes: 34 additions & 15 deletions main/util/HullPolygon.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
// based on https://d3js.org/d3-polygon/ Version 1.0.2.

import { Point } from "./point.js";
import { Polygon } from "./polygon.js";

// Type definition for a polygon (array of points)
type Polygon = Point[];
// Type definition for a polygon (array of points) - keeping for backwards compatibility
type PolygonArray = Point[];

// Type for points with index used in hull calculation
interface IndexedPoint {
Expand All @@ -19,7 +20,11 @@ export class HullPolygon {
/**
* Returns the signed area of the specified polygon.
*/
public static area(polygon: Polygon): number {
public static area(polygon: Polygon | PolygonArray): number {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense to represent various properties in the type system? For example, we could have HullPolygon be a subclass of Polygon, and AxisAlignedRectangle as a subclass of HullPolygon. This would make some of this code simpler or easier to follow, hull() can return HullPolygon and so on. We can still override methods as needed for precision or speed, since eg area of AxisAlignedRectangle is very simple.

if (polygon instanceof Polygon) {
return polygon.area();
}

let i = -1;
const n = polygon.length;
let a: Point;
Expand All @@ -38,7 +43,11 @@ export class HullPolygon {
/**
* Returns the centroid of the specified polygon.
*/
public static centroid(polygon: Polygon): Point {
public static centroid(polygon: Polygon | PolygonArray): Point {
if (polygon instanceof Polygon) {
return polygon.centroid();
}

let i = -1;
const n = polygon.length;
let x = 0;
Expand All @@ -62,11 +71,12 @@ export class HullPolygon {

/**
* Returns the convex hull of the specified points.
* The returned hull is represented as an array of points
* The returned hull is represented as a new Polygon
* arranged in counterclockwise order.
*/
public static hull(points: Polygon): Polygon | null {
const n = points.length;
public static hull(points: Polygon | PolygonArray): Polygon | null {
const pointArray = points instanceof Polygon ? points.points : points;
const n = pointArray.length;
if (n < 3) return null;

let i: number;
Expand All @@ -75,8 +85,8 @@ export class HullPolygon {

for (i = 0; i < n; ++i) {
sortedPoints[i] = {
x: points[i].x,
y: points[i].y,
x: pointArray[i].x,
y: pointArray[i].y,
index: i,
};
}
Expand All @@ -99,26 +109,31 @@ export class HullPolygon {
const skipRight =
lowerIndexes[lowerIndexes.length - 1] ===
upperIndexes[upperIndexes.length - 1];
const hull: Polygon = [];
const hullPoints: Point[] = [];

// Add upper hull in right-to-left order.
// Then add lower hull in left-to-right order.
for (i = upperIndexes.length - 1; i >= 0; --i)
hull.push(points[sortedPoints[upperIndexes[i]].index]);
hullPoints.push(pointArray[sortedPoints[upperIndexes[i]].index]);
for (
i = skipLeft ? 1 : 0;
i < lowerIndexes.length - (skipRight ? 1 : 0);
++i
)
hull.push(points[sortedPoints[lowerIndexes[i]].index]);
hullPoints.push(pointArray[sortedPoints[lowerIndexes[i]].index]);

return hull;
return new Polygon(hullPoints);
}

/**
* Returns true if and only if the specified point is inside the specified polygon.
*/
public static contains(polygon: Polygon, point: Point): boolean {
public static contains(polygon: Polygon | PolygonArray, point: Point): boolean {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven’t looked at the uses of this function but it seems like the member function on Polygon should just be called directly.

if (polygon instanceof Polygon) {
const result = polygon.contains(point);
return result === true; // convert null to false
}

const n = polygon.length;
let p = polygon[n - 1];
const x = point.x;
Expand All @@ -145,7 +160,11 @@ export class HullPolygon {
/**
* Returns the length of the perimeter of the specified polygon.
*/
public static length(polygon: Polygon): number {
public static length(polygon: Polygon | PolygonArray): number {
if (polygon instanceof Polygon) {
return polygon.perimeter();
}

let i = -1;
const n = polygon.length;
let b = polygon[n - 1];
Expand Down
29 changes: 29 additions & 0 deletions main/util/geometryutil.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@

(function (root) {
"use strict";

// Import the new Polygon class - will be available if polygon.js is loaded
const Polygon = root.Polygon;

// private shared variables/methods

Expand Down Expand Up @@ -551,6 +554,10 @@

// returns the rectangular bounding box of the given polygon
getPolygonBounds: function (polygon) {
if (polygon instanceof Polygon) {
return polygon.bounds();
}

if (!polygon || polygon.length < 3) {
return null;
}
Expand Down Expand Up @@ -584,6 +591,10 @@

// return true if point is in the polygon, false if outside, and null if exactly on a point or edge
pointInPolygon: function (point, polygon, tolerance) {
if (polygon instanceof Polygon) {
return polygon.contains(point, tolerance);
}

if (!polygon || polygon.length < 3) {
return null;
}
Expand Down Expand Up @@ -633,6 +644,10 @@
// returns the area of the polygon, assuming no self-intersections
// a negative area indicates counter-clockwise winding direction
polygonArea: function (polygon) {
if (polygon instanceof Polygon) {
return polygon.area();
}

var area = 0;
var i, j;
for (i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
Expand Down Expand Up @@ -1503,6 +1518,10 @@
},

isRectangle: function (poly, tolerance) {
if (poly instanceof Polygon) {
return poly.isRectangle(tolerance);
}

var bb = this.getPolygonBounds(poly);
tolerance = tolerance || TOL;

Expand Down Expand Up @@ -2106,6 +2125,16 @@
},

rotatePolygon: function (polygon, angle) {
if (polygon instanceof Polygon) {
var rotated = polygon.rotate(angle);
var bounds = rotated.bounds();
rotated.x = bounds.x;
rotated.y = bounds.y;
rotated.width = bounds.width;
rotated.height = bounds.height;
return rotated;
}

var rotated = [];
angle = (angle * Math.PI) / 180;
for (var i = 0; i < polygon.length; i++) {
Expand Down
Loading
Loading