SVG paths not showing after upgrade to fabric version 1.5.0 - fabricjs

See this fiddle. The arc is visible with fabric 1.4.0 but not when the library is changed to 1.5.0.
https://jsfiddle.net/ex5s9mx9/2/
The relevant code is
function makeArcPath(path, lineColor)
{
var p = new fabric.Path(path,
{
stroke: lineColor,
strokeWidth: 1,
fill: false
});
return p;
};
function circularArcPath(x, y, radius, startAngle, endAngle)
{
var start = polarToCartesian(x, y, radius, endAngle);
var end = polarToCartesian(x, y, radius, startAngle);
var anglediff = startAngle - endAngle;
var arcSweep = ((anglediff > 0 && anglediff < 180) || (anglediff < -180)) ? 0 : 1;
var d = [["M", start.x, start.y], ["A", radius, radius, 0, 0, arcSweep, end.x, end.y]];
return d;
}
I have looked at the arc object with the debugger in both cases, and all fields are the same.
I'd be very grateful for any pointers.
Thanks.
Edit: If I append ["M", 0, 0] to the end of the path, then the arc does appear!
Edit 2: In the fiddle, when I first make my arc it has radius 0, but this is updated when it is moved by calling
c.arc.set('path', circularArcPath(c.left, c.top, 30, a1, a2));
However, if I use a non-zero radius right from the start, the problem disappears, so I think it must be something about the initial zero radius.

Related

Draw a circle divided in two by chord

I want to create an svg like that:
So, a circumference divided into two parts by a chord. The two sections must have different colors.
I want to draw this object in SVG. I think I need to use the PATH tag, right? Or is there another way to do it?
What points do I need to draw the object? I'm a bit confused..
Yes. It is a <path> element that you will need.
Paths always start with an M (move) command. You'll also need an A (arc) command, and probably an L line command for the line that bisects the circle.
For the arc command, you just need to know the X and Y coordinates of the start and end points (B and C). Plus the radius of the circle. It is important to have accurate coordinates for the start and end points of an arc command. Small discrepancies can cause the position of the circle to move around quite a bit.
In the following demo, I have chosen to calculate the B and C positions based on their angle from the centre. Plus setting the path description attribute from code, allows me to document for you what each of the parameters are for.
// Radius of the circle
var radius = 80;
// Centre coordinate of the circle
var Ox = 100;
var Oy = 100;
// Angles of each point from which we calculate their X and Y coordinates.
// Here, 0 degrees is East, and angle increases in a clockwise direction.
var angleB = 285; // degrees
var angleC = 35;
var B = angleToCoords(angleB, Ox, Oy, radius);
var C = angleToCoords(angleC, Ox, Oy, radius);
// Get the "major segment" path element
var majorPath = document.getElementById("major");
// Set the path description for the "major segment"
majorPath.setAttribute("d", ['M', B.x, B.y, // Move to point B
'L', C.x, C.y, // Line to point C
'A', radius, radius, // X radius and Y radius of the arc
0, // ellipse angle
1, // large arc flag (1 indicates we want the larger of the two possible arcs between the points
1, // clockwise direction flag
B.x, B.y, // arc end point is back at point B
'Z'].join(" ")); // Z command closes the path
// Get the "minor segment" path element
var minorPath = document.getElementById("minor");
// Set the path description for the "minor segment"
minorPath.setAttribute("d", ['M', B.x, B.y, // Move to point B
'A', radius, radius, // X radius and Y radius of the arc
0, // ellipse angle
0, // large arc flag (0 indicates we want the smaller of the two possible arcs between the points
1, // clockwise direction flag
C.x, C.y, // arc end point is at point C
'L', B.x, B.y, // Line to point B
'Z'].join(" ")); // Z command closes the path
// Function to convert from an angle to an X and Y position
function angleToCoords(angleInDegrees, centreX, centreY, radius)
{
var angleInRadians = angleInDegrees * Math.PI / 180;
return {
'x': centreX + (radius * Math.cos(angleInRadians)),
'y': centreY + (radius * Math.sin(angleInRadians))
}
}
path {
stroke: black;
stroke-width: 1;
}
#major {
fill: #78dcdc;
}
#minor {
fill: #aaffaa;
}
<svg width="200" height="200">
<path id="major" d="" />
<path id="minor" d="" />
</svg>

Read inner and outer radius of arc - d3.js

I'm working on a project which deals with visual representation of large relational data. We are using pie chart to display the data components(In order). Due to lack of space we are displaying only 10 at a time.
Consider the following example:
Assume that I have 100 data components, out of which I'll display only 10 at a given point of time. The logic i'm using is, I'm setting start and end angle to 0(Zero) for other 90 components. Where in for those 10 components i'm calculation the start and end angle as below-
var angle = 360;
var count = 10;
if(data.length > count) angle = angle/count; //data is array of data component names
else angle = angle/data.length;
//Initially I'll be displaying first ten components
for(var i=0; i<data.length; i++){
var startAngle = i * angle;
var endAngle = startAngle + angle;
var pi = = Math.PI/180;
var arc = d3.svg.arc()
.innerRadius(innerRadius) //dynamic value, calculated based on available space
.outerRadius(outerRadius) //dynamic value, calculated based on available space
.startAngle((startAngle)*pi)
.endAngle((endAngle)*pi);
//Hiding rest of the data components
if(i >= count){
arc.startAngle(0);
arc.endAngle(0);
}
arcGroup.append("path")
.attr("d", arc)
.attr("stroke", "#2E2E2E")
.attr("stroke-width", "1")
.attr("fill","gold");
var text = arcGroup.append("text")
.attr("transform", "translate(" + arc.centroid() + ")")
.attr("text-anchor", "middle")
.attr("font-family","noto_sansregular")
.attr("font-size", 40)
.attr("font-weight","Bold")
.attr("fill", "#000000")
.attr("y",0)
.style("visibility", "visible")
.text(data[i]);
//Hiding text of hidden arcs
if(i >= count) text.style("visibility", "hidden");
}
Then if user wants to see the rest of the components, i'm providing two buttons to rotate (clock or anti-clock) the content.
If current view is -> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
On rotate by one cell clockwise, resulting view should be -> 100, 1, 2, 3, 4, 5, 6, 7, 8, 9
In this case I need to hide component '10' and display component '100', and shift rest of the cells. To achieve this i just need to change the start and end angles of the arcs. I can create new arc object with calculated angles.
The problem here is I don't know how to get the inner and outer radius of the arc, which are dynamically created.
Just like that,
...
arcGroup.append("path")
.filter(function(d) {
// You're Not restricted to the "filter" function
var innerRadius = d.innerRadius()(d);
var outerRadius = d.outerRadius()(d);
})
.attr("d", arc)
...
Technically, it is possible to retrieve the innerRadius and outerRadius from the d attribute of the path elements, but it will require parsing the DSL and will be tedious. These values are not stored nicely on the elements itself by d3.
Hence, it would be better if you recompute the innerRadius and outerRadius while updating the elements:
function showFromIdx(firstIndex) {
argGroup.selectAll('path')
.data( d3.range(data.length)
.map(function (d) {
return (d - firstIndex + data.length) % data.length;
})
)
.attr('d', function (d) {
// You will have to calculate the radii again here.
var innerRadius = foo(d), outerRadius = bar(d);
return d3.svg.arc()
.startAngle(i < count ? i * angle : 0)
.endAngle(i < count ? (i + 1) * angle : 0)
.innerRadius(innerRadius)
.outerRadius(outerRadius)(d);
});
}
Here are several functions that I wrote to get inner and outer radiuses of arcs created using d3. Please tell me if you find mistakes in code.
function getInnerRadiusFromArc(arc) {
var numbersInPattern = _getArcNumbers(arc);
// Possibly, that's sector, so it starts from 0.
// Or maybe that's something else.
if (numbersInPattern.length < 4) {
return 0;
}
// Getting minimum from the array.
var innerRadius = Math.min.apply(null, numbersInPattern);
return innerRadius;
}
function getOuterRadiusFromArc(arc) {
var numbersInPattern = _getArcNumbers(arc);
// Getting maximum from the array.
var outerRadius = Math.max.apply(null, numbersInPattern);
return outerRadius;
}
function _getArcNumbers(arc) {
// Path description parameter, containing necessary data.
var pathDescription = arc.getAttribute("d");
// We need to get all patterns like A<number>,<number>.
// RegExp source:
// http://www.regular-expressions.info/floatingpoint.html
const numberRegExp = /[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?/g;
var arcPattern = new RegExp("A" + numberRegExp.source + "," + numberRegExp.source, "g");
var arcParameters = pathDescription.match(arcPattern);
var numbersInPattern = [];
// We get all the numbers from array ["A<number>,<number>", "A<number>,<number>", ...].
for (let parameterIndex = 0; parameterIndex < arcParameters.length; parameterIndex++) {
let parameter = arcParameters[parameterIndex];
let numbers = parameter.match(numberRegExp);
if (numbers !== null) {
numbersInPattern = numbersInPattern.concat(numbers);
}
}
// Transform strings in our array to numbers.
numbersInPattern = numbersInPattern.map(function (numberString) {
return parseFloat(numberString);
});
return numbersInPattern;
}

How to get color (r, g, b ,a) of every pixel in Three.js

I'm using Particle to draw irregular shapes in Three.js, the code snippet is like:
var hearts = function(context){
context.globalAlpha = 0.5;
var x = 0, y = 0;
context.scale(0.1, -0.1); // Scale so canvas render can redraw within bounds
context.beginPath();
context.bezierCurveTo(x + 2.5, y + 2.5, x + 2.0, y, x, y);
context.bezierCurveTo(x - 3.0, y, x - 3.0, y + 3.5, x - 3.0, y + 3.5);
...
context.closePath();
context.lineWidth = 0.1; //0.05
context.stroke();
}
var material = new THREE.ParticleCanvasMaterial({
program: heart,
blending: THREE.AdditiveBlending
});
material.color.setRGB(255, 0, 0);
var particle = new THREE.Particle(material);
what I want to do is select the irregular shape properly, my question is, if I draw shape this way, how can I get the color of every pixel so I can used in the picking algorithm
Thanks.
Have you looked into toDataURL()?
I use that in my three.js logic to grab and save the canvas out of the browser. From looking at this:
http://www.patrick-wied.at/blog/how-to-create-transparency-in-images-with-html5canvas
It looks to me like toDataURL() can also be used to peer into the RGB and A of each pixel, if need be change them and write it back to the visible framebuffer.

Drawing a filled 180deg arc in Raphael

I've just started playing with SVG and Raphael.js and am trying to draw a filled arc but not sure how to start. I'm trying to draw a basic padlock and have the body and two parts of the bolt to fit the arc on top. It's basically a filled 180deg arc, 10 pixels wide. I'm guessing I need to use the .path() but not sure of the syntax or whether I want to be using "curveTo" or "arc" - struggling to find any good SVG or Raphael tutorial sites to be honest.
var padlockBody = paper.rect(100, 100, 100, 100, 5);
padlockBody.attr("fill", "#000000");
var leftBoltPart = paper.rect(120, 70, 10, 30);
leftBoltPart.attr("fill", "#000000");
var rightBoltPart = paper.rect(170, 70, 10, 30);
rightBoltPart.attr("fill", "#000000");
// TODO: filled arc to fit on top of left/right bolt parts
Arcs are notoriously challenging to write by hand (in pure SVG or in Raphael, it's the same).
I usually extend Raphael with these methods (credits belong to the55)
// http://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands
Raphael.fn.arc = function(startX, startY, endX, endY, radius1, radius2, angle) {
var arcSVG = [radius1, radius2, angle, 0, 1, endX, endY].join(' ');
return this.path('M'+startX+' '+startY + " a " + arcSVG);
};
Raphael.fn.circularArc = function(centerX, centerY, radius, startAngle, endAngle) {
var startX = centerX+radius*Math.cos(startAngle*Math.PI/180);
var startY = centerY+radius*Math.sin(startAngle*Math.PI/180);
var endX = centerX+radius*Math.cos(endAngle*Math.PI/180);
var endY = centerY+radius*Math.sin(endAngle*Math.PI/180);
return this.arc(startX, startY, endX-startX, endY-startY, radius, radius, 0);
};
You can find a demo here: http://jsfiddle.net/cahT9/

How is the getBBox() SVGRect calculated?

I have a g element that contains one or more path elements. As I mentioned in another question, I scale and translate the g element by computing a transform attribute so that it fits on a grid in another part of the canvas.
The calculation is done using the difference between two rectangles, the getBBox() from the g element and the rectangle around the grid.
Here is the question -- after I do the transform, I update the contents of the g element and call getBBox() again, without removing the transform. The resulting rectangle appears to be calculated without considering the transform. I would have expected it to reflect the change. Is this behavior consistent with the SVG specification? How do I get the bounding box of the transformed rectangle?
This, BTW, is in an HTML 5 document running in Firefox 4, if that makes any difference.
Update: Apparently this behavior seems pretty clearly in violation of the specification. From the text here at w3c:
SVGRect getBBox()
Returns the tight bounding box in current user space (i.e., after application of the ‘transform’ attribute, if any) on the geometry of all contained graphics elements, exclusive of stroking, clipping, masking and filter effects). Note that getBBox must return the actual bounding box at the time the method was called, even in case the element has not yet been rendered.
Am I reading this correctly? If so this seems to be an errata in the SVG implementation Firefox uses; I haven't had a chance to try any other. I would file a bug report if someone could point me to where.
People often get confused by the behavioral difference of getBBox and getBoundingClientRect.
getBBox is a SVG Element's native method as equivalent to find the offset/clientwidth of HTML DOM element. The width and height is never going to change even when the element is rotated. It cannot be used for HTML DOM Elements.
getBoundingClientRect is common to both HTML and SVG elements. The bounded rectangle width and height will change when the element is rotated or when more elements are grouped.
The behaviour you see is correct, and consistent with the spec.
The transform gets applied, then the bbox is calculated in "current user units", i.e. the current user space. So if you want to see the result of a transform on the element you'd need to look at the bbox of a parent node or similar.
It's a bit confusing, but explained a lot better in the SVG Tiny 1.2 spec for SVGLocatable
That contains a number of examples that clarify what it's supposed to do.
there are at least 2 easy but somewhat hacky ways to do what you ask... if there are nicer (less hacky) ways, i haven't found them yet
EASY HACKy #1:
a) set up a rect that matches the "untransformed" bbox that group.getBBox() is returning
b) apply the group's "unapplied transform" to that rect
c) rect.getBBox() should now return the bbox you're looking for
EASY HACKY #2: (only tested in chrome)
a) use element.getBoundingClientRect(), which returns enough info for you to construct the bbox you're looking for
Apparently getBBox() doesn't take the transformations into consideration.
I can point you here, unfortunately I wasn't able to make it working: http://tech.groups.yahoo.com/group/svg-developers/message/22891
SVG groups have nasty practice - not to accumulate all transformations made. I have my way to cope with this issue. I'm using my own attributes to store current transformation data which I include in any further transformation. Use XML compatible attributes like alttext, value, name....or just x and y for storing accumulated value as atribute.
Example:
<g id="group" x="20" y="100" transform="translate(20, 100)">
<g id="subgroup" alttext="45" transform="rotate(45)">
<line...etc...
Therefore when I'm making transformations I'm taking those handmade attribute values, and when writing it back, I'm writing both transform and same value with attributes I made just for keeping all accumulated values.
Example for rotation:
function symbRot(evt) {
evt.target.ondblclick = function () {
stopBlur();
var ptx=symbG.parentNode.lastChild.getAttribute("cx");
var pty=symbG.parentNode.lastChild.getAttribute("cy");
var currRot=symbG.getAttributeNS(null, "alttext");
var rotAng;
if (currRot == 0) {
rotAng = 90
} else if (currRot == 90) {
rotAng = 180
} else if (currRot == 180) {
rotAng = 270
} else if (currRot == 270) {
rotAng = 0
};
symbG.setAttributeNS(null, "transform", "rotate(" + rotAng + "," + ptx + ", " + pty + ")");
symbG.setAttributeNS(null, "alttext", rotAng );
};
}
The following code takes into account the transformations (matrix or otherwise) from parents, itself, as well as children. So, it will work on a <g> element for example.
You will normally want to pass the parent <svg> as the third argument—toElement—as to return the computed bounding box in the coordinate space of the <svg> (which is generally the coordinate space we care about).
/**
* #param {SVGElement} element - Element to get the bounding box for
* #param {boolean} [withoutTransforms=false] - If true, transforms will not be calculated
* #param {SVGElement} [toElement] - Element to calculate bounding box relative to
* #returns {SVGRect} Coordinates and dimensions of the real bounding box
*/
function getBBox(element, withoutTransforms, toElement) {
var svg = element.ownerSVGElement;
if (!svg) {
return { x: 0, y: 0, cx: 0, cy: 0, width: 0, height: 0 };
}
var r = element.getBBox();
if (withoutTransforms) {
return {
x: r.x,
y: r.y,
width: r.width,
height: r.height,
cx: r.x + r.width / 2,
cy: r.y + r.height / 2
};
}
var p = svg.createSVGPoint();
var matrix = (toElement || svg).getScreenCTM().inverse().multiply(element.getScreenCTM());
p.x = r.x;
p.y = r.y;
var a = p.matrixTransform(matrix);
p.x = r.x + r.width;
p.y = r.y;
var b = p.matrixTransform(matrix);
p.x = r.x + r.width;
p.y = r.y + r.height;
var c = p.matrixTransform(matrix);
p.x = r.x;
p.y = r.y + r.height;
var d = p.matrixTransform(matrix);
var minX = Math.min(a.x, b.x, c.x, d.x);
var maxX = Math.max(a.x, b.x, c.x, d.x);
var minY = Math.min(a.y, b.y, c.y, d.y);
var maxY = Math.max(a.y, b.y, c.y, d.y);
var width = maxX - minX;
var height = maxY - minY;
return {
x: minX,
y: minY,
width: width,
height: height,
cx: minX + width / 2,
cy: minY + height / 2
};
}
I made a helper function, which returns various metrics of svg element (also bbox of transformed element).
The code is here:
SVGElement.prototype.getTransformToElement =
SVGElement.prototype.getTransformToElement || function(elem) {
return elem.getScreenCTM().inverse().multiply(this.getScreenCTM());
};
function get_metrics(el) {
function pointToLineDist(A, B, P) {
var nL = Math.sqrt((B.x - A.x) * (B.x - A.x) + (B.y - A.y) * (B.y - A.y));
return Math.abs((P.x - A.x) * (B.y - A.y) - (P.y - A.y) * (B.x - A.x)) / nL;
}
function dist(point1, point2) {
var xs = 0,
ys = 0;
xs = point2.x - point1.x;
xs = xs * xs;
ys = point2.y - point1.y;
ys = ys * ys;
return Math.sqrt(xs + ys);
}
var b = el.getBBox(),
objDOM = el,
svgDOM = objDOM.ownerSVGElement;
// Get the local to global matrix
var matrix = svgDOM.getTransformToElement(objDOM).inverse(),
oldp = [[b.x, b.y], [b.x + b.width, b.y], [b.x + b.width, b.y + b.height], [b.x, b.y + b.height]],
pt, newp = [],
obj = {},
i, pos = Number.POSITIVE_INFINITY,
neg = Number.NEGATIVE_INFINITY,
minX = pos,
minY = pos,
maxX = neg,
maxY = neg;
for (i = 0; i < 4; i++) {
pt = svgDOM.createSVGPoint();
pt.x = oldp[i][0];
pt.y = oldp[i][1];
newp[i] = pt.matrixTransform(matrix);
if (newp[i].x < minX) minX = newp[i].x;
if (newp[i].y < minY) minY = newp[i].y;
if (newp[i].x > maxX) maxX = newp[i].x;
if (newp[i].y > maxY) maxY = newp[i].y;
}
// The next refers to the transformed object itself, not bbox
// newp[0] - newp[3] are the transformed object's corner
// points in clockwise order starting from top left corner
obj.newp = newp; // array of corner points
obj.width = pointToLineDist(newp[1], newp[2], newp[0]) || 0;
obj.height = pointToLineDist(newp[2], newp[3], newp[0]) || 0;
obj.toplen = dist(newp[0], newp[1]);
obj.rightlen = dist(newp[1], newp[2]);
obj.bottomlen = dist(newp[2], newp[3]);
obj.leftlen = dist(newp[3], newp[0]);
// The next refers to the transformed object's bounding box
obj.BBx = minX;
obj.BBy = minY;
obj.BBx2 = maxX;
obj.BBy2 = maxY;
obj.BBwidth = maxX - minX;
obj.BBheight = maxY - minY;
return obj;
}
and full functional example is here:
http://jsbin.com/acowaq/1

Resources