I'm trying to draw a path in SVG that draws a smoothed curve between a series of points.
For example - see this (where "interpolations" is set to "monotone"):
http://bl.ocks.org/mbostock/4342190
I've found an excellent implementation of an algorithm in JavaScript here:
http://blog.mackerron.com/2011/01/01/javascript-cubic-splines/
I can use this function to create as many points on a the curve as a want, and use those points to create a polyline that will follow the curve. With a huge number of points, I'd have a relatively smooth curve.
But I'd really like to be able to create a path that uses cubic bezier curves, like d3 does.
I also found this SVG implementation of Catmull Rom Splines: https://gist.github.com/njvack/6925609
This works great, but unlike Monotone cubic interpolation, this tends to "overshoot" the given points, which isn't acceptable for what I'm doing.
Obviously - I could use d3, but I'd really like to be able to do this without a library, if at all possible.
Thanks in advance.
You could extract d3's implementation, which is in a function called d3_svg_lineMonotoneTangents in src/svg/line.js:
// Interpolates the given points using Fritsch-Carlson Monotone cubic Hermite
// interpolation. Returns an array of tangent vectors. For details, see
// http://en.wikipedia.org/wiki/Monotone_cubic_interpolation
function d3_svg_lineMonotoneTangents(points) {
// ...
return tangents;
}
Permanent link to d3_svg_lineMonotoneTangents in d3 v3.5.5.
For D3 v5, you can find its (improved) implementation here: https://github.com/d3/d3-shape/blob/master/src/curve/monotone.js
Related
Creating a map similar to the one found here:
https://www.plantmaps.com/interactive-california-2012-usda-plant-zone-hardiness-map.php
Have all the data needed.
Create polygons around each one without distance inbetween. No overlapping or similar.
Drawing it all by hand on geojson.io, seems impossible.
I want to create polygons around each [point] and make sure there is no distance between them.
What you're describing here is a tessellation. Depending on your data, you might opt for a regular tessellation (i.e. creating a grid of squares or hexagons, paying a modicum of attention to the units of your coordinate system) or the well-known-among-GIS-people Voronoi tessellation. Note that a Voronoi tessellation created over a regular grid of points will result in a regular grid of polygons.
There are plenty of tools for Voronoi tessellations. For javascript and GeoJSON, my tool of choice would be turf.js's voronoi module.
Here are some things to consider:
creating polygon from a point is simple enough. For example you use the point as the center of a regular polygon and devide 2PI by the number of sides and step through the points to create the polygon. But, what is the radius? That depends on the projection you are using. I happen to use OpenLayers and really like this map control. It's default projection it EPSG:3857. So, the coordinates are already in meters - so easy. But if your points are in long/lat then you have to do some math. it may be easier to transform to a different projection temporarily. An opensource library that is really nice for gis calculations if you need one is Turf.
You mentioned also about having non-overlapping polygons? Well, in this case you will have a lot of gaps if you use regular polygons. To have non-overlapping polygons with, as you put it no distance between them is an interesting constraint. Now you are dealing with different shaped polygons. And an algorithm for handling that is pretty intense. I know that MapInfo GIS has a feature for adjusting polygons to be non-overlapping. But, in a JavaScript environment with GeoJSON, you are probably talking about server side logic for this.
That map you are looking at looks like it is using Leaflet with svg overlays.
This question already has answers here:
svg: generate 'outline path'
(2 answers)
Closed 5 years ago.
I want to convert a stroked path to a filled object. (Programmatically, in JavaScript.)
The line is just a simple curved line, a sequence of coordinates. I can render this line as a path, and give it a stroke of a certain thickness... but I'm trying to get a filled shape rather than a stroked line, so that I can do further modifications on it, such as warping it, so the resulting 'stroke' might vary in thickness or have custom bits cut out of it (neither of these things are possible with a real SVG stroke, as far as I can tell).
So I'm trying to manually 'thicken' a line into a solid shape. I can't find any function that does this – I've looked through the docs of D3.js and Raphaël, but no luck. Does anyone know of a library/function that would do this?
Or, even better: if someone could explain to me the geometry theory about how I would do this task manually, by taking the list of line coordinates I have and working out a new path that effectively 'strokes' it, that would be amazing. To put it another way, what does the browser do when you tell it to stroke a path – how does it work out what shape the stroke should be?
There has been a similar question recently:
svg: generate 'outline path'
All in all, this is a non-trivial task. As mentioned in my answer to the linked question, PostScript has a command for generating paths that produce basically the same output as a stroke, called strokepath. If you look at what Ghostscript spits out when you run the code I posted at the linked question, it's pretty ugly. And even Inkscape doesn't really do a good job. I just tried Path => Outline stroke in Inkscape (I think that's what the English captions should say), and what came out didn't really look the same as the stroked path.
The "simplest" case would be if you only have non-self-intersecting polylines, polygons or paths that don't contain curves because in general, you can't draw exact "parallel" Bézier curves to the right and the left of a non-trivial Bézier curve that would delimit the stroked area - it's mathematically non-existent. So you would have to approximate it one way or the other. For straight line segments, the exact solution can be found comparatively easily.
The classic way of rendering vector paths with curves/arcs in them is to approximate everything with a polyline that is sufficiently smooth. De Casteljau's Algorithm is typically used for turning Bézier curves into line segments. (That's also basically what comes out when you use the strokepath command in Ghostscript.) You can then find delimiting parallel line segments, but have to join them correctly, using the appropriate linejoin and miterlimit rules. Of course, don't forget the linecaps.
I thought that self-intersecting paths might be tricky because you might get hollow areas inside the path, i.e. the "crossing area" of a black path might become white. This might not be an issue for open paths when using nonzero winding rule, but I'd be cautious about this. For closed paths, you probably need the two "delimiting" paths to run in opposite orientation. But I'm not sure right now whether this really covers all the potential pitfalls.
Sorry if I cause a lot of confusion with this and maybe am not of much help.
This page has a fairly good tutorial on bezier curves in general with a nice section on offset curves.
http://pomax.github.io/bezierinfo/
A less precise but possibly faster method can be found here.
http://seant23.wordpress.com/2010/11/12/offset-bezier-curves/
There is no mathematical answer, because the curve parallel to a bezier curve is not generally a bezier curve. Most methods have degenerate cases, especially when dealing with a series of curves.
Think of a simple curve as one with no trouble spots. No cusps, no loops, no inflections, and ideally a strictly increasing curvature. Chop up all the starting curves into these simple curves. Find all the offset curves of these simple curves. Put all the offset curves back together dealing with gaps and intersections. Quadratic curves are much more tractable if you have the option to work with them.
I think most browsers do something similar to processingjs, as they have degenerate cases even with quadratic curves. For example, look at the curve 200,300 719,301 500,300 with a thickness of 100 or more.
The standard method is the Tiller-Hanson algorithm (Offsets of Two-Dimensional Profiles, 1984, which irritatingly is not on line for free) which creates a good approximation. The idea is that because the control points of each Bezier curve lie on lines tangent to the start and end of the curve, a parallel curve will have the same property. So we offset the start and the end of the curve, then find new control points using these intersections. However, that gives very bad results for sharp curves, so the first step is to bisect the original curve, which is very easy to do to Bezier curves, until it turns through a sufficiently small angle.
Other refinements are needed to deal with (i) intersections between the parallels, on the inside of each vertex; (ii) inserting an arc of a circle to fill the gap on the outside of each vertex; and (iii) adding end-caps - square, butt or circular.
Tiller-Hanson is difficult to implement, but there's a good open-source implementation in the FreeType library, in ftstroke.c (http://git.savannah.gnu.org/cgit/freetype/freetype2.git/tree/src/base/ftstroke.c).
I'm sorry to say that it can be quite difficult to integrate this code, but I have used it successfully, and it works well.
Right now, I am working with bezier curves in fabric.js. Assuming, for instance, 3 of them, with intersections between them. I need to find the exactly crossing point and, even more difficult, draw the selection in other canvas. For fabric.path, I'm using
perPixelTargetFind: true,
Image to clarify the situation:
Finally, after a long time, I can't find any solution in fabric library, a workaround pass for convert to svg, use a another library or program your own proccess (pretty hard) and convert back to fabric.
The final solution is use paper.js, it has some functions to detect intersections and colissions, and, also, is very powerful for some other task.
What is the neatest (code design) and most per-formant way of getting an array of points for an arc (polyline), for the purpose of animating using Cesium's timer/clock.
Variable inputs include (start/end location), height (highest point) from earth's surface and number of points for drawing.
I'm currently using a polyline collection, so the answer should describe how to generate the points for existing polylines or convert to a different approach.
I would also need the arc (color) to fadeIn or fadeOut to opacity 0.
Multiple arcs may be added or removed from the collection per second. Each arc will have different start and end points
The start and end location should have height 0 (touching the earth).
(For Cesium version b26)
Just to be sure I understand your question, you have a bunch of polylines on a map and you want to get a bunch of data points along the line for use in animating the something along the path. I'll also assume you want geodesic lines/arcs for the polylines rather than the straight lines that are normally drawn on Mercator maps as geodesic lines actually follow the spatially accurate path of the polyline i.e. the same path a plane would take. If this is the case then take a look at this blog post: http://alastaira.wordpress.com/2011/06/27/geodesics-on-bing-maps-v7/ This post describes how to calculate data points along the geodesic path of a polyline.
CesiumJS includes several spline functions that can be used. One of the easier ones to use that an accomplish what you want with just three points is the Catmull-Rom Spline:
http://cesiumjs.org/Cesium/Build/Documentation/CatmullRomSpline.html
You will need to produce a middle point. To do this you can take a mean of the lat/lon coordinates and add a large height. Because of the spline used and the low number of points, it does end up looking a little egg shaped. The benefit to this is that you can ask the spline object for an arbitrary number of points, so the arc can be as smooth as you want. Just don't forget to add the first and last points to the array returned by the spline as those are omitted.
There are other types of splines, but I found the Catmull-Rom spline the easiest to use. You can search the CesiumJS documentation for some of the other included splines.
I've been looking into the same thing (minus the time aspect) and I found Cesium.EllipsoidGeodesic(start, end, ellipsoid), which allows you to get points at fractions of the path. It seems to me that you can choose the fraction based on the distance and calculate regular points using the result.
https://cesiumjs.org/Cesium/Build/Documentation/EllipsoidGeodesic.html
I haven't tried it yet, but it's on my list of things to do.
I am wondering if anyone has heard of some javascript which can take a simple SVG path that took maybe 30 seconds to create in illustrator or something and convert it into something that looks 3d. There is an extrude function in illustrator which does this. It is also called polling in sketchUp.
I am using Raphael.js now, but am open to other suggestions. A simple solution would be to make a copy of the path and move it a couple pixels down and to the right and give it a darker color behind the original path, but I am looking for something that might have a little more shading.
Thanks!
There is always a possibility to use three.js for extruding the path for use in webGL in browser:
http://alteredqualia.com/three/examples/webgl_text.html#D81F0A21010#23a
(More samples here:http://stemkoski.github.io/Three.js/)
It uses js-fonts and parses the path commands on them, extrudes the paths and renders the scene. In the same way it should be possible to take an SVG path and extrude it. Raphael has Raphael.parsePathString() which gives you the path segments as an array of individual segments. If you first convert the path commands to cubic curves using Raphael.path2curve() and Raphael._pathToabsolute(), you have only only one segment type so you can use three.js:s BEZIER_CURVE_TO command. If you have transformations applied on the path (which is usually the case in Illustrator export) you can flatten them using function from here: https://stackoverflow.com/a/13102801/1691517.
One possible starting point is here (click the fiddle of the answer):
Extruding multiple polygons with multiple holes and texturing the combined shape
Three.js supports few path commands, but have not tested all of them (
http://threejsdoc.appspot.com/doc/three.js/src.source/extras/core/Path.js.html, see below).
THREE.PathActions = {
MOVE_TO: 'moveTo',
LINE_TO: 'lineTo',
QUADRATIC_CURVE_TO: 'quadraticCurveTo', // Bezier quadratic curve
BEZIER_CURVE_TO: 'bezierCurveTo', // Bezier cubic curve
CSPLINE_THRU: 'splineThru', // Catmull-rom spline
ARC: 'arc' // Circle
};
I have used a custom rather complex function to polygonize SVG path, so was no need to rely to other commands than moveto and lineto.
The downside is of course rather low support level for webGL, 31-53%: http://caniuse.com/webgl
Other more cross-browser solution is this SVG3d library if lesser quality and slowness is not an issue:
http://debeissat.nicolas.free.fr/svg3d.php
https://code.google.com/p/svg3d/
I think this resource could be helpful to you, he uses d3 to generate 2D visualization and then uses d3-threeD to extrude.
Sounds like you want to use svg filters. Webplatform.org has a pretty good tutorial about that. Scroll down a bit and you'll find some lighting filters that looks like 3d.
Raphaël doesn't support filters though, so either you'll need to extend it, or just use svg directly.