thi-ng / umbrella

⛱ Broadly scoped ecosystem & mono-repository of 199 TypeScript projects (and ~180 examples) for general purpose, functional, data driven development
https://thi.ng
Apache License 2.0
3.36k stars 149 forks source link

[geom-splines] expose cubic conversion for polylines #208

Closed vorg closed 4 years ago

vorg commented 4 years ago

I'm trying to create smooth curve from a polyline and hit a roadblock when realized that cubic-from-controlpoints assumes that curve is closed. Turns of that buildNonUniform is all I needed. Is there an official way to convert list of points to list of Cubics? Currently i'm doing:

let path = [[0, 0, 0], [0.5, 1, 0], [1, 0.25, 0]]

let segments = convertToSegments(path)

let cubics = buildNonUniform(segments, 1, false)
let resampledPath = cubics.reduce((resampledPath, c) => {
  // resample each segment individually, is there better way?
  var curve = sampleCubic(c, 6)
  return resampledPath.concat(curve)
}, [])

// remove overlapping start/end points for each cubic
let smoothPath = simplify(resampledPath, 0, false)

assuming following buildNonUniform from cubic-from-controlpoints

const buildNonUniform = (segments, t = 1) => {
    const res = []
    for (let i = 0, n = segments.length - 2; i < n; i += 2) {
      const a = segments[i]
      const b = segments[i + 1]
      const c = segments[i + 2]
      res.push([a, vec3.lerp([...a], b, t), vec3.lerp([...c], b, t), c])
    }
    return res
  }

and modified segment generation

function convertToSegments(path) {
      const segments = [path[0], path[0]]
      for (let i = 0, num = path.length - 1; i < num; i++) {
        const q = path[i + 1];
        segments.push(vec3.lerp([...path[i]], q, 0.5), [...q]);
      }    
      segments.push(path[path.length-1])
      return segments
}

Note: vec3.lerp comes from pex-math/vec3.js#L150 and is similar to mixN.

postspectacular commented 4 years ago

Hey @vorg - with the above commits I've just added some new functions which should address most of this issue, mainly: openCubicFromControlPoints() and sampleCubicArray (via this higher-order helper). I will try to do a new release later today...

There should also be an openCubicFromBreakPoints() version at some point, but don't have time for that right now... Also, am sure some of this can/should be refactored/merged...

The use of the main @thi.ng/geom package in the example below is just for demo/convenience and the SVG output here. You can also just use the resample() function from the @thi.ng/geom-resample pkg to work with the raw point array...

import { openCubicFromControlPoints, sampleCubicArray } from "@thi.ng/geom-splines";
import { polyline, resample, circle, svgDoc, asSvg } from "@thi.ng/geom";

// input control points
const src = [[0,0], [100,50], [200,0], [150,100], [200,200], [100,150], [0,200]];

// cubic segments
const segments = openCubicFromControlPoints(src, 2/3);

// sampled curve points (w/ default sample config)
// the `false` arg indicates a non-closed curve
const pts = sampleCubicArray(segments, false);

// uniformly resample polyline
const line = resample(polyline(pts), { dist: 10 });

fs.writeFileSync(
  "test.svg",
  asSvg(
    svgDoc(
      { stroke:"black" },
      line, ...line.points.map((p) => circle(p, 1))      
    )
  )
);

test.svg

postspectacular commented 4 years ago

Just for future reference, here's how you'd use the aforementioned resample() function to resample the raw point array (without @thi.ng/geom shape wrappers):

resample(
  sampleCubicArray(
    openCubicFromControlPoints(src, 2 / 3),
    // open (non-closed) curve
    false,
    // optional config (here default aka 20 points per cubic segment)
    { num: 20 }
  ),
  // uniform distance resampling
  { dist: 5 }
)

1) The resample step is obviously optional and you could also ask for a fixed number of points (aka num option) instead of uniform distance. 2) Do NOT use the dist sampling option for the first stage, since that only applies to single segments, rather than the complete polyline. Also, as you can see, there's no need to call simplify(), since sampleCubicArray doesn't create duplicated intermediate vertices...

postspectacular commented 4 years ago

@vorg - openCubicFromBreakPoints() is now available too...

open-brk

postspectacular commented 4 years ago

Hi @vorg - this is all released now as "@thi.ng/geom-splines": "0.5.2". Let me know if that works for you and we can close this issue... thx! :)

vorg commented 4 years ago

Works great! My strange attractors thank you...

Screenshot 2020-03-03 at 10 12 15
postspectacular commented 4 years ago

@vorg Nice one! :) + great to hear!