Drawing a filled 180deg arc in Raphael - svg

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/

Related

Why does the alpha value change when I have given it a constant value?

I am using the p5.js Web Editor
var sketch = function (p) {
with(p) {
p.setup = function() {
createCanvas(400, 400);
secCanvas = createGraphics(400, 400);
secCanvas.clear();
trans = 0;
drop_size = 10;
sun_size = 50;
radius = 10;
};
p.draw = function() {
background(3, 182, 252, 1);
image(secCanvas, 0, 0)
secCanvas.fill(255, 162, 0, 1)
secCanvas.ellipse(width/2, 0 + sun_size, sun_size)
fill(40, trans)
trans = random(255);
ellipse(random(mouseX + radius, mouseX - radius), random(mouseY + radius, mouseY - radius), drop_size)
drop_size = random(50)
};
}
};
let node = document.createElement('div');
window.document.getElementById('p5-container').appendChild(node);
new p5(sketch, node);
body {
background-color:#efefef;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.js"></script>
<div id="p5-container"></div>
When I set a discrete value of alpha in secCanvas.fill(). The value appears to be gradually increase(and stops soon), while I gave no such instruction. Why is this happening? This only happens when I put background(3, 182, 252, 1); in the draw function but not when I put it in the setup function.
Each frame is drawn on top of all previous frames, so when you draw a semi-transparent background, you can still see the previous frames underneath it.
Think of it as adding a very thin coat of paint over top what you've already painted. Because the color you're adding is semi-transparent, you can still see what's underneath it. Then during the next frame, you add another layer of paint, and the previous frames get just a little more faint.
They stop becoming more faint because of the way the computer calculates the new color, based on the previous frames and the new semi-transparent background color. Long story short, the color you're drawing is almost 100% transparent, so it's not strong enough to completely hide previous frames.

D3.js semantic zoom misbehaving

I've been trying to teach myself D3.js, but I can't seem to get semantic zoom (zooming positions but not shapes) to work for me.
I've read the d3 zoom docs here, and attempted to functionally copy the svg semantic zoom example code
This is my code:
var X, Y, circle, circles, h, i, j, svg, transform, w, zoom, _i, _j;
w = 1200;
h = 600;
circles = [];
for (j = _i = 0; _i <= 6; j = ++_i) {
for (i = _j = 0; _j <= 12; i = ++_j) {
circles.push({r: 25, cx: i * 50, cy: j * 50});
}
}
X = d3.scale.linear()
.domain([0, 1])
.range([0, 1]);
Y = d3.scale.linear()
.domain([0, 1])
.range([0, 1]);
zoom = d3.behavior.zoom()
.x(X)
.y(Y)
.on("zoom", function() {
return circle.attr("transform", transform);
});
transform = function(d) {
return "translate(" + (X(d.cx)) + ", " + (Y(d.cy)) + ")";
};
svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h)
.call(zoom)
.append("g");
circle = svg.selectAll("circle")
.data(circles)
.enter().append("circle")
.attr("r", function(d) {
return d.r;
}).attr("cx", function(d) {
return d.cx;
}).attr("cy", function(d) {
return d.cy;
}).attr("transform", transform);
Live version at jsfiddle.
This should be pretty simple. I'm creating grid of circles that should exactly touch when no zoom is applied (distance is 50 px, diameter is 50 px). When I zoom in, I expect the circles to spread apart, with the point under the mouse remaining stationary. I expect the zoom to be smooth and linear with applied mouse wheeling. The circles should remain the same size, though, so that they stop touching when I zoom in; they should overlap when I zoom out.
Instead, initially, the circles are spread out exactly twice as far as they should be. When I zoom in and out, the center point is not under the mouse (and moves around depending on how I pan). Zoom is highly nonlinear, asymptotically approaching a scale of 1 (circles touching) as I zoom out, and rapidly accelerating as I zoom in.
This seems really odd, and I can't spot significant differences between my code and the semantic zoom example, which works as expected. I conclude that I don't actually understand how D3 zoom is supposed to work. Can someone sort me out?
Your code is very close to being correct: Working demo.
Use scale to map the location of objects
Instead of saving the exact location of objects in them and then using scales with range and domain set to [0, 1], use the scales to do the mapping for you:
for (j = _i = 0; _i <= 6; j = ++_i) {
for (i = _j = 0; _j <= 12; i = ++_j) {
circles.push({
r: 25,
cx: i,
cy: j,
color: "#000"
});
}
}
X = d3.scale.linear()
.domain([0, 6])
.range([0, w]);
Y = d3.scale.linear()
.domain([0, 12])
.range([0, h]);
The change here is that now D3 knows about the aspect ratio of your viewport and in what proportions it should transform the scales so as to keep the point under the svg static under the mouse. Otherwise, it was trying to zoom in and out of a square, resulting in a jarring experience.
The problem was the initial position of the circles stacking up on the translation.
Live code with the problem pointed out and fixed, and a few other modifications:
var size = 600
var scale = 100
circles = []
for (var j = 0; j<6; j++) {
for (var i = 0; i<6; i++) {
circles.push({x: i*scale, y: j*scale })
}
}
var X = d3.scale.linear()
.domain([0,6*scale])
.range([0,size])
var Y = d3.scale.linear()
.domain([0,6*scale])
.range([0,size])
function transform(d) {
return "translate("+X(d.x)+", "+Y(d.y)+")"
}
var circle /*fwd declaration*/
var zoom = d3.behavior.zoom()
.x(X).y(Y)
.on("zoom", function () {
circle.attr("transform", transform)
})
var svg = d3.select("body").append("svg")
.attr("width", size).attr("height", size)
.call(zoom)
.append("g")
circle = svg.selectAll("circle")
.data(circles)
.enter().append("circle")
.attr("r", 20)
/*the problem was this initial offset interfering with the
translation we were applying, resulting in very strange behavior*/
/* .attr("cx", function (d) {return d.x})
.attr("cy", function (d) {return d.y})*/
.attr("transform", transform)
The "scale" parameter should do nothing, but if you add in those commented lines, it affects the initial position and causes the non-intuitive effects.
The original problems were:
Initial scale appeared to be more zoomed than it should have been.
Zooming out very var produced a noticeable nonlinear asymptotic effect.
Zooming out then panning around, then zooming back in did not work at all like expected, with the diagram sliding under the mouse instead of staying pinned.
All of these are straightforward consequences of the initial position:
The initial distances appeared bigger because we applied their original positions plus the zoom translation.
The nonlinear asymptotic effect was the zoom translation distances going to zero asymptotically (as expected), but the initially applied distances not going to zero, giving the appearance of a nonzero zoom asymptote.
While zoomed out, D3 thinks it's zoomed out more than the user does (because of the extra distances between circles), which means when a pan is applied, the center of the image as D3 tracks it is moving differently than what the user expects, which causes the effect of the zoom center not being under the mouse.
You can play with these effects to understand them by uncommenting the initial position lines and applying the same zoom actions with different scale parameters. Commenting them causes the circles to initially be all at screen-space 0,0, so that only the zoom distance translation is applied, which is what we want.
Props to musically_ut's answer for suggesting the smaller world-space coordinate scale, which shouldn't have made any difference, but did, which helped me identify the problem.

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.

Putting an arrow (marker) at specific point on a path when using the d3 javascript library

I am working currently on a graph visualization and I use SVG and the D3 library. I was asked by our designer if I can put the arrowheads of the edges of the graph on a position corresponding to 80% of length of the lines.
I was able to achieve the first part - getting the position - by using the getPointAtLength method.
var svg = d3.select("body").append("svg")
.attr("width", 960)
.attr("height", 500)
var path = svg.append("path")
.attr("d", "M20,20C400,20,20,400,400,400")
.attr("fill", "none")
.attr("stroke", "black");
var pathEl = path.node();
var pathLength = pathEl.getTotalLength();
var pathPoint = pathEl.getPointAtLength(pathLength*0.5);
var point = svg.append("svg:circle")
.style("fill", "red")
.attr("r", 5)
.attr("cx", pathPoint.x)
.attr("cy", pathPoint.y);
Here is a jsfidle example
Now I wonder how ca I attach an arrowhead to this position with corresponding orientation. More important how can I do this so I can update the edges of the graph when moving the associated nodes.
I was not able to find any answer yet, the examples on "markers" are working with path properties like : style('marker-end', "url(#end-arrow)")
Firstly, the long answer from SO. The quick answer is SVG <markers>
The (basic) short answer: Take a point a little before the red dot, measure the slope and draw a line between the two points. Now the question is simplified to: How do add an arrow to the end of a straight line? Use the quick answer.
Add this to your code to visualize the answer:-
var pathPoint2 = pathEl.getPointAtLength(pathLength*0.78);
var point2 = svg.append("svg:circle")
.style("fill", "blue")
.attr("r", 3)
.attr("cx", pathPoint2.x)
.attr("cy", pathPoint2.y);
var slope = (pathPoint.y - pathPoint2.y)/(pathPoint.x - pathPoint2.x);
var x0 = pathPoint2.x/2;
var y0 = slope*(x0 - pathPoint.x) + pathPoint.y;
var line = svg.append("svg:path")
.style("stroke","green")
.attr("d", "M" + pathPoint.x + "," + pathPoint.y + " L" + x0 +","+ y0);

How to draw circle with specific color in XNA?

XNA doesn't have any methods which support circle drawing.
Normally when I had to draw circle, always with the same color, I just made image with that circle and then I could display it as a sprite.
But now the color of the circle is specified during runtime, any ideas how to deal with that?
You can simply make an image of a circle with a Transparent background and the coloured part of the circle as White. Then, when it comes to drawing the circles in the Draw() method, select the tint as what you want it to be:
Texture2D circle = CreateCircle(100);
// Change Color.Red to the colour you want
spriteBatch.Draw(circle, new Vector2(30, 30), Color.Red);
Just for fun, here is the CreateCircle method:
public Texture2D CreateCircle(int radius)
{
int outerRadius = radius*2 + 2; // So circle doesn't go out of bounds
Texture2D texture = new Texture2D(GraphicsDevice, outerRadius, outerRadius);
Color[] data = new Color[outerRadius * outerRadius];
// Colour the entire texture transparent first.
for (int i = 0; i < data.Length; i++)
data[i] = Color.TransparentWhite;
// Work out the minimum step necessary using trigonometry + sine approximation.
double angleStep = 1f/radius;
for (double angle = 0; angle < Math.PI*2; angle += angleStep)
{
// Use the parametric definition of a circle: http://en.wikipedia.org/wiki/Circle#Cartesian_coordinates
int x = (int)Math.Round(radius + radius * Math.Cos(angle));
int y = (int)Math.Round(radius + radius * Math.Sin(angle));
data[y * outerRadius + x + 1] = Color.White;
}
texture.SetData(data);
return texture;
}

Resources