Git Product home page Git Product logo

flatten-js's Introduction

npm version Build Status Coverage Status @flatten-js/core

Rate on Openbase

Javascript library for 2d geometry

flatten-js is a javascript library for manipulating abstract geometrical shapes like point, vector, line, ray, segment, circle, arc and polygon. Shapes may be organized into Planar Set - searchable container which support spatial queries.

flatten-js provides a lot of useful methods and algorithms like finding intersections, checking inclusion, calculating distance, applying affine transformations, performing boolean operations and more.

Packages are distributed in 3 formats: commonjs, umd and es6 modules. Package.json file provides various entry points suitable for different targets.

TypeScript users may take advantage of static type checking with typescript definition file index.d.ts included into the package.

flatten-js does not concern too much about visualization. Anyway, all classes implement svg() method, that returns a string which may be inserted into SVG container. It works pretty well together with d3js library, but it is definitely possible to create bridges to other graphic libraries.

The best way to start working with FlattenJS is to use awesome Observable javascript interactive notebooks. Check out collection of Tutorials published in Observable Notebooks.

Full documentation may be found here: https://alexbol99.github.io/flatten-js/index.html

Contacts

Follow me on Twitter @alex_bol_

Installation

npm install --save @flatten-js/core

Usage

import {Point, Vector, Circle, Line, Ray, Segment, Arc, Box, Polygon, Matrix, PlanarSet} from '@flatten-js/core';

It is possible to import Flatten namespace as default import, and then destruct all classes from it.

import Flatten from '@flatten-js/core'
const {Point, Vector, Circle, Line, Ray, Segment, Arc, Box, Polygon, Matrix, PlanarSet} = Flatten;

Some classes have shortcuts to avoid annoying new constructor:

import {point, vector, circle, line, ray, segment, arc, polygon, matrix} from '@flatten-js/core';

Example

After module imported, it is possible to create some construction:

    // extract object creators
    import {point, circle, segment} from '@flatten-js/core';

    // make some construction
    let s1 = segment(10,10,200,200);
    let s2 = segment(10,160,200,30);
    let c = circle(point(200, 110), 50);
    let ip = s1.intersect(s2);

You may test the code above also in NPM RunKit

You may also check out examples section in the code which illustrate different use cases:

  • in nodejs
  • in a browser using <script> tag with unpkg.com loader
  • in a React application

Content of the library

Basic shapes

flatten-js library implements following basic shapes:

Polygon

Polygon in flatten-js library is actually a multi-polygon. Polygon is a collection of faces - closed oriented chains of edges, which may be of type Segment or Arc. The most external face called island, a face included into it is called hole. Holes in turn may have inner islands, number of inclusion levels is unlimited.

Orientation of islands and holes is matter for calculation of relationships and boolean operations, holes should have orientation opposite to islands. It means that for proper results faces in a polygon should be orientable: they should not have self-intersections. Faces also should not overlap each other. Method isValid() checks if polygon fit these rules.

Constructor of the polygon object accept various inputs:

  • Array of shapes (instances of Flatten.Segment or Flatten.Arc) that represent closed chains
  • Array of shapes as json objects that represent closed chains
  • Array of points (Flatten.Point) that represent vertices of the polygon
  • Array of numeric pairs [x,y] that represent vertices of the polygon
  • Instances of Circle or Box

Polygon provides various useful methods:

  • area - calculate area of a polygon
  • addFace - add a new face to polygon
  • deleteFace - removes face from polygon
  • addVertex - split an edge of polygon adn create new vertex
  • cut - cut polygon with multiline into sub-polygons
  • findEdgeByPoint - find edge in polygon
  • contains - test if polygon contains shape (point, segment or arc)
  • transform - transform polygon using affine transformation matrix
  • reverse - revert orientation of faces
  • splitToIslands - split to array of islands with holes

Multiline

Multiline represent an unclosed chain of edges of type Segment or Arc

Planar Set

Planar Set is a container of shapes that enables spatial seach by rectangular query.

Transformations

All the classes have methods translate, rotate and scale which may be chained.
Example:

// Rotate segment by 45 deg around its center
let {point,segment,matrix} = Flatten;
let s = segment(point(20,30), point(60,70));
let center = s.box.center;
let angle = 45.*Math.PI/180.;
let rotated_segment = s.rotate(angle, center)

Intersection points

All classes have method intersect(otherShape) that return array of intersection points, if two shapes intersect each other, or empty array otherwise. The is no predefined order of intersection points in the array.

Please don't be confused, there are another two methods BooleanOperations.intersect() that performs boolean intersection of polygons and logical predicate Relations.intersect() that check if two shapes intersected or not.

Distance between shapes

All basic classes and polygon have method distanceTo(othershape) that calculate distance to other shape. Together with the distance function returns the shortest segment between two shapes - segment between two closest point, where the first point lays on this shape, and the second - on the other shape, see example:

let s = segment(point(10,30), point(150, 40));
let c = circle(point(75,75),10);
let [dist,shortest_segment] = s.distanceTo(c);

Intersection model (DE-9IM)

The Dimensionally Extended nine-Intersection Model (DE-9IM) is a topological model and a standard used to describe the spatial relations of two geometries in 2-dimensional plane.

First, for every shape we define:

  • An interior
  • A boundary
  • An exterior

For polygons, the interior, boundary and exterior are obvious, other types have some exclusions:

  • Point has no interior
  • Line has no boundary

The DE-9IM model based on a 3ร—3 intersection matrix with the form:

         [ I(a) ^ I(b)   B(a) ^ I(b)   E(a) ^ I(b)
de9im =    I(a) ^ B(b)   B(a) ^ B(b)   E(a) ^ B(b)
           I(a) ^ E(b)   B(a) ^ E(b)   E(a) ^ E(b)  ]

where aand b are two shapes (geometries),

I(), B(), E() denotes interior, boundary and exterior operator and

^ denotes operation of intersection. Dimension of intersection result depends on the dimension of shapes, for example,

  • intersection between an interior of the line and an interior of the polygon is an array of segments
  • intersection between an interior of the line and boundary polygon is an array of points (may include segments in case of touching)
  • intersection between interiors of two polygons (if exists) will be a polygon.

DE-9IM matrix describes any possible relationships between two shapes on the plane.

DE-9IM matrix is available via method relate under namespace Relations.

Each element of DE-9IM matrix is an array of the objects representing corresponding intersection. Empty array represents case of no intersection. If intersection is not applicable (i.e. intersection with a boundary for a line which has no boundary), correspondent cell left undefined.

Intersection between two exteriors not calculated because usually it is meaningless.

let {relate} = Flatten.Relations;
// 
// define two shapes: polygon1, polygon2
//
let de9im = relate(polygon1, polygon2);
//
// explore 8 of 9 fields of the de9im matrix:
// de9im.I2I  de9im.B2I  de9im.E2I
// de9im.I2B  de9im.B2B  de9im.E2B
// de9im.I2E  de9im.B2E     N/A

Another common way to represent DE-9IM matrix is a string where

  • T represent intersection where array is not impty
  • F represent intersection where array is empty
  • . means not relevant or not applicable

String may be obtained with de9im.toString() method.

Relationship predicates

The spatial relationships between two shapes exposed via namespace Relations. The spatial predicates return true if relationship match and false otherwise.

let {intersect, disjoint, equal, touch, inside, contain, covered, cover} = Flatten.Relations;
// define shape a and shape b
let p = intersect(a, b);
console.log(p)             // true / false
  • intersect - shapes a and b have at least one common point
  • disjoint - opposite to intersect
  • equal - shapes a and b are topologically equal
  • touch - shapes a and b have at least one point in common but their interiors not intersect
  • inside - shape a lies in the interior of shape b
  • contain - shape b lies in the interior of shape b
  • covered - every point of a lies or in the interior or on the boundary of shape b
  • covered - every point of b lies or in the interior or on the boundary of shape a

Boolean operations

Boolean operations on polygons available via namespace BooleanOperations. Polygons in boolean operation should be valid: both operands should have same meaning of face orientation, faces should not overlap each other and should not have self-intersections.

User is responsible to provide valid polygons, boolean operation methods do not check validity.

let {unify, subtract, intersect, innerClip, outerClip} = BooleanOperations;
  • unify - unify two polygons and return resulted polygon
  • subtract - subtract second polygon from the first and return resulted polygon
  • intersect - intersect two polygons and return resulted polygon
  • innerClip - intersect two polygons and return boundary of intersection as 2 arrays. The first aray contains edges of the first polygon, the second - the edges of the second
  • outerClip - clip boundary of the first polygon with the interior of the second polygon

Implementation based on Weiler-Atherton clipping algorithm, described in the article Hidden Surface Removal Using Polygon Area Sorting

Serialization

All flatten-js shape objects may be serialized using JSON.stringify() method. JSON.stringify transforms object to string using .toJSON() formatter implemented in the class. JSON.parse restore object from a string, and then constructor can use this object to create Flatten object.

let {lint, point} = Flatten;
let l = line(point(4, 0), point(0, 4));
// Serialize
let str = JSON.stringify(l);  
// Parse and reconstruct
let l_json = JSON.parse(str);
let l_parsed = line(l_json);

Visualization

All classes provide svg() method, that create svg string that may be inserted into svg container element in a very straightforward way:

<body>
    <svg id="stage" width="500" height="500"></svg>
<script>
    const Flatten = window["@flatten-js/core"];
    const {point, circle, segment} = Flatten;

    // make some construction
    let s1 = segment(10,10,200,200);
    let s2 = segment(10,160,200,30);
    let c = circle(point(200, 110), 50);
    let ip = s1.intersect(s2);

    document.getElementById("stage").innerHTML = s1.svg() + s2.svg() + c.svg() + ip[0].svg();
</script>
</body>

Method svg() may accept as a parameter an object that enables to define several basic attributes of svg element: stroke, strokeWidth, fill, fillRule, fillOpacity, id and className. If attributes not provided, method svg() use default values.

Other packages

Other packages, published under scope @flatten-js/:

Name Description
@flatten-js/interval-tree Interval binary search tree
@flatten-js/boolean-op Boolean operations (deprecated, use this functionality from the core package)
@flatten-js/polygon-offset Polygon offset

Support

Buy Me A Coffee

flatten-js's People

Contributors

aballet avatar aiiak avatar alexbol-fls avatar alexbol99 avatar angrycat9000 avatar awhitty avatar axtavt avatar delmohf avatar dependabot[bot] avatar dotariel avatar earthiverse avatar ebshimizu avatar janmeier avatar joelrbrandt avatar oldrichdlouhy avatar ps-bzaragoza avatar redexp avatar rtm avatar saraedum avatar schelmo avatar spoiple avatar sqcrh avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

flatten-js's Issues

Intersect between two circles missing

Currently, when using the Relations.intersect function, an error is thrown when both params are Circle because the the relate function doesn't have a relateCircle2Circle

Would be useful when you need to find whether two circles intersect, without using Circle's intersect method and testing for a non-empty array.

Example for Adding a Tooltip to the Polygon With Data

Hello,
I am looking at:
https://observablehq.com/@alexbol99/flattenjs-tutorials-polygons
and would like to know if there is any way to add a name attribute to the polygon that can pop up when you move your mouse over the polygon in D3?
I'm using a planerSet with lots of polygons, I managed to get it showing on d3, but now I would like to do actions when the mouse hovers over the polygon.
How would you recommend to do this? It would be helpful if there was a tutorial on how to do that as well.
Thank you,

Zero division

  let shapes = [
    point(0, 0), point(200, 0),
    point(200, 0), point(200, 200),
    point(200, 200), point(0, 200),
    point(0, 200), point(0, 0),
  ];

  let polygon = new Polygon();
  polygon.addFace(shapes);
  let offsetPolygon = offset(polygon, 2);

I get an error:

Error: Zero division
    at Function.get ZERO_DIVISION [as ZERO_DIVISION]
    at Vector.normalize 
    at offsetSegment 
    at offset 

What am I doing wrong? And How do I create a simple square by points?

Face#svg not documented?

Is it intentional that Face#svg is not documented, perhaps because it's intended to be private?

"Drop-in geometry library" concept

I have a suggestion for this project.

I've recently tried working with an SVG graphics library, only to realize it can't really manipulate vectors very well on its own. I've actually noticed this is a theme among many graphics libraries.

The best in terms of 2d geometry is (by far) paper.js, but not everyone can use that library or wants to (it's a canvas library, first of all, and sometimes you might want to specifically use an SVG library). Also, even that library doesn't really have a complete toolset.

So I came up with this concept of a 2d geometry library that you can "drop-in" and use with other graphics libraries that actually do the rendering. The idea is to have lots of conversions (sometimes implicit ones) to and from different formats. This includes for lines, matrices, vector/point/complex numbers, etc.

I did a bit of work on it, and you can see the API I came up with here, including some implementations for things: https://github.com/GregRos/dropin-geometry

But I figured that there are lots of 2d geometry libraries for JS, it's a lot of work to make one, and there's no point if you can just add that functionality to other libraries.

Do you think this kind of thing is a good fit for this library?

BooleanOperations.intersect(a, b) doesn't seem to return correct results

Here is an observable notebook to play: https://observablehq.com/d/adead0595ff39aa7

  let {point, BooleanOperations, Polygon} = Flatten;  
  // Create new polygon
  let a = new Polygon([
    point(10,10), 
    point(10, 100),
    point(100, 100), 
    point(100, 10), 
  ]);
  let b = new Polygon([
    point(90,50), 
    point(120, 10),
    point(120, 100)
  ]);
  
  let stage = d3.select(DOM.svg(400, 400));
  stage.html(a.svg() + b.svg() + BooleanOperations.intersect(a, b).svg({fill: 'red'})); 

I expect the intersection to be there, but seems like return path is empty:

image

Intersect between a line and polygon doesn't work

If you try to intersect a line and a polygon you get:
Cannot read property 'not_intersect' of undefined (line: 322)
because the function intersectShape2Polygon wants the line to have a box which it does not have.

could you please support ellipse?

hi alexbol99!
I use flatten-js in my project to draw window and door. it works very well right now.
but I need to draw ellipse window. could you please support ellipse

Problem with ray intersection?

I'm sure I'm just doing something stupid, but can anyone shed any light on the below?

jsFiddle

import { Vector, Point, Ray, Segment } from '@flatten-js/core';
const segment = new Segment(new Point(0, 0), new Point(1, 0));
const origin = new Point(0.5, 0.5);
const vector = new Vector(origin, new Point(0.5, 0.4));
const ray = new Ray(origin, vector);
const intersections = ray.intersect(segment);

I would expect the ray (starting at { x: 0.5, y: 0.5 }, with a vector heading negative y) would intersect with the segment ({ x: 0, y: 0 } to { x: 1, y: 0 }) at a point on that segment: { x: 0.5, y: 0 }.

But intersections.length === 0. What am I doing wrong?

image

comment is not right

in point.js line 93 :"point1.x < point2.y ".
It seems it should be "point1.x < point2.x ".

Intersect and subtract boolean operations fail when passed empty polygons

I get an error when passing an empty polygon as one of the arguments to the intersect or subtract boolean operations. Eg see the console output at https://codesandbox.io/s/late-https-8sjsf?file=/src/index.js

I expected output for intersect to be empty polygon.

I expected output for subtract to be same as the first polygon passed.

If the current behaviour is an error I'm happy to submit a PR.

In my browser console the error looks like:

Uncaught (in promise) TypeError: element is undefined
    toArray modules.js:160363
    get edges modules.js:164770
    get shapes modules.js:164778
    clone modules.js:165370
    subtract modules.js:158791

Intersect does not seem to work when a shape is inside another

const { polygon } = require('@flatten-js/core');
const { intersect } = require('@flatten-js/boolean-op');

const item1 = polygon([
  [0, 30],
  [30, 30],
  [30, 0],
  [0, 0],
  [0, 30],
]);

const item2 = polygon([
  [10, 20],
  [20, 20],
  [20, 10],
  [10, 10],
  [10, 20],
]);

const intersection = intersect(item1, item2);

console.log('item1', item1.svg());
console.log('item2', item2.svg());
console.log('intersection', intersection.svg());

Prints:

<path stroke="black" stroke-width="1" fill="lightcyan" fill-rule="evenodd" fill-opacity="1"   d="
M0,30 L30,30 L30,0 L0,0 L0,30 L0,30 z" >
</path>
item2 
<path stroke="black" stroke-width="1" fill="lightcyan" fill-rule="evenodd" fill-opacity="1"   d="
M10,20 L20,20 L20,10 L10,10 L10,20 L10,20 z" >
</path>
intersection 
<path stroke="black" stroke-width="1" fill="lightcyan" fill-rule="evenodd" fill-opacity="1"   d="" >
</path>

I also tried using the intersect method on the polygon object:

const { polygon } = require('@flatten-js/core');

const item1 = polygon([
  [0, 30],
  [30, 30],
  [30, 0],
  [0, 0],
  [0, 30],
]);

const item2 = polygon([
  [10, 20],
  [20, 20],
  [20, 10],
  [10, 10],
  [10, 20],
]);


console.log('item1', item1.svg());
console.log('item2', item2.svg());
console.log('intersection', item1.intersect(item2));

Which prints:

item1 
<path stroke="black" stroke-width="1" fill="lightcyan" fill-rule="evenodd" fill-opacity="1"   d="
M0,30 L30,30 L30,0 L0,0 L0,30 L0,30 z" >
</path>
item2 
<path stroke="black" stroke-width="1" fill="lightcyan" fill-rule="evenodd" fill-opacity="1"   d="
M10,20 L20,20 L20,10 L10,10 L10,20 L10,20 z" >
</path>
intersection []

Support for basic vector operations

The various distanceTo() methods have been of great use to me. Now I wanted to use the library for some basic operations, like adding and subtracting vectors, but I don't seem to find them in documentation or code. The closest thing I can find is Point.translate(), but that's tied to Point and does not include other vector operations.

Am I missing something, or are these vector operations currently not supported?

Infinite loop for boolean union over (valid) polygons.

I found two polygons for which a union operation causes an infinite loop. I did some digging and tracked it down to an apparently infinite CircularLinkedList. I put a limit on the number of iterations in CircularLinkedList and made it throw an exception when it exceeds 1,000,000 iterations, which then gives me this stack trace when I run the unify function in a test:

     Error: CircularLinkedList iteration exceeded limit.
      at Object.next (src/data_structures/circular_linked_list.js:29:25)                                                                                                                                          
      at Face.setArcLength (src/classes/face.js:281:9)                                                                                                                                                            
      at new Face (src/classes/face.js:130:18)                                                                                                                                                                    
      at Polygon.addFace (src/classes/polygon.js:146:17)                                                                                                                                                          
      at restoreFaces (src/algorithms/boolean_op.js:755:28)                                                                                                                                                       
      at swapLinksAndRestore (src/algorithms/boolean_op.js:174:5)                                                                                                                                                 
      at booleanOpBinary (src/algorithms/boolean_op.js:204:9)                                                                                                                                                     
      at unify (src/algorithms/boolean_op.js:30:32)                                                                                                                                                               
      at Context.<anonymous> (test/algorithms/boolean_op.js:636:33)                                                                                                                                               
      at processImmediate (internal/timers.js:456:21)

The changed iterator function is:

    [Symbol.iterator]() {
        let element = undefined;
        let count = 0;
        return {
            next: () => {
                let value = element ? element : this.first;
                let done = this.first ? (element ? element === this.first : false) : true;
                element = value ? value.next : undefined;
                count++;
                if (count > 1000000) {
                    throw Error("CircularLinkedList iteration exceeded limit.")
                }
                return {value: value, done: done};
            }
        };
    };

The test I ran is:

import { infiniteLoopPolygons}  from './boolean_op_data';
const polygons = infiniteLoopPolygons.map(p => new Polygon(p));
const result = unify(...polygons);

The polygons are attached.
boolean_op_data.zip

Can you please explain why norm of Line is 90 CCW from it vector?

Hello, I'm trying to figure out why Flatten.Line has norm vector which is 90 CCW from original vector? It was surprise for me that to build vertical line code should be NOT

new Line(new Point(10, 10), new Vector(0, 1))

it should be

new Line(new Point(10, 10), new Vector(-1, 0))

instead. In docs it says just norm - normal vector to a line, nothing about 90 CCW.

Thanks great lib.

misleading use of JSON term in documentation

Multiple times the documentation mentions 'json structure' or 'json object'. None of those are valid terminology and can be confusing. JSON is a string representation of a JS object, not an object. The documentation would be more accurate if terms like 'object' and 'JSON string' were used where appropriate.

Intersect between a line and circle doesn't work

If I have a circle and a line passing through I would expect intersect to return an array on points where the line passes through the diamater of the circle. As far as I can tell what intersect acceally returns is an array of points where the line passes through the bounding box of the circle.

let center = new Flatten.Point(width / 2, width / 2)
let circle = new Flatten.Circle(center, width / 3)
let eighth = new Flatten.Line(center, new Flatten.Vector(-1, 1))
let points = circle.intersect(eighth)

See this observable

How to convert between different formats?

I wanted to use FlattenJS for just it's polygon boolean operations (since I already have implemented other functionality otherwise) and for that want to convert my polygon format into FlattenJS format.
For that I use the following function: return Flatten.polygon(this.points.map(p => Flatten.point(p.x, p.y)));
this.points is an array of points Point: {x, y}. For a hexagon I would have 6 points at the corners for example.This actually works fine.

But when I want to convert the result of a boolean intersection back the polygon seems to be intersecting itself (even though .isValid returns true). This is the function I use to convert them back:
var points = intersection.vertices.map(p => new Point(p.x, p.y))
new Point is the same type I used above.
It seems like the vertices of flattenJs seem to be a bit more complicated. How would I go about converting them to just an array of points, each point being connected to the next one?

What is the difference between Segment and Line?

Hello, can you please explain what is the difference between these two classes? For me it looks like Segment is same as Line only with more methods.
Also, can you please help me to figure out easy way to change length of segment?
And last one, if there any question can I post them on stackoverflow with tag flatten-js (do you watch on them) or it will be easier for you if post questions here?

Thank you!

Division by zero error when checking if polygon contains a point

The following code causes a Zero division exception. Because Chrome is struggling with the source map and doesn't point to the right lines as I try to detect where this is happening, I'm lazily submitting it here:

             const Flatten = window.flatten;
             const points = [
                 new Flatten.Point(-0.0774582, 51.4791865),
                 new Flatten.Point(-0.0784252, 51.4792941),
                 new Flatten.Point(-0.0774582, 51.4791865)
             ]
             const poly = new Flatten.Polygon();
             poly.addFace(points);
             const pp = new Flatten.Point(-0.07776044568759738, 51.47918678917519);
             console.log(poly.contains(pp));

EDIT: reduced number of points causing error

Allow other shapes to be passed into the PlanarSet.search function

Hello,
I am trying to make a walking algorithm where I have old_x, old_y, new_x, and new_y, and I need to check if there are items between those points.

Plane has a great check point function, but being able to only pass in a bounding box means I need to not use Plane for checking a line, and instead iterate through all the objects and use Line.intersect, which defeats the reason for having a plane in the first place.

So I think it would be very nice to have just one search function that could take a point, bounding box, polygon, line, circle, or any shape, and run the respective intersect functions on all the polygons.
You could make a point that shapes other than point and box are slower, but in my case, it doesn't matter.
Thanks,

Is there a way to combine multiple shapes together that are overlapping or touching?

Am I able to have two shapes, where there is some overlap between the two, and somehow combine them into a single custom polygon that combines the two shapes together?

Frame 21

In the picture above, if I have the left side, how can I get the right side? Is there some sort of algorithm or simple function to call?

I need this because I'd like to calculate the overall surface area that multiple shapes may take up, in a given box. I know if I just added up surface areas, that there would be errors if there is overlap.

broken with node v6.14.2

our platform has switched from node v6.11.5 -> v6.14.2
We can't use flatten-js anymore

// require package
let Flatten = require('flatten-js');

generates an error:

./workspace/Electron/idscan-electron/node_modules/flatten-js/classes/point.js:232
let {r, stroke, strokeWidth, fill, ...rest} = attrs;

any clue?
Rgds

Question on comparing overlap of two segments: How to handle floating point errors.

The setup:

  • I have two rectangles(as polygons) that have the same rotation, but have different sizes.
  • The rectangles are touching and I want to move them so the length of the touching segment is maximized.
  • I do this by calculating the intersection of the touching sides (as segments).

The problem:
Because of floating point calculations, the rectangles are sometimes slightly apart and it is not possible to directly calculate the overlap.

Do you have any idea how to make this more robust?
One of my ideas was to use the flatten/offset library to make the rectangles or the side-segments larger and then calculate the intersection on those. This should make the calculation more stable.

planarSet.search does not handle inbetween multipolygons or holes with polygons

Hello,
If I do the planarSet.search function and the box I pass in is in the hole of a polygon, or between two parts of a multipolygon, it returns true.
Here's the code:

const plane = new PlanarSet()
const poly = new Polygon()
poly.addFace([
	new Point(1,1),
	new Point(5,1),
	new Point(5,5),
	new Point(1,5)
])
poly.addFace([
	new Point(8,1),
	new Point(8, 4),
	new Point(11,4),
	new Point(11,1)
])

//Hole face
poly.addFace([
	new Point(10,2),
	new Point(10,3),
	new Point(9,3),
	new Point(9,2)
])

plane.add(poly)

//The lines that we pass to the search function
const intersectLine = new Segment(new Point(3,5), new Point(3,6))
const parallelLine = new Segment(new Point(6,1), new Point(6, 2)) // Should be empty, but has poly
const perpLine = new Segment(new Point(6,2), new Point(7,2)) // should be empty, but has poly
const insideLine = new Segment(new Point(2,2), new Point(2,3))
const inHoleLine = new Segment(new Point(9.5,2.5), new Point(9.9, 2.5)) // should be empty, but it has poly

//Function to test the search function
function t(l){
	return plane.search(l)
}

//print the results of the search function to the console
console.log(t(parallelLine)) // should be empty
console.log(t(perpLine)) // should be empty
console.log(t(intersectLine)) // should have the polygon
console.log(t(insideLine)) // should have the polygon
console.log(t(inHoleLine  )) // should be empty

Add ability to set tolerance

I'm using Flatten to model real world geometry. For my use anything less that 0.1 mm is essentially zero. I would like to be able to set the DP_TOL value a custom value instead of having it hard coded as a constant.

See an example on Runkit

Arc direction is undefined when parameter counterClockwise omitted

When in arc constructor the last parameter counterClockwise is omitted, like this:

let arc = new Flatten.Arc(point(0,1000),980, 0, 2*Math.PI)

property counterClockwise is set to undefined, while its value should be set to the default value Flatten.CCW, which is true.
It will be fixed in the ongoing release 0.6.4

Allow creation of Polygon from a Nested Array of Points

I have a number of arrays, such as [[1,1], [1,2], [2,2], [2,1]], and [[[1,1],[1,3],[2,3],[2,1]], [[2,2], [2,3], [4,3], [4,2]]].
That describe a single polygon and multipolygon respectively. The first function I need to add for any project I have is the code to convert from the nested points, to the Flatten polygons.
If there could be a function that would do this for me, it would make the library much easier to work with when implementing into a new project.

Typescript error?

Maybe it is because I am inexperienced with Typescript, but if you import something in Typescript it tries to load the default property of the exported object, which gives me the error:

new flatten_js_1.default.Point(x, y);
TypeError: Cannot read property 'Point' of undefined

because in the index.js is only module.exports = f (if I change it to
module.exports.default = f;
it fixes the problem for me)

distanceTo between Polygons: First point of segment is not always on the 'this' polygon

Version: 1.2.10

According to documentation this should be the case. An example to show the problem:

import Flatten from "@flatten-js/core";
import Box = Flatten.Box;
import Polygon = Flatten.Polygon;

function buildPoly(x: number, y: number) {
    let width = 200;
    let height = 50;
    let rec = new Box(
        x,
        y,
        x + width,
        y + height
    );
    return new Polygon(rec);
}

const p1 = buildPoly(0, 0);
const p2 = buildPoly(0, 500);

const [distance, segment] = p1.distanceTo(p2);

console.log("Polygons", p1.vertices, p2.vertices)
console.log("Segment Start: ", segment.start, p1.contains(segment.start));
console.log("Segment End: ", segment.end, p2.contains(segment.end));

Which results in segment.start being part of p2, and not p1 as expected.

The Typescript definitions aren't up to date

If you look closely, some definitions are off.

For example - in the definitions, it's allowed to pass an instance of Box to the constructor of a Polygon. The documentation doesn't mention it and it doesn't work.

Another instance are the Box toSegments and toPoints methods. They aren't present in the type definitions at all.

Infinite loop error in Relations.relate()

Examples Code:

import { Polygon, Relations } from "@flatten-js/core";

let polygonA = new Polygon(JSON.parse('[[{"pc":{"x":361.86046511627904,"y":358.1395348837209,"name":"point"},"r":3.7013112186046513,"startAngle":0.8060492302297078,"endAngle":4.549840858948246,"counterClockwise":false,"name":"arc"},{"ps":{"x":361.26146984929693,"y":354.4870139177165,"name":"point"},"pe":{"x":355.3805768669687,"y":355.45145110913296,"name":"point"},"name":"segment"},{"pc":{"x":356.27906976744185,"y":360.93023255813955,"name":"point"},"r":5.551966827906977,"startAngle":4.549840858948247,"endAngle":0.8060492302297152,"counterClockwise":false,"name":"arc"},{"ps":{"x":360.12299918636324,"y":364.936295747922,"name":"point"},"pe":{"x":364.42308472889334,"y":360.8102436769092,"name":"point"},"name":"segment"}]]'));
let polygonB = new Polygon(JSON.parse('[[{"pc":{"x":356.27906976744185,"y":360.93023255813955,"name":"point"},"r":5.551966827906977,"startAngle":4.569083935001064,"endAngle":1.9978954813868353,"counterClockwise":false,"name":"arc"},{"ps":{"x":353.979265872936,"y":365.9834728754998,"name":"point"},"pe":{"x":359.7242924817441,"y":368.59811887275936,"name":"point"},"name":"segment"},{"pc":{"x":362.7906976744186,"y":361.86046511627904,"name":"point"},"r":7.402622437209303,"startAngle":1.9978954813868424,"endAngle":4.56908393500106,"counterClockwise":false,"name":"arc"},{"ps":{"x":361.7334917412655,"y":354.5337240561091,"name":"point"},"pe":{"x":355.48616531757705,"y":355.4351767630121,"name":"point"},"name":"segment"}]]'));
try {
    let val = Relations.relate(polygonA, polygonB);
} catch (err) {
    console.error(err);
}

Error:

Error: Infinite loop
    at Function.get (flatten-js.esm.js:159)
    at Function.testInfiniteLoop (flatten-js.esm.js:215)
    at restoreFaces (flatten-js.esm.js:1322)
    at swapLinksAndRestore (flatten-js.esm.js:526)
    at booleanOpBinary (flatten-js.esm.js:555)
    at intersect (flatten-js.esm.js:405)
    at relatePolygon2Polygon (flatten-js.esm.js:2786)
    at Object.relate (flatten-js.esm.js:2613)

Add Holes Into Polygons

It would be very useful to specify a whole in a polygon using either a new method, such as addHole, or the addFace method. The area method states: "Returns area of the polygon. Area of an island will be added, area of a hole will be subtracted"

So it looks as if holes were contemplated at one point in time.

intersect for box and line or segment doesn't exist

Box isn't a shape inside the intersect method for either segment or line. So passing a box to their intersect method fails.

I didn't see anywhere the intersectLine2Box(line, box) function was exported. Though its used internally.

I took the three function: intersectLine2Box, intersectSegment2Line and intersectLine2Line: They work for what I'm doing..

Great library! thanks for all your work!

Is there a way to find the center point of a shape?

I need to rotate a rectangle (I'm using a polygon to represent it) by using it's center point as pivot. Is there a way to determine the center point of a shape (or at least a polygon)? I didn't find anything in the documentation (maybe it's named differently).

On a side note: I'm not sure that using 0,0 for the rotation center is a good default. What do you think about using the center of the shape that is being rotated by default?

Vector.multiple

In the index.d.ts you are declaring the method Vector.multiple (line 252) although in the documentation and the implementation it is named multiply.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.