Fill a closed path in easeljs - svg

Is there a way to fill a closed drawn path in easeljs? I have along string of mt(x_t,y_t).lt(x_(t+1),y_(t+1)) that draws a wacky shape. the shape closes off, but I can't find a way to have it actually fill in the closed area. Any ideas?
T is how many coordinates there are to connect, [round.X, round.Y] is the Tx2 array of coordinate pairs, ghf is the graphics object. xline.y is just a the lowest y value.
for(var i=0;i<T;i++){
x0 = round.X[i];
y0 = round.Y[i];
// scale for drawing
px0 = Math.round(xscale * x0);
py0 = Math.round(yscale * y0) + xline.y;
if(x0>gp.xmin){ // if not first point ...
ghf.mt(prevx,prevy).lt(px0,py0); // draw line from prev point to this point
}
// set this point as prev point
prevx = px0;
prevy = py0;
}
// fill out thing
ghf.mt(prevx,prevy).lt(px0,xline.y);
ghf.mt(px0,xline.y).lt(0,xline.y);
x0 = round.X[0];
y0 = round.Y[0];
px0 = Math.round(xscale * x0);
py0 = Math.round(yscale * y0) + xline.y;
ghf.mt(0,xline.y).lt(px0,py0);
ghf.f('red');

Your code is not very helpful, but I think what you need is the beginFill method. See link.
You can use it like this:
var ball = new createjs.Shape();
ball.graphics.setStrokeStyle(5, 'round', 'round');
ball.graphics.beginStroke(('#000000'));
ball.graphics.beginFill("#FF0000").drawCircle(0,0,50);
ball.graphics.endStroke();
ball.graphics.endFill();
ball.graphics.setStrokeStyle(1, 'round', 'round');
ball.graphics.beginStroke(('#000000'));
ball.graphics.moveTo(0,0);
ball.graphics.lineTo(0,50);

Related

OpenSCAD: inner curved edges between 2 circles

I'm not sure what to search for or how to ask the question as I can't draw. Please bear with me.
If I have a rectangle with circular end caps. I want to remove some of the edges of the rectangle so there is a smooth path all round. Kinda like of you were to stretch the ends, the middle gets thinner.
I was trying to work out the chord of a larger, outer circle until I got stuck trying to work out where the circles should touch.
I can see some relationships for trigonometry, but my brain just won't go the extra mile.
Can anyone please help point me in the right direction.
Thanks.
Here is the answer:
// Small value for CSG
Delta = 0.01;
2Delta = 2 * Delta;
$fa=1; $fs=$fa;
module roudedArm(xl=50, yt=10, zh=5, in=2, bh=0.8) {
EndRadius = yt/2; // size of outer ends
EndSpacing = xl-yt; // distance between end point radii
ArmConcavity = in; // how much in does it go in on each side
ArmThickness = zh; // height in z
// Negative curve to narrow the Arm (calculated by pythagoras)
ArmCurveRadius = (pow((EndSpacing / 2), 2) - 2 * EndRadius * ArmConcavity + pow(ArmConcavity, 2)) / (2 * ArmConcavity);
// The orthogonal distance between the middle of the Arm the point it touches the round pillar sections
ArmSectionLength = (EndSpacing / 2) * ArmCurveRadius / (ArmCurveRadius + EndRadius);
// end points
lbxcylinder(r=EndRadius, h=ArmThickness);
translate([EndSpacing, 0, 0]) lbxcylinder(r=EndRadius, h=ArmThickness);
// inner curve
difference()
{
translate([EndSpacing / 2 - ArmSectionLength, -EndRadius -ArmThickness, 0])
translate([ArmSectionLength, (EndRadius + ArmThickness),0])
lbxcube([ArmSectionLength * 2, 2 * (EndRadius + ArmThickness), ArmThickness], bh=bh);
// Cut out Arm curve
translate([EndSpacing / 2, ArmCurveRadius + EndRadius - ArmConcavity, -Delta])
lbxcylinder(r = ArmCurveRadius, h = ArmThickness + 2Delta, bh=-bh);
translate([EndSpacing / 2, -(ArmCurveRadius + EndRadius - ArmConcavity), -Delta])
lbxcylinder(r = ArmCurveRadius, h = ArmThickness + 2Delta, bh=-bh);
}
}
module lbxcube(size, bh=0.8) {
// don't support bevelling in demo
translate([-size[0]/2, -size[1]/2, 0]) cube(size);
}
module lbxcylinder(r, h, bh=0.8) {
// don't support bevelling in demo
cylinder(r=r, h=h);
}
roudedArm(xl=50, yt=10, zh=5, in=2, bh=0.8);
Thanks to Rupert and his Curvy Door Handle on Thingiverse.

Calculating elliptical arc end point

I need to draw a circular arc to an image where I know radius (r), center point (x0, y0), start point (x1, y1) and angles (a, b => y). I need to calculate where the end point (x2, y2) is. I guess this is done with elliptical arc but I have not succeeded in it.
I have taken example from the link mentioned in the comments (How to calculate the SVG Path for an arc (of a circle)) and my code goes like this:
get circle center (pictured as orange dot in the image below)
calculate radius (from the left corner to center point)
var radius = Math.sqrt(Math.pow(circleCenter.getY() - corner.getY(), 2) + Math.pow(circleCenter.getX() - corner.getX(), 2));
calculate points like in the opsb's example above
var start = this.polarToCartesian(circleCenter, radius, angleB);
var end = this.polarToCartesian(circleCenter, radius, angleA);
polarToCartesian(center: Coordinate, radius: number, angle: number): Coordinate {
// zero angle is in 9 o'clock, angle in radian
var xDiff = radius * Math.cos(angle - Math.PI / 2)
var yDiff = radius * Math.sin(angle - Math.PI / 2)
var x = center.getX() + xDiff;
var y = center.getY() + yDiff;
return new Coordinate(x, y);
}
What I end up with, is here (the arrow points don't meet the lines):
I also tried with wdebeaum's example and got pretty close but not all the way. End points are little off.

Get coordinates of svg group on drag with snap.svg

I'm brand new to svg and thought I would try out snap svg. I have a group of circles that I am dragging around, and am looking to get the coordinates of the group. I am using getBBox() to do this, but it isn't working as I would expect. I would expect getBBox() to update its x and y coordinates but it does not seem to do that. It seems simple but I think I am missing something. Here's the code
var lx = 0,
ly = 0,
ox = 0,
oy = 0;
moveFnc = function(dx, dy, x, y) {
var thisBox = this.getBBox();
console.log(thisBox.x, thisBox.y, thisBox);
lx = dx + ox;
ly = dy + oy;
this.transform('t' + lx + ',' + ly);
}
startFnc = function(x, y, e) { }
endFnc = function() {
ox = lx;
oy = ly;
console.log(this.getBBox());
};
var s = Snap("#svg");
var tgroup = s.group();
tgroup.add(s.circle(100, 150, 70), s.circle(200, 150, 70));
tgroup.drag(moveFnc, startFnc, endFnc);
The jsfiddle is at http://jsfiddle.net/STpGe/2/
What am I missing? How would I get the coordinates of the group? Thanks.
As Robert says it won't change. However getBoundingClientRect may help.
this.node.getBoundingClientRect(); //from Raphael
Jsfiddle here showing the difference http://jsfiddle.net/STpGe/3/.
Edit: Actually I'd be tempted to go here first, I found this very useful Get bounding box of element accounting for its transform
Per the SVG specification, getBBox gets the bounding box after transforms have been applied so in the co-ordinate system established by the transform attribute the position is the same.
Imagine like you drew the shape on graph paper setting a transform moves the whole graph paper but when you look at position of the shape on the graph paper it hasn't changed, it's the graph paper that's moved but you're not measuring that.
Try to use the group.matrix object to get x and y coordinate of group object.
moveFnc = function(dx, dy, x, y, event) {
lx = this.matrix.e;
ly = this.matrix.f;
this.transform('translate(' + lx + ',' + ly+')');
}

Rotating object relative to mouse position

At the moment I'm using the dot product of the mouse position and (0, 1) to generate radians to rotate an object, in three.js
Code below, works ok but the object 'jumps' because the radian angle skips from positive to negative when the clientX value goes between window.innerWidth / 2
onDocumentMouseMove : function(event) {
// rotate circle relative to current mouse pos
var oldPos = new THREE.Vector2(0, 1);
Template.Main.mouseCurrPos = new THREE.Vector2((event.clientX / window.innerWidth ) * 2 - 1, - (event.clientY / window.innerHeight) * 2 + 1);
Template.Main.mouseCurrPos.normalize();
//Template.Main.projector.unprojectVector(Template.Main.mouseCurrPos, Template.Main.scene);
var angle = oldPos.dot(Template.Main.mouseCurrPos);
Template.Main.mousePrevPos.x = event.clientX;
Template.Main.mousePrevPos.y = event.clientY;
if (event.clientX < window.innerWidth / 2) {
Template.Main.circle.rotation.z = -angle;
}
else {
Template.Main.circle.rotation.z = angle;
}
console.log(Template.Main.circle.rotation.z);
}
However if I add this to assign the value to oldPos:
if (event.clientX < window.innerWidth / 2) {
oldPos = new THREE.Vector2(0, -1);
}
else {
oldPos = new THREE.Vector2(0, 1);
}
Then the "jumping" goes but the effect of rotation is inverted when the mouse is on the left of the window.
I.e. mouse going up rotates anti-clockwise and vice-versa which is not desired.
It's frustrating.
Also if I keep the oldPos conditional assignment and leave out the conditional negation of the angle instead, the jumping comes back.
You can see a demo here: http://theworldmoves.me/rotation-demo/
Many thanks for any tips.
Why are you using the result of the dot product as the angle (radians)? The dot product gives you the cosine of the angle (times the magnitude of the vectors, but these are a unit vector and a normalized vector, so that doesn't matter).
You could change your angle computation to
var angle = Math.acos(oldPos.dot(Template.Main.mouseCurrPos));
However, you may get the wrong quadrant, since there can be two values of theta that satisfy cos(theta) = n. The usual way to get the angle of a vector (origin to mouse position) in the right quadrant is to use atan2():
var angle = Math.atan2(Template.Main.mouseCurrPos.y,
Template.Main.mouseCurrPos.x);
This should give the angle of the mouse position vector, going counterclockwise from (1, 0). A little experimentation can determine for sure where the zero angle is, and which direction is positive rotation.

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