I have some values that I need to plot into a 2D HTML5 <canvas>. All values are in the range [-1, +1] so I decided to set a transformation (scale + displacement) on the canvas 2D-context before drawing:
var scale = Math.min(canvas.width, canvas.height) / 2;
ctx.setTransform(scale, 0, 0, scale, canvas.width / 2, canvas.height / 2);
Each value is drawn using the arc method, but since I want a fixed arc-radius (no matter what scaling is used) I'm dividing the radius with the current scale value:
ctx.arc(value.X, value.Y, 2 / scale, 0, 2 * Math.PI, false);
Now, a canvas of size 200 x 200 will result in scale factor of 100, which in turn results in a arc-radius of 0.02. Unfortunately, it seems that values like 0.2 or 0.02 don't make any difference to the resulting arc-radius, only the stroke thickness is changing.
You can see this behavior in the JsFiddle. Is this a bug or am I doing something wrong?
The issue is that after scaling by a huge factor your lines you now have a lineWidth far too big to be drawn correctly with stroke.
Just adjust the lineWidth to 1/scale before drawing, and all will work fine.
Related
I have a sphere in threejs, and I'd like a ring to animate over the top of it.
I have the following progress:
https://codepen.io/EightArmsHQ/pen/zYRdQOw/2919f1a1bdcd2643390efc33bd4b73c9?editors=0010
In the animate function, I call:
const scale = Math.cos((circlePos / this.globeRadius) * Math.PI * 0.5);
console.log(scale);
this.ring.scale.set(scale, scale, 1);
My understanding is that the sin and cos functions are exactly what I need to work out how far around the circle the ring has gotten to. However, the animation actually shows the ring fall inside the sphere, before eventually hitting the 0 scale at the outside of the sphere.
Ideally, I'd also like to just be changing the radius of the sphere but I cannot work out how to do that either, so I think it may be an issue of using the scale function.
How can I keep the ring on the surface of the sphere?
Not quite. Consider this:
You have a right triangle whose bases are your x and y, with a hypotenuse of r = globeRadius. So by Pythagoras' theorem, we have:
x2 + y2 = r2.
So if we solve for the height, y, we get:
y = √(r2 - x2).
Thus, in your code, you could write it e.g. like this:
const scale = Math.sqrt(this.globeRadius * this.globeRadius - circlePos * circlePos);
However, this is the scale in terms of world units, not relative to the objects. So for this to work, you need to either divide by your radius again, or just initialise your ring with radius 1:
this.ringGeometry = new THREE.RingGeometry(1, 1.03, 32);
Here I gave it an arbitrary ring width of 0.03 - you may of course adjust it to your own needs.
Is there any way that I can focus into d3 world Map around a specific latitude and longitude on load of file.
Here is working plunker in which I can zoom around a d3 world Map.
plunker
Below code is used to zoom in for click.
function clicked() {
currScale2 = projection.scale();
if(beforeClickValue == 0)
beforeClickValue = 150;
beforeClickValue = beforeClickValue + 100;
projection.scale(beforeClickValue);
g.selectAll("path").attr("d", path);
}
I need to zoom in near or around Kenya, if I provide a particular location in Kenya, eg:
Latitude 0.55378653650984688
Longitude 35.661578039749543
If your centering point is determined by a feature
If your point is a feature centroid, then you can automatically center your map using that feature:
There are a few ways to achieve this, one would be to set your projection to be centered on your features:
projection.fitSize([width,height],geoJSONKenyaTurkana);
fitSize takes the width and height of a bounding box - your svg - and sets the scale and translate of the projection to maximize the size of the features within that bounding box. .fitExtent will allow a bit more flexibility regarding margins:
projection.fitExtent([[10,10],[width-10,height-10]],geoJSONKenyaTurkana);
This will provide margins of 10 pixels: the first coordinate is the top left of the bounding box, while the second coordinate is the bottom right.
After setting your projection to be centered with either method, then you can append the features - your zoom constraints, however, will be relative to this starting point - as you have zoomed in on the projection. Here's a plunkr with this approach (using fitSize):
https://plnkr.co/edit/E7vqcwwISmmxUarCsWvw?p=preview
I've used your featureCollection as the feature, but you could center it on an individual feature in the feature collection.
Alternatively, and possibly more in line with your title, you can use a zoom identity to set the intitial zoom factor with d3.zoom, this manipulates the svg rather than the projection and uses your zoom function:
var bounds = path.bounds(geoJSONKenyaTurkana),
dx = bounds[1][0] - bounds[0][0],
dy = bounds[1][1] - bounds[0][1],
x = (bounds[0][0] + bounds[1][0]) / 2,
y = (bounds[0][1] + bounds[1][1]) / 2,
scale = .9 / Math.max(dx / width, dy / height),
translate = [width / 2 - scale * x, height / 2 - scale * y];
svg.call(_zoom.transform, d3.zoomIdentity
.scale(scale)
.translate(translate[0]/scale,translate[1]/scale)
);
This gives us something that looks like this:
https://plnkr.co/edit/CpL4EDUntz853WzrjtU0?p=preview
If you want to manually set a centering point
If however, you want to set your map to be centered according to a manually set point, you can accomplish this much the same way as above: modifying the projection, or modifying the zoom:
To modify the projection, you can use .center() which takes a coordinate and centers the map on this point:
projection.center([longitude,latitude])
Of course, points don't have area, so you will have to set the scale factor yourself, the value will depend on what you want to show:
projection.center([longitude,latitude]).scale(k);
Larger values are more zoomed in.
Alternatively, to manipulate the zoom function, we can use something like:
var x = projection([35.661578039749543,0.55])[0],
y = projection([35.661578039749543,0.55])[1],
scale = 20,
translate = [width / 2 - scale * x, height / 2 - scale * y];
svg.call(_zoom.transform, d3.zoomIdentity
.scale(scale)
.translate(translate[0]/scale,translate[1]/scale)
);
As with setting the projection to center on a specific point, you'll need to set a scale value manually. Here I've arbitrarily chosen 20.
I'm trying to blend two circles, one black and the other white. The parts where they intersect must be in grey.
I tried to use blend functions but I don't get the expected results.
The objective is to mix ONLY this two elements, other elements (like the background) can't mix there, and I don't know how to do this maintaining the 100% of the alpha channel.
This is my current render code, circle1 and circle2 are TextureRegion.
public void draw(Batch batch, float parentAlpha) {
super.draw(batch, parentAlpha);
batch.enableBlending();
batch.setBlendFunction(GL20.GL_ONE, GL20.GL_ONE);
batch.draw(circle1, c1.getX(), getY(), getWidth(), getHeight());
batch.draw(circle2, c2.getX(), getY(), getWidth(), getHeight());
}
This is an example of the color mix.
EDIT: Problem solved.
I did a test using Spritebatch, I drew a third circle with reduced opacity, this is the code used to test and the result:
Gdx.gl20.glEnable(GL20.GL_BLEND);
Gdx.gl20.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
shapeRenderer.setProjectionMatrix(camera.combined);
shapeRenderer.begin(ShapeRenderer.ShapeType.Filled);
shapeRenderer.setColor(1, 1, 1, 1f);
shapeRenderer.circle(15, 5, 5);
shapeRenderer.setColor(0, 0, 0, 1f);
shapeRenderer.circle(19, 5,5);
shapeRenderer.setColor(1, 1, 1, 0.5f);
shapeRenderer.circle(15, 5, 5);
shapeRenderer.end();
Gdx.gl20.glDisable(GL20.GL_BLEND);
I've never used libGDX so I'm not sure how the specific implementation goes, but I'll give you the general outline of what you're trying to achieve.
The most basic blending you can achieve is a linear blend between two colors with a single parameter t. Consider this example - let's say each color is represented by a single channel value for simplicity - black is 0.0 and white is 1.0. What you want to achieve is the mid range gray that is 0.5, to do this you would blend by scaling each channel appropriately ( black * 0.5 + white * 0.5 = 0.0 * 0.5 + 1.0 * 0.5 = 0.5)
A more sophisticated way of blending colors is by using the source alpha channel to govern the blend weight, so say you have a color black and a color white with 0.4 alpha, or opacity. You would blend using the following method black * 0.6 + white * 0.4 (your alpha sums up to 1.0), which would give you 0.4 a slightly "darker" than mid range gray.
To tie this back to your question, you specify the blend method as ONE, ONE, i.e, you're doing black * 1 + white * 1 which should result in 1 (all white).
Looking over the docs for libGDX, you have either the GL_ONE_MINUS_CONSTANT_ALPHA option for constant weight blend, or if you want to do "alpha blending" you could go for GL_ALPHA, GL_ONE_MINUS_SRC_ALPHA.
Hope this helps and clears up blending a bit :)
This have been driving me crazy for the past couple of days.
I'm animating a spritesheet, and it actually works out fine on my 96px 384px texture with this code:
glBegin(GL_QUADS);
glTexCoord2f((frameCount*24.0f)/imgWidth, (row*24.0f)/imgHeight); glVertex3f(0+x, 0+y, -0.001f*(y+32));
glTexCoord2f((frameCount*24.0f)/imgWidth, ((row+1)*24.0f)/imgHeight); glVertex3f(0+x, 32+y, -0.001f*(y+32));
glTexCoord2f(((frameCount+1)*24.0f)/imgWidth, ((row+1)*24.0f)/imgHeight); glVertex3f(32+x, 32+y, -0.001f*(y+32));
glTexCoord2f(((frameCount+1)*24.0f)/imgWidth, (row*24.0f)/imgHeight); glVertex3f(32+x, 0+y, -0.001f*(y+32));
glEnd();
Problem is though, that when I load in a 32px 32px texture, it looks weird! I suspect that the number 24.0f should be different according to the texture size, but I can't figure out how.
Second question: How does this method affect the performance, are there better ways of doing it?
The texture coordinate for the x-axis (width or u value) should be:
frameCount * (frameWidth / imgWidth)
with frameWidth being the width of each frame in your texture and imgWidth being the total width of the texture.
The texture coordinate for the y-axis (height or v value) should be:
frameCount * (frameHeight / imgHeight)
with frameHeight being the height of each frame in your texture and imgHeight being the total height of the texture (in this case they are probably the same since each frame texture has same height as the entire texture here - or that's what I'm assuming by looking at your code).
If you want the code to be more efficient, you can precompute the multiplications that happen multiple times for each quad. So you can probably precompute:
float widthFraction = frameWidth / imgWidth;
float heightFraction = frameHeight / imgHeight;
The same applies for the vertex coordinate calculations, by the way.
Over hundreds of thousands of vertices, this will definitely speed the computations up a bit, but you should compare the two methods to see how much.
I have one outer canvas inside which I am loading another canvas (large size). I have set the clip geometry so only a part of inner (large) canvas is visible on screen.
Since the inner canvas is large in size so I have kept the initial scale of inner canvas as 0.4 i.e. I have applied composite transform on inner canvas and made scaleX and scaleY as 0.4.
Now I have implemented gesture listener methods OnPinchStart and OnPinchDelta. In OnPinchDelta I am zooming the canvas.
The problem is that since the initial scale is 0.4 so until the scale reaches (or scale crosses 1), the canvas is not zooming from center means it's position gets changed. However as soon as scale factor reaches (or crosses) 1, the zooming of canvas starts properly.
In short when the scale factor is less than 1 the zooming is not happening from center or other way, canvas position does not remain proper.
I have tried many different approaches but scale factor less than 1 is not working properly for me. Any help.
var factor = // calculate this based on amount of data and the speed
var width = // element rendered width
var height = // element rendered height
var changeInWidth = (1-factor) * width; // can be 0, neg or pos
var changeInHeight = (1-factor) * height;
var changeInX = changeInWidth / 2;
var changeInY = changeInHeight / 2;
Canvas.SetLeft(element, Canvas.GetLeft(element) + changeInX);
Canvas.SetTop(element, Canvas.GetTop(element) + changeInY);
Just make sure your element is wrapped by a Canvas, otherwise the Left and Top would be ignored.
In order to calculate factor, you can use e.DistanceRatio;
It can be used itself as factor, but you can multiply it by a constant if you want to increase(e.g. 1.2) or decrease (0.8) the speed of zooming.