How to convert VML path to SVG path? - javascript

I am currently working on an app for which I need to convert VML shapes into SVG shapes. While I can handle all other aspects of it, I am facing problem in correctly converting the path of the shape from VML path to SVG path. I am using a combination of XSLT and Javascript for my codes.
I have enough control on conversion of angular shapes (i.e. shapes containing only straight lines) but I am facing difficulty in converting path with curves.
For instance, for a simple shape this:
The VML path is: m10800,qx21600,10800,10800,21600l,21600,,xe
Now if I replace m with M, l with L and qx with Q and do the necessary scaling of the coordinates I get the following SVG shape:
The SVG path treats first set of coordinates in Q/qx as a control point and hence the actual path doesn't passes through the point whereas the VML intended those coordinates as the point over which the path should pass through. I don't understand how I can achieve that with SVG (i.e. making sure that the path passes through a specific point or points).
Currently I am using this and this for researching SVG and VML respectively. I also tried using Vector Converter 1.2 but that doesn't works either.
Can anyone suggest me a way, a library, any study links or tutorials where I can find a solution to my problem?
Thanks in advance!!

"qx" in VML is an "elliptical quadrant", "Q" in SVG is a quadratic bezier. Completely different things.
The simplest solution to converting a "qx" is to approximating it with a cubic bezier. Using an arc would be most accurate, but there will be some tricky maths involved in order to determine the correct value for "sweep flag". Although a cubic bezier is not a perfect approximation to a quadrant, it is very close, and the error will not be noticeable enough to affect your drawings.
The secret to drawing circular/elliptical quadrants is the constant 0.5522847498. It defines how long the control point lines have to be to simulate the elliptical curve. You can find explanations for how it is derived by googling that number.
So VML defines "qx" as an elliptical quadrant starting out in the X direction. So given the path command "qx21600,10800", the algorithm for conversion will be:
arcFactor = 0.5522847498;
currentX = 10800;
currentY = 0; // start coords (from the move)
x = 21600;
y = 10800; // first coords in "qx"
dx = x - currentX;
dy = y - currentY;
// Calculate first control point
cp1x = currentX + dx * arcFactor;
cp1y = currentY; // starts out horizontal
// Calculate second control point
cp2x = x;
cp2y = y - dy * arcFactor;
svgBezier = "C" + cp1x + "," + cp1y + "," + cp2x + "," + cp2y + "," + x + "," + y;
Now your curve has a second set of coordinates to the qx. The spec says that it means a repeat of the "qx" command. However it makes no sense for the second set to behave exactly the same as the qx (ie. start out horizontal). So I think they must actually behave as a "qy" (start out vertical). Ie. the qx and qy alternate. Assuming that is the case, the calculation for the qy should be:
// Calculate first control point
cp1x = currentX; // starts out vertical
cp1y = currentY + dy * arcFactor;
// Calculate second control point
cp2x = x - dx * arcFactor;
cp2y = y;
Demo fiddle is here

Related

Drawing an indifference curve that tangents a line in JSXGraph

I've started to use JSXGraph to draw a function. It should be a simple straight line (budget curve) that has a half-circle as his tangent (an indifference curve).
Furthermore the tangent should move with the line, if the line is moved.
Is this possible in JSXGraph?
Edit: The Curve and the function should look similar to this graph:
https://www.economicsonline.co.uk/Pictures2017/Grid-indifference-Basic-Equilibrium-new.png
Thanks,
Mike
Yes, it is possible with JSXGraph. One approach is to start with a point A and fix the slope s of the budget curve through A as well as the radius r of the circle line.
Doing the relatively straightforward math gives as coordinates of the center M of the circle:
M = A + r / sqrt(r*r + 1) * [-s, 1]
The equation of the circle line is
(y - M_y)^2 + (x - M_x)^2 = r^2
It follows that the indifference curve is
y = -sqrt(r^2 - (x - M_x)^2) + M_y
We take the negative square root, since we want to take the lower semicircle as indifference curve. A working example is at http://jsfiddle.net/4sg1dpq8/

Are there any options for inverting a bezier curve? (cytoscape.js)

I'm trying to shape my edges similarly to this:
static mock of desired edges/curves
I'm able to create "S" shaped curves, but i would like them to invert when going downwards from the root node (similarly to the picture). I haven't noticed anything in the docs that describes settings to do this.
I have a demo here: https://codesandbox.io/s/l5m6mnlqrz
What could also work is if I were able to smooth out the 90 degree curve with the "taxi" curve-style, although this doesn't seem possible.
Any suggestions appreciated. Thanks.
It is not possible to create desired form using single Bezier curve, because central range should be vertical. But two conjugated curves might provide appropriate result.
For points A (left one), and B (it is not important - whether B point is lower or higher than A):
The first curve have starting point P0=(XA, YA) and ending point P3=((XA + XB)/2, ((YA + YB)/2)
The first control point must lie at the same horizontal as starting point, the second one - at the same vertical as ending point
X1, Y1 = X0 + DX, Y0
X2, Y2 = X3, Y3 - DY
Parameters DX and DY define rounding of right angle.
Try to set them as DX = (X3 - X0) / 3 and DY = (Y3 - X0) / 3m then vary denominator to get desired curve form
The second part is mirrored curve with points
(X3, Y3), (X3, Y3 + DY), (XB - DX, YB), (XB, YB)

Canvas - How to find if a point is above or under a diagonale line?

In a 2D game, i need to find if an object is above or under a diagonale line.
Anyone knows how to do this ?
(i use createJS framework)
OK, scrap my previous answer and use line intersection instead. Shoot a line from the point to test straight up. If there is an intersection the point is below, if none, the point is either above or to the side of the line.
To avoid side cases (no pun), extend the original line using interpolation.
Here is a function to do line intersection. To do linear interpolation of the original line simply use some extreme values:
var tx1 = x1 + (x2-x1) * -51000;
var ty1 = y1 + (y2-y1) * -51000;
var tx2 = x1 + (x2-x1) * 53200;
var ty2 = y1 + (y2-y1) * 53200;
Update I was a bit in a hurry this morning so here's a small update. As blindman67 points out, you can use just the d in the linked intersection function and check s/t if they are in the normalized range (or just use cross product - see his answer it that is a better fit).
Build a triangle using the upper coordinates to create a shape. For example, if your line look like:
You can create a shape of if using x2 and y1:
Now simply add the triangle to the path and do a isPointInPath(x, y), if true it's above, if false it's below.
If you need to check below reverse the process.
(wowa! a lot of arrows there... but you'll get the idea :) )
Edge cases (pun intended): if point is very close to one of the ends -> just extend the line, or make polygon extending (x1,y1) up to edge of the area.
Actually, thinking about it: triangles may not be so suitable, rather, use the upper edge of the canvas as a segment of polygon, then the next segment would be vertical line down to the end of the diagonal line, the the final segment from the beginning of the diagonal line to the upper left side of the canvas. I'm just too lazy to redo the graphics but you get the idea..
Use the cross product of the point and the line.
You need to move the whole coord system to the start of the line and then get the cross product of the line and the point. If the result is negative then the point is left of the line, if positive then the point is right of the line, if zero then the point is on the line.
// the point
var px = 100;
var py = 100;
// the line
var lx1 = 20;
var ly1 = 20;
var lx2 = 320;
var ly2 = 120;
// move line end and point so that line start is at 0,0
lx2 -= lx1;
ly2 -= ly1;
px -= lx1;
py -= ly1;
// get cross product
var cross = lx2 * py - ly2 * px;
if(cross < 0){ // point is to the left (anticlockwise)
}else if(cross > 0){ // point is to the right (clockwise)
}else{ // cross must be zero then point is on the line
}

Does an HTML5 canvas always have to be a rectangle?

The reason I'm interested in canvases having any shape is that it would then be possible to cut out images with Bezier curves and have the text of a web page flow around the canvas shape, i.e. cut-out image.
What is needed is the possibility to have a free-form shaped div, SVG and HTML5 canvas. (Applied to SVG, I understand this would be equivalent to Flash symbols.) You could then imagine applying a box model (padding, border and margin) for shapes, but it wouldn't be a box (it would be parallel to the shape)!
I suppose it would also then be possible to have text that wraps inside a shape as much as text that flows around a shape.
I read an interesting blog post about "Creating Non-Rectangular Layouts with CSS Shapes" here: http://sarasoueidan.com/blog/css-shapes/
but it doesn't include text wrapping inside a shape.
Then, there's also a CSS Shapes editor for Brackets (a code editor):
http://blogs.adobe.com/webplatform/2014/04/17/css-shapes-editor-in-brackets/
As simple as it may sound it actually involves quite a few steps to achieve.
An outline would look something like this:
Define the shape as a polygon, ie. point array
Find bounds of polygon (the region the polygon fits inside)
Contract polygon with padding using either a cetronid algorithm or simply a brute-force approach using center of bounds
Define line height of text and use that as a basis for number of scan-lines
Basically use a polygon-fill algorithm to find segment within the shape which can fill in text. The steps for this is:
Use an odd/even scanner by getting an intersection point (using line intersection math) with text scan line and each of the lines between the points in the polygon
Sort the points by x
use odd and even point to create a segment. This segment will always be inside the polygon
Add clipping using original polygon
Draw in image
Use the segments to get a width. Start parsing the text to fill and measure the width.
When text width fits within the segment width then print the chars that fits
Repeat for next text/words/chars until end of text or segments
In other words: you would need to implement a polygon fill algorithm but instead of filling in lines (per pixel line) you use the line as basis for the text.
This is fully doable; actually, I went ahead to create a challenge for myself on this problem, for the fun of it, so I created a generic solution that I put on GitHub released under MIT license.
The principle described above are implemented, and to visualize the steps:
Define the polygon and padding - here I chose to just use a simple brute-force and calculate a smaller polygon based on center and a padding value - the light grey is the original polygon and the black obviously the contracted version:
The points are defined as an array [x1, y1, x2, y2, ... xn, yn] and the code to contract it (see link to project for full source on all these parts):
var pPoints = [],
i = 0, x, y, a, d, dx, dy;
for(; i < points.length; i += 2) {
x = points[i];
y = points[i+1];
dx = x - bounds.px;
dy = y - bounds.py;
a = Math.atan2(dy, dx);
d = Math.sqrt(dx*dx + dy*dy) - padding;
pPoints.push(bounds.px + d * Math.cos(a),
bounds.py + d * Math.sin(a));
}
Next step is to define the lines we want to scan. The lines are based on line height for font:
That is simple enough - just make sure the start and end points are outside the polygon.
We use an odd/even scan approach and check intersection of the scanline versus all lines in the polygon. If we get a intersect point we store that in a list for that line.
The code to detect intersecting lines is:
function getIntersection(line1, line2) {
// "unroll" the objects
var p0x = line1.x1,
p0y = line1.y1,
p1x = line1.x2,
p1y = line1.y2,
p2x = line2.x1,
p2y = line2.y1,
p3x = line2.x2,
p3y = line2.y2,
// calc difference between the coords
d1x = p1x - p0x,
d1y = p1y - p0y,
d2x = p3x - p2x,
d2y = p3y - p2y,
// determinator
d = d1x * d2y - d2x * d1y,
px, py,
s, t;
// if is not intersecting/is parallel then return immediately
if (Math.abs(d) < 1e-14)
return null;
// solve x and y for intersecting point
px = p0x - p2x;
py = p0y - p2y;
s = (d1x * py - d1y * px) / d;
if (s >= 0 && s <= 1) {
// if s was in range, calc t
t = (d2x * py - d2y * px) / d;
if (t >= 0 && t <= 1) {
return {x: p0x + (t * d1x),
y: p0y + (t * d1y)}
}
}
return null;
}
Then we sort the point for each line and use pairs of points to create segments - this is actually a polygon-fill algorithm. The result will be:
The code to build segments is a bit extensive for this post so check out the project linked above.
And finally we use those segments to replace with actual text. We need to scan a text from current text pointer and see how much will fit inside the segment width. The current code is somewhat basic and skips a lot of considerations such as word breaks, text base-line position and so forth, but for initial use it will do.
The result when put together will be:
Hope this gives an idea about the steps involved.

Applying rounded corners to paths/polygons

I'm gathering some info for a project that has to start within a few weeks.
This project contains a browser-based drawing tool where users can add predefined shapes or forming shapes themselves. Shapes must be selectable, freely scalable and rotatable with a Illustrator-like transformtool (handles).
Predefined shapes that we have in mind are: rectangles, ellipses, half ellipses and (isosceles) triangles.
So far so good, to achieve this I was thinking of RaphaelJS or FabricJS but... Every shape (polygon/path) must be drawn with a certain cornerradius. And the cornerradius must be maintained while scaling, so no distortion occurs. The user can specify the rounding by input.
There's a few obstacles/questions:
Is there some uniform mathemetical formula to apply a cornerradius to the shapes I mentioned? Or must every shape be treated as a mini-project itself? I want to return it as a path or poly, so it can be drawn with SVG or canvas.
Every scale or rotate operation, by dragging the transformhandles, will result in (massive) calculations to retrieve an updated shape I think. Rectangles are the easiest to achieve and except ellipses, all the other shapes will be a lot harder to compute. Is there some way to speed up the process?
I found a site where users can draw flowcharts and apply a cornerradius on almost all shapes the are offered. It works so smoothly, I can't nail how they did it.
Link: https://www.lucidchart.com/ (try button)
For now, I'm a bit clueless, I guess to mediocre in mathematics. Perhaps someone can push me in the right direction and share some experiences?
Thanks in advance.
BTW. Performance is key in this project. The ouput of the drawing must be SVG format.
I ended up having a similar problem, and wasn't able to find a simple solution. I ended up writing a fairly generic corner-rounding function based on Adobe Illustrator's operation. It uses Bezier curves instead of arcs, but I think the result is pretty decent.
It supports rounding with a radius given in the coordinate space of the SVG image or as a fraction of the distance between a corner and its neighbors.
To use this, include rounding.js in your project and call the function:
roundPathCorners(pathString, radius, useFractionalRadius)
The code and some test paths are here: http://embed.plnkr.co/kGnGGyoOCKil02k04snu/preview
This is how the examples from the Plnkr render:
The starting point could be using-svg-curves-to-imitate-rounded-corners. The principle is to convert every corner with shorthand relative cubic (s). This example is very basic and works only with two possible corner cases.
I think expanding this like corner replace with shorthand relative cubic is possible to expand to cover also other path segments. Every segment has a on-curve coordinate point, which have to be replaced with s segment. The math can be interesting part of this solution.
Despite this question being around for some time, some may stop by and try this solution:
var BORDER_RADIUS = 20;
function roundedPath( /* x1, y1, x2, y2, ..., xN, yN */ ){
context.beginPath();
if (!arguments.length) return;
//compute the middle of the first line as start-stop-point:
var deltaY = (arguments[3] - arguments[1]);
var deltaX = (arguments[2] - arguments[0]);
var xPerY = deltaY / deltaX;
var startX = arguments[0] + deltaX / 2;
var startY = arguments[1] + xPerY * deltaX / 2;
//walk around using arcTo:
context.moveTo(startX, startY);
var x1, y1, x2, y2;
x2 = arguments[2];
y2 = arguments[3];
for (var i = 4; i < arguments.length; i += 2) {
x1 = x2;
y1 = y2;
x2 = arguments[i];
y2 = arguments[i + 1];
context.arcTo(x1, y1, x2, y2, BORDER_RADIUS);
}
//finally, close the path:
context.arcTo(x2, y2, arguments[0], arguments[1], BORDER_RADIUS);
context.arcTo(arguments[0], arguments[1], startX, startY, BORDER_RADIUS);
context.closePath();
}
The trick is to start (and stop) at the middle of the first line, and then use the arcTo function which is described very nicely here.
Now you "just" have to find a way to express all of your shapes as polygons.

Categories