Giter VIP home page Giter VIP logo

points's Introduction

Points

A specification for storing shape data in Javascript. Includes functions for adding, removing, reordering, converting and manipulating points.

If working with SVG you might find it well paired with svg-points.

If you are looking to convert a SVG DOM node directly to points, then check out the frameShape function of Wilderness DOM node.

4.0kb gzipped.

Example shape

const shape = [
  { x: 50, y: 30, moveTo: true },
  { x: 50, y: 70, curve: { type: 'arc', rx: 20, ry: 20, sweepFlag: 1 } },
  { x: 50, y: 30, curve: { type: 'arc', rx: 20, ry: 20, sweepFlag: 1 } }
]

Functions

  • add() – add additional points to a shape
  • boundingBox() – get a shape's bounding box and center coordinates
  • cubify() – convert shape's curves to cubic beziers
  • length() – get a shape's length
  • moveIndex() – change the starting point of a shape
  • offset() – offset a shape
  • position() – find the coordinates and angle at a specific point of a shape
  • remove() – remove unrequired points of a shape
  • reverse() – reverse the order of points of a shape
  • rotate() – rotate a shape
  • scale() – scale a shape

Specification

A shape is an array of 2 or more point objects.

A line should be drawn between each point in a shape.

Adding a moveTo property to a point indicates that a line should not be drawn to that point from the previous.

The first point in a shape must include the moveTo property.

Point types

Each point is somewhere on an x, y plane. Therefore, at the very least each point object requires x and y properties. Values should be numeric.

Basic

{ x: 10, y: 25 }

Arc

{
  x: 80,
  y: 35,
  curve: {
    type: 'arc',
    rx: 2,
    ry: 2,
    xAxisRotation: 45,
    sweepFlag: 1,
    largeArcFlag: 1
  }
}

The curve properties xAxisRotation, sweepFlag and largeArcFlag are all optional and if missing are assumed to be 0.

Quadratic bezier

{
  x: 100,
  y: 200,
  curve: {
    type: 'quadratic',
    x1: 50,
    y1: 200
  }
}

Cubic bezier

{
  x: 5,
  y: 10,
  curve: {
    type: 'cubic',
    x1: 2,
    y1: 0,
    x2: 3,
    y2: 10
  }
}

Installation

npm install points

Function usage

add

import { add } from 'points'
const newShape = add(shape, 25)

Takes an existing shape array as the first argument, and the total number of desired points as the second argument. Adds points without changing the shape and returns a new shape array.

boundingBox

import { boundingBox } from 'points'
const { top, right, bottom, left, center } = boundingBox(shape)

Takes an existing shape array, or an array of shape arrays, as the only argument and returns an object of bounding coordinates including a center property containing the x, y values.

cubify

import { cubify } from 'points'
const newShape = cubify(shape)

Takes an existing shape array as the only argument, or an array of shape arrays, and converts any arc or quadratic bezier points to cubic bezier points.

Returns a new shape array or an array of shape arrays, depending on input.

length

import { length } from 'points'
const value = length(shape, 1)

Takes an existing shape array as the first argument. The optional second argument takes a number above 0 but below 180. This second argument is the accuracy (in degrees) used to calculate when a curve is straight enough to be considered a straight line. Returns the length of the shape.

moveIndex

import { moveIndex } from 'points';
const newShape = moveIndex(shape, 3);

Takes an existing shape array as the first argument, and the desired number of points to shift the index as the second argument (this can be a negative integer too). Returns a new shape array.

offset

import { offset } from 'points'
const newShape = offset(shape, 10, 20)

Takes an existing shape array, or an array of shape arrays, as the first argument, the horizontal offset as the second argument, and the vertical offset as the third argument.

Returns a new shape array or an array of shape arrays, depending on input.

position

import { position } from 'points'
const { angle, x, y } = position(shape, 0.5, 1)

Takes an existing shape array as the first argument, and an interval (a number from 0 to 1) as the second argument. The optional third argument takes a number above 0 but below 180. This third argument is the accuracy (in degrees) used to calculate when a curve is straight enough to be considered a straight line. Returns an object that includes the x and y coordinates at the interval of the shape, and the angle of that point with the vertical.

remove

import { remove } from 'points'
const newShape = remove(shape)

Takes an existing shape array, or an array of shape arrays, as the only argument, and removes any points that do not affect the shape.

Returns a new shape array or an array of shape arrays, depending on input.

reverse

import { reverse } from 'points'
const newShape = reverse(shape)

Takes an existing shape array, or an array of shape arrays, as the only argument, and reverses the order of the points.

Returns a new shape array or an array of shape arrays, depending on input.

rotate

import { rotate } from 'points'
const newShape = rotate(shape, 45)

Takes an existing shape array, or an array of shape arrays, as the first argument. Takes the clockwise angle of rotation as the second argument.

Returns a new shape array or an array of shape arrays, depending on input.

scale

import { scale } from 'points'
const newShape = scale(shape, 0.5, 'topLeft')

Takes an existing shape array, or an array of shape arrays, as the first argument. Takes the scale factor as the second argument and an anchor point as the third argument.

The anchor point can take any of the following strings:

  • center (default)
  • topLeft
  • topRight
  • bottomRight
  • bottomLeft

Returns a new shape array or an array of shape arrays, depending on input.

CommonJS

This is how you get to the good stuff if you're using require.

const Points = require('points')
const add = Points.add
const boundingBox = Points.boundingBox
const cubify = Points.cubify
const moveIndex = Points.moveIndex
const offset = Points.offset
const position = Points.position
const remove = Points.remove
const reverse = Points.reverse
const scale = Points.scale

UMD

And if you just want to smash in a Javascript file you're also covered. Drop this in place ...

https://unpkg.com/points/dist/points.min.js

Then access it on the Points global variable.

const add = Points.add
const boundingBox = Points.boundingBox
const cubify = Points.cubify
const moveIndex = Points.moveIndex
const offset = Points.offset
const position = Points.position
const remove = Points.remove
const reverse = Points.reverse
const scale = Points.scale

Help make this better

Issues and pull requests gratefully received!

I'm also on twitter @colinmeinke.

Thanks 🌟

License

ISC.

points's People

Contributors

colinmeinke avatar npmcdn-to-unpkg-bot 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

Watchers

 avatar  avatar  avatar  avatar

points's Issues

boundingBox maximum call stack size exceeded

Issue received via email:

I am using your svg-points & points modules - thank you for those nicely
done libraries. However, after converting some path data to points and
then passing it to points.boundingbox, I got

"RangeError: Maximum call stack size exceeded
at straighten
(C:\Users\me\AppData\Roaming\npm\node_modules\less-plugin-zvg\node_modules\points\lib\decurve.js:80:37)"

The path data follows after this message. If you need a short demo
script, I could send that.

The points I get back from passing this string to toPoints trace the
outline of the shape reasonably well so I think that part is working

d="M1536 1312v-1120q0 -50 -34 -89t-86 -60.5t-103.5 -32t-96.5 -10.5t-96.5
10.5t-103.5 32t-86 60.5t-34 89t34 89t86 60.5t103.5 32t96.5 10.5q105 0 192
-39v537l-768 -237v-709q0 -50 -34 -89t-86 -60.5t-103.5 -32t-96.5
-10.5t-96.5 10.5t-103.5 32t-86 60.5t-34 89
t34 89t86 60.5t103.5 32t96.5 10.5q105 0 192 -39v967q0 31 19 56.5t49
35.5l832 256q12 4 28 4q40 0 68 -28t28 -68z"

`cubify` returns weird shape, or I use it worng

Hello @colinmeinke, first of all thank you for making this repository public.

I'm have a need to transform an arc to cubic bezier in my svg. And for that I'm trying to use cubify method. But for some reason it returns me wrong result or I'm doing something wrong. I hope you may help me.

So to be more specific. I've tried to do the following

var mapToCubicString = (c) => `C${cutN(c.x)} ${cutN(c.y)} ${cutN(c.curve.x1)} ${cutN(c.curve.y1)} ${cutN(c.curve.x2)} ${cutN(c.curve.y2)}`;
var cubic = _.tail(points.cubify([
            { x: 50, y: 30, moveTo: true },
            { x: 50, y: 70, curve: { type: 'arc', rx: 20, ry: 20, sweepFlag: 1 } },
            { x: 50, y: 30, curve: { type: 'arc', rx: 20, ry: 20, sweepFlag: 1 } },
        ]
    )).map(mapToCubicString);

mapToCubicString will be equal to:

[ 
  'C70 50 61.046 30 70 38.954',
  'C50 70 70 61.046 61.046 70',
  'C30 50 38.954 70 30 61.046',
  'C50 30 30 38.954 38.954 30' 
]

after some manipulations we will get such svg:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 832.97 218">
    <defs>
        <style>
            .cls-1{opacity:0.7;}.cls-2{fill:#fff;}
        </style>
    </defs>
    <path stroke="#00f" class="cls-2 bet_8" d="M62.25 134.49C70 50 61.046 30 70 38.954C50 70 70 61.046 61.046 70C30 50 38.954 70 30 61.046C50 30 30 38.954 38.954 30" />
</svg>

which will lead to the following image:
screen shot 2016-12-05 at 17 26 59

Although we expect to have something like that:
screen shot 2016-12-05 at 17 27 29

Please let me know if there are not enough details about this matter.
Kind regards,
Dmitrijs.

ISSUE: Decurve Function not work

Hi @colinmeinke, decurve function causes maxium call excedded, below i have log (maybe helps you).
I changed code place/structure for my own work, but what function caused i have

[LOGGY.JS, pre-build]: executed via [decurve(shape, [[arguments]]) >> straight]
[{
    Browser: Chrome,
    Version: 52.1,
    Task Manager: [{title:'undefined',path:'file:///D:/Apps/points.js/compiled/points.html'}]
    OS: Windows7,
    Machine: Intel,
    GPU: Intel,
    CPU Usage: [{amount:69,unit:'%'}, {count:2,used:2}],
    Memory Usage: [{amount:57,unit:'%'}, {total:3221225472,used:1836098519}],
    Model: "Acer 5610z",
    Manf: "Acer, Inc."
},loggyjs:!0,date:[[22, 30], [17,10,2016]]]

points.js:1222 Uncaught RangeError: Maximum call stack size exceeded
>> straight @ points.js:1222
>> straighten @ points.js:1239
>> straighten @ points.js:1250
>> straighten @ points.js:1250
>> straighten @ points.js:1250
>> straighten @ points.js:1250
>> straighten @ points.js:1250
>> straighten @ points.js:1250
>> straighten @ points.js:1250
>> straighten @ points.js:1250
>> angleFromSides:NaN @ points.js:1250

add function adding incorrect points

The add function is adding incorrect points in this situation. I've not yet identified the cause.

const points = toPoints({
  type: 'path',
  d: 'M481.41,123.93l-8.41-8.42l28.8-28.8c2.32-2.32,6.09-2.32,8.41,0.01c2.32,2.32,2.32,6.08,0,8.41L481.41,123.93z M550.32,126.84c-2.32-2.32-6.09-2.32-8.41,0l-28.8,28.8l8.42,8.42l28.8-28.8C552.65,132.93,552.65,129.16,550.32,126.84z M459.08,108.07c-0.18-0.18-0.47-0.17-0.65,0l-7.76,7.77c-0.18,0.17-0.18,0.47,0,0.64l2.27,2.27l-11.65,11.65c-11.99,11.99-12.79,30.91-2.44,43.85l-2.31,2.31c-3.9,3.89-4.52,9.76-1.98,14.35l-12.97,12.97l11.57,11.57l12.97-12.97c4.59,2.54,10.46,1.91,14.35-1.98l2.31-2.31c12.93,10.36,31.86,9.56,43.85-2.43l11.65-11.65l2.26,2.26c0.18,0.18,0.47,0.18,0.65,0l7.77-7.76c0.17-0.18,0.17-0.47,0-0.66L459.08,108.07z'
})

add(points, 125)

See a more detailed example.

Using alternative solution for fill missing path

@colinmeinke I am when looking some other library morphing interpolating, i see something interesting. Maybe we improve result by using alternative method filling?

Here link to method and code: https://codepen.io/dalisoft/pen/mMdyZx

Idea:
The old and stable Points#add method

  1. Loop over shape points
  2. Check, if moveTo skips
  3. If it should be add new point, it uses [prevPoint, currentPoint], then moves to 2 forward.

It is good and best as i seem, but it makes some strange situation

let shape1 = [M, L, C, L, Z]
let shape2 = [M, L, C, L, L, C, L, L, C, C, L, L, L, C, C, C, Z]
let shapeAdded [M, +, +, L, C, +, +, L, +, +, Z, +, +, .....] // Bit more points pack comes at last when shape is bigger than 10-length (points length)

I think how it would be if there should be

let shape1 = [M, L, C, L, Z]
let shape2 = [M, L, C, L, L, C, L, L, C, C, L, L, L, C, C, C, Z]
let shapeAdded [M, +, +, L, +, +, C, +, +, L, +, +, Z] // Uses better technique

Maybe we together do better it.
Also, this affects on interpolation naturality

EDIT:
Do you think why i ask from you?
Answer: Because i don't know algorithms better like you

BUG: moveIndex not applies to curved polyline?

Hi. Thanks for amazing and best library for making our job easier. As i know polyline aka non closed, non joined shape cannot be change index. It is now possible?

Last commit sometimes may get wrong shape looking for me, but i not sure.

RangeError: Maximum call stack size exceeded

/node_modules/points/cjs/decurve.js:80
var straighten = function straighten(prevPoint, point, accuracy) {
                                    ^

RangeError: Maximum call stack size exceeded
    at straighten (/node_modules/points/cjs/decurve.js:80:37)
    at straighten (/node_modules/points/cjs/decurve.js:101:39)
    at straighten (node_modules/points/cjs/decurve.js:101:39)
    at straighten (/node_modules/points/cjs/decurve.js:101:39)
    at straighten (/node_modules/points/cjs/decurve.js:101:39)
    at straighten (/node_modules/points/cjs/decurve.js:101:39)
    at straighten (/node_modules/points/cjs/decurve.js:101:39)
    at straighten (/node_modules/points/cjs/decurve.js:101:39)
    at straighten (/node_modules/points/cjs/decurve.js:101:39)
    at straighten (/node_modules/points/cjs/decurve.js:101:39)

Using svg-points to make the points array and then using either position or length triggers this error on several different paths. Any ideas what I might be doing wrong?

Providing a couple for example but I've got plenty:
M3337.1,5965.8c-26.4-9.2-64.5-15.1-87,4.6c3.7,0.8,7.5,1.9,11.1,3.3c2.9,1.1,4.7,4.3,4.4,8.2c-0.3,5-3.6,8.4-7.8,7.6c-3.3-0.6-6.4-1.9-9.7-2.4c-4.6-0.6-9.9,3.1-11.8,7.7c-2.1,5.3-0.5,12.5,3.8,16c3.5,2.8,7.8,3.4,11.8,1.8c3-1.2,6.2-2.9,8.3-5.2c3.9-4.3,7.9-5.5,13.3-3.1c5.2,2.4,9.4,0.3,13.8-2.9c7.6-5.5,9-5,10.7,4.5c1.5,8.3,3.9,16.2,7.7,23.7c0.6,1.1,2.6,1.6,4,2c0.4,0.1,1.6-0.9,1.7-1.6c1.6-10.7,2.9-21.4-0.7-32.1c-2.1-6.3-4-12.7-5.6-19.1c-0.3-1.1,1.2-2.7,2.1-3.8c0.4-0.5,1.7-0.6,2.4-0.3c3.8,1.6,7.7,3.3,11.3,5.3c4.1,2.3,4.4,6,1.3,10.3c-4.1,5.7-4.1,5.7,1.7,11.8l-0.1-0.1c-2.5,5.4-4.2,10.7-3.4,16.9c1.3,9.9,1.9,19.9,3.1,29.8c0.4,3.2,1.6,6.4,3.1,9.3c0.5,1,2.9,1.8,4.2,1.6c1.3-0.2,3.1-1.8,3.3-2.9c0.6-5,0.7-10,0.7-15c0-5.3,1.5-9.7,5.9-13c2.6-2,5-4.4,7.3-6.8c2.7-2.9,3.4-8.2,0.7-11.6c-4.7-6.2-7.8-12.8-7.3-20.7c0.1-0.8,0.5-2,1-2.2c1.5-0.4,3.8-1.1,4.5-0.4c7,7.1,13.8,14.4,20.3,21.9c2.1,2.4,3.7,5.4,4.8,8.4C3379.6,5993.2,3357.7,5972,3337.1,5965.8zM3258.8,6027.3c1-2.2,1.4-5.7,3.1-6.4c7.4-3.2,14.5-7.4,22.7-8.4c2.7-0.3,6.4,4.1,5.6,6.7c-2.3,7.4-7.7,11.4-14.5,10.6C3270,6029.1,3264.4,6028.1,3258.8,6027.3C3258.7,6027.2,3258.8,6027.3,3258.8,6027.3zM3349.6,6039.3c0.1,2.8-1,4.6-3.4,5.5c-3.1,1.2-6.6-1.3-6.2-4.6c0.3-2.7,2.1-4.1,4.6-4.5C3347,6035.4,3349.3,6037.2,3349.6,6039.3z

M5053.6,5247.3c2.5,0.6,7.5,1.3,8.1,0.6c3.1-13.2-2.5-26.3-4.4-39.5c-2.5-13.8-5-27.6-1.3-41.4c3.6-14,7.9-28.2,8.8-42.6c0,0,0,0,0,0c-9.8-2.6-19-7.1-27.4-12.9c0.9,5.6,1.1,11.2,0.4,16.7c-1.3,8.4-7.2,12.9-14.7,14.5c-2.1,11.3-5.4,22.5-4.1,34.4c1.9,20.1,0.6,39.5,1.3,59.6c-1.4,0.2-2.7,0.5-4.1,0.8c2.9,1.5,5.9,2.6,9.1,3C5035.4,5242.3,5044.2,5243.6,5053.6,5247.3z

M4247.5,5519.1c-14.6,0-30.7-8.7-44.6-2.8c-8.8,3.8-10,19.4-20.1,23.8c-5.9,2.7-11.9-1.1-17.4-3.8c-8.4,11.8-16.9,23.4-24.6,36.4c-1.9,3.1-5,6.9-7.5,10.7c-1.3,1.6-2.4,3.6-3.4,5.7c0.1,0,0.2-0.1,0.3-0.1c2.3,11.6,14.5,24.2,15.2,35c21.9-5.5,43.8-11.3,65.6-17.5c0,0,0,0,0,0.1c6.7-17.8,12.8-35.5,21.9-52.1C4239,5542.9,4243,5530.9,4247.5,5519.1z

Reverse strips `moveTo`

reverse([
  { x: 0, y: 0, moveTo: true },
  { x: 50, y: 25 },
  { x: -10, y: -100 }
]

should equal:

[
  { x: -10, y: -100, moveTo: true },
  { x: 50, y: 25 },
  { x: 0, y: 0 }
]

However, the result is currently:

[
  { x: -10, y: -100 },
  { x: 50, y: 25 },
  { x: 0, y: 0 }
]

Import/Export SVG path string

I found the methods in this library very useful, but it lacks a way to import/export shapes from/to anything else. SVG path strings seem like a good intermediary representation.

I wrote this small method for exporting to an SVG path, maybe it can be integrated into the library?

function svgPath(shape) {
  ret = [];
  
  for (let i = 0, l = shape.length; i < l; i++) {
    elem = shape[i];
    if (elem.moveTo) {
      ret.push("M", elem.x, elem.y);
    } else if (elem.curve && elem.curve.type == 'arc') {
      ret.push("A", elem.curve.rx, elem.curve.ry, elem.curve.xAxisRotation || 0, elem.curve.largeArcFlag || 0, elem.curve.sweepFlag || 0, elem.x, elem.y);
    } else if (elem.curve && elem.curve.type == 'cubic') {
      ret.push("C", elem.curve.x1, elem.curve.y1, elem.curve.x2, elem.curve.y2, elem.x, elem.y);
    } else if (elem.curve && elem.curve.type == 'quadratic') {
      ret.push("Q", elem.curve.x1, elem.curve.y1, elem.x, elem.y);
    } else {
      ret.push("L", elem.x, elem.y);
    }
  }
  return ret.join(" ");
}

For importing, maybe we could use https://github.com/hughsk/svg-path-parser

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.