Coder Social home page Coder Social logo

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  avatar

flatten-js's Issues

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,

comment is not right

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

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

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

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.

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?

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.

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?

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

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

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

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!

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.

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

"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?

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 []

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)

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.

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

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.

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.

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?

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

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

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)

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

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

Face#svg not documented?

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

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.

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!

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.

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,

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?

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.

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.

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.

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.