how to account for linewidth in drawing lines within a bounding box - algorithm

I am drawing a set of evenly spaced horizontal lines within the entirety of a bounding box.
The problem I am having is that the lines (when larger than 1px) get drawn beyond the top and bottom edges of my bounds. Half on each side of the top and bottom is missing, to be precise.
Here is some pseudo code that attempts a fix for this, but it didn't work. It should describe what I am trying to do:
var halfline = linewidth / 2.;
var maxheight = boxsize.height - halfline;
var minheight = halfline;
//draw h lines
for(i = 0; i < maxlines; i++)
{
var xloc = 0;
var xfrac = i / maxlines - 1;
var yloc = (xfrac * boxsize.height) + minheight;
move_to(xloc, yloc);
line_to(boxsize.width, yloc);
}
Please keep in mind that the lang is not important here, just the idea of how to offset and scale the lines (that are drawn within the for loop) properly.
Thanks for any tips... It's safe to assume the following:
the line width is in pixels
the coordinate system is pixel-based, from (0,0) to (n,n)

Your question is a little unclear, but I think this might help:
var availablespace = boxsize.height - linewidth;
...
var yloc = (xfrac * availablespace) + minheight;

Related

D3 Donut chart projected to sphere/globe

I want to use d3 for the next task:
display rotating globe with donut chart in center of every country. It should be possible to interact with globe (select country, zoom, rotate).
Seems d3 provide an easy way to implement every part of it but I can not get donuts part working as I need.
There is an easy way draw donut chart with the help of d3.arc:
var arc = d3.arc();
var data = [3, 23, 17, 35, 4];
var radius = 15/scale;
var _arc = arc.innerRadius(radius - 7/scale)
.outerRadius(radius).context(donutsContext);
var pieData = pie(data);
for (var i = 0; i < pieData.length; i++) {
donutsContext.beginPath();
donutsContext.fillStyle = color(i);
_arc(pieData[i]);
}
by with code as it is donuts are displayed on a plane on top of the globe, like:
globe with donut
​
while I want them to be 'wrapped' around the globe
There is d3.geoCircle method that can be projected to globe correctly. I got 'ring' projected correctly to the globe with the help of two circles:
var circle = d3.geoCircle()
.center(centroid)
.radius(2);
var outerCircle = circle();
var circle = d3.geoCircle()
.center(centroid)
.radius(1);
var innerCircle = circle();
var interCircleCoordinates = [];
for (var i = innerCircle.coordinates[0].length - 1; i >= 0; i--) {
interCircleCoordinates.push(innerCircle.coordinates[0][i]);
}
outerCircle.coordinates.push(interCircleCoordinates);
​globe with rings
but I really need to get a donut.
The other way I tried is getting image from donuts and wrapping this image around globe with the help of pixels manipulation:
var image = new Image;
image.onload = onload;
image.src = img;
function onload() {
window.dx = image.width;
window.dy = image.height;
context.drawImage(image, 0, 0, dx, dy);
sourceData = context.getImageData(0, 0, dx, dy).data;
target = context.createImageData(width, height);
targetData = target.data;
for (var y = 0, i = -1; y < height; ++y) {
for (var x = 0; x < width; ++x) {
var p = projection.invert([x, y]), λ = p[0], φ = p[1];
if (λ > 180 || λ < -180 || φ > 90 || φ < -90) { i += 4; continue; }
var q = ((90 - φ) / 180 * dy | 0) * dx + ((180 + λ) / 360 * dx | 0) << 2;
var r = sourceData[q];
var g = sourceData[++q];
var b = sourceData[++q];
targetData[++i] = r;
targetData[++i] = g;
targetData[++i] = b;
targetData[++i] = 125;//
}
}
context.clearRect(0,0, width, height);
context.putImageData(target, 0, 0);
};
by this way I get extremely slow rotating and interaction with a globe for a globe size I need (1000px)
So my questions are:
Is there is some way to project donuts that are generated with the help of d3.arc to a sphere (globe, orthographic projection)?
Is there is some way to get a donut from geoCircle?
Maybe there is some other way to achieve my goal I do not see
There is one way that comes to mind to display donuts on a globe. The key challenge is that d3 doesn't project three dimensional objects very well - with one exception, geographic features. Consequently, an "easy" solution is to convert your pie charts into geographic features and project them with the rest of your features.
To do this you need to:
Use a pie/donut generator as you normally would
Go along the paths generated to get points approximating the pie shape.
Convert the points to long/lat points
Assemble those points into geojson
Project them onto the map.
The first point is easy enough, just make a pie chart with an inner radius.
Now you have to select each path and find points along its perimeter using path.getPointAtLength(), this will be dependent on path length, so path.getTotalLength() will be handy (and corners are important, so you might want to incorporate a little bit of complexity for these corner cases to ensure you get them)).
Once you have the points, you need the use of a second projection, azimuthal equidistant would be best. If the pie chart is centered on [0,0] in svg coordinate space, rotate the azimuthal (don't center), so that the centroid coordinate is located at [0,0] in svg space (you can use translates on the pies to position them, but it will just add extra steps). Take each point and run it through projection.invert() using the second projection. You will need to update the projection for each donut chart as each one will have a different geographic centroid.
Once you have lat long points, it's easy - you've already done it with the geo circle function - convert to geojson and project with the orthographic projection.
This approach gave me something like:
Notes: Depending on your data, it might be easiest to preprocess your data into geojson and store that as opposed to calculating the geojson each page load.
You are using canvas, while you don't need to actually use an svg, you need to still be able to access svg functions like getPointAtLength, you do not need to have an svg or display svg elements by using a custom element replicating a path :
document.createElementNS(d3.namespaces.svg, 'path');
Oh, and make sure the second projection's translate is set - the default is [480,250] for all (most?) d3 projections, that will throw things off if unaccounted for.

How to increase size of mesh?

Here's a demo of a rotating paper cup
https://jsfiddle.net/o5cd8g0h/
I want to increase the size of image in the cup, tried this
logoMesh.scale.x = 1.5;
logoMesh.scale.y = 1.5;
logoMesh.scale.z = 1.5;
logoMesh.position.set(0,-0.6,-0.6);
But the result is not what i expect https://jsfiddle.net/nL2spe5d/
Image gets placed incorrectly
How to increase size of logoMesh?
As I get it, it depends on logoFaceArray. In simple words, it contains indices of faces. 6 faces on width, 8 faces on height.
Thus, if you want to make your logo mesh bigger, then you have to change this array of indices.
var logoFaceArray = [];
var multiplier = 2;
for(var lx = 0; lx < 6 * multiplier; lx++){
for(var ly = 0; ly < 8 * multiplier; ly++){
var index = 24 * lx + ly;
logoFaceArray.push(index);
}
}
Here you can play around with the multiplier variable, or you can leave it equal to 1, but change the limits of lx and ly, but remember that they (lx, ly) have to be even numbers, else you'll get a saw edge at the bottom of the logo.
jsfiddle example

How does depth work in a frustum environment?

I need some help understanding the basics of a frustum transformation. Mainly, how depth works.
The following uses a viewport of 768x1024. Using an Orthogonal projection and a square of 768x768 (z defaults to 0) with no translation or scaling, and a viewport of glViewport(0, 0, 768, 1024) this square easily fills the width of the frame:
Now when I change the project to a frustum and mess with the z translation, the square scales appropriately due to the perspective changes.
Here is the same square in such an environment:
I can play with this z translation, as well as the near and far parameters of the frustum matrix and make the square change is apparent onscreen size accordingly. Fine.
But what I cannot figure out is the obvious relationship between its onscreen size and these depth parameters.
For example, suppose I want to use a frustum but have the square fill the frame width, as in my first example image above. How to achieve this?
I would think that if the z translation matched the near plane, then you'd essentially have a square "right in front of the camera", filling the frame. But I cannot figure a way to achieve this. If my near is 1 and my z translation is -1, then the square should be sitting right on the near plane itself (right?!) , filling the width of the frame (where the frustum's left and right planes are the same as the orthogonal projection).
I could paste a bunch of code here to show what I'm doing but I think the concept here is clear. I just want to figure out where the near plane actually is, how to situate something on it, as this will help me understand how the frustum is working.
Okay here is the relevant code I'm using, where width=768 and height=1024.
My vertex shader is the simple gl_Position=Projection*Modelview*Position;
My projection matrix (frustum) is thus:
Frustum(-width/2, width/2, -height/2, height/2, 1,10);
This function is:
static Matrix4<T> Frustum(T left, T right, T bottom, T top, T near, T far)
{
T a = 2 * near / (right - left);
T b = 2 * near / (top - bottom);
T c = (right + left) / (right - left);
T d = (top + bottom) / (top - bottom);
T e = - (far + near) / (far - near);
T f = -2 * far * near / (far - near);
Matrix4 m;
m.x.x = a; m.x.y = 0; m.x.z = 0; m.x.w = 0;
m.y.x = 0; m.y.y = b; m.y.z = 0; m.y.w = 0;
m.z.x = c; m.z.y = d; m.z.z = e; m.z.w = -1;
m.w.x = 0; m.w.y = 0; m.w.z = f; m.w.w = 1;
return m;
}
My square is just two 2d triangles with a default z=0, and an x range from left as -768/2 and right edge at 768/2. The square is clearly working properly as my first image above shows, using the orthogonal projection. (Though I switched to the frustum projection for this question)
To draw the square, I translate the Modelview with:
Translate(0, 0, -1);
Using:
static Matrix4<T> Translate(T x, T y, T z)
{
Matrix4 m;
m.x.x = 1; m.x.y = 0; m.x.z = 0; m.x.w = 0;
m.y.x = 0; m.y.y = 1; m.y.z = 0; m.y.w = 0;
m.z.x = 0; m.z.y = 0; m.z.z = 1; m.z.w = 0;
m.w.x = x; m.w.y = y; m.w.z = z; m.w.w = 1;
return m;
}
As you can see, the translation should put the square on the near plane, yet it looks like this:
If I translate instead of -1.01 just to be sure I avoid near clipping, the result is the same. If I do not translate, thus z=0, the square does not appear, as you'd expect, since it would be behind the camera.
In your frustum matrix, m.w.w should be 0, not 1. This will fix your problem.
But, the mistake isn't your fault. It's my fault! I'm actually the one who wrote that code in the first place, and unfortunately it has proliferated. It's an errata in my book (iPhone 3D Programming), which is where it first appeared.
Feeling very guilty about this!
If my near is 1 and my z translation is -1, then the square should be sitting right on the near plane itself (right?!)
Yes
, filling the width of the frame (where the frustum's left and right planes are the same as the orthogonal projection).
Not neccesarily. The near plane has the extents given with the left, right, bottom and top parameters of glFrustum. A rectangle going to exactly those bounds will snugly fit the viewport when being placed at the near plane distance.

How to change only width of path(rect, circle) while mouse drag using paper.js

I have stucked at resizing only width of an item in the canvas using paper.js
I have done in following ways to resize , but it results in resizing both left and right sides from the center of rectangle/circle.
function onMouseDrag(event){
(selectedItem.bounds.contains(event.point)) ?
selectedItem.scale(0.9871668311944719,1) : selectedItem.scale(1.013, 1);
}
above code resizes in both x-directions.
Help me to resize width only in one direction.
thanks,
suribabu.
You can center the scale operation at any point by using the form:
scale(hor, ver, point)
So in your case, if you want to scale from the left-center of your selected item, you could use:
function onMouseDrag(event){
(selectedItem.bounds.contains(event.point)) ?
selectedItem.scale(0.9871668311944719, 1, selectedItem.bounds.left) : selectedItem.scale(1.013, 1, selectedItem.bounds.left);
}
I am not sure what you mean with scale only the width. If you want to have thicker path than changing the strokeWidth instead might do what you want.
If you are wondering why the scaled path expands in both directions on the x-axis than you might check the location of the paths local center. If some nodes of the path have negative coordinates relative to this local center, scaling them with a positive value decreases their coordinates even more.
Perhaps you should normalize all the vertices, means move them directly so that the smallest x and y value of all vertices is 0.
Greetings and good luck
Try this:
function onMouseDown(e) {
var cx = e.point.x;
var cy = e.point.y;
var rectangle = new Rectangle(e.point, new Size(1,1));
path = new Path.Ellipse(rectangle);
path.strokeColor = 'red';
}
function onMouseDrag(e) {
path.remove();
var x = Math.min(e.point.x, cx),
y = Math.min(e.point.y, cy),
w = Math.abs(e.point.x -cx),
h = Math.abs(e.point.y -cy);
var rectangle = new Rectangle(x,y,w,h);
path = new Path.Ellipse(rectangle);
path.strokeColor = 'red'
}

Snapping vertical line cursor to data point in jqPlot

I have just started using jqPlot for a line chart with multiple series. It seems great.
I have added the Cursor plugin with the intention of showing a vertical line on the nearest data point on the x axis. In other words, it snaps to the nearest point. The Cursor plugin, however always shows the vertical cursor right where the mouse is.
It seems like I just want to "override" or replace moveLine to change the current functionality.
What's the most appropriate way of doing so?
It seems a little much to copy/past all of the cursor plugin just to modify a very small subset.
Thanks!
I know I'm a kind of archaeologist by edited this post but I think the following can be useful for someone (I hope).
I've made a piece of code which allow to draw a vertical line following the cursor and displaying a tooltip on the nearest point (according to the cursor). You can find a demo of it on this JSFiddle.
I also post the corresponding code below (only the part which calculate nearest point and display tooltip):
//Show nearest point's tooltip
$("#chart1").bind('jqplotMouseMove', function(ev, gridpos, datapos, neighbor, data){
var c_x = datapos.xaxis;
var index_x = -1;
var pos_index = 0;
var low = 0;
var high = data.data[0].length-1;
while(high - low > 1){
var mid = Math.round((low+high)/2);
var current = data.data[0][mid][0];
if(current <= c_x)
low = mid;
else
high = mid;
}
if(data.data[0][low][0] == c_x){
high = low;
index_x = high;
}else{
var c_low = data.data[0][low][0];
var c_high = data.data[0][high][0];
if(Math.abs(c_low - c_x) < Math.abs(c_high - c_x)){
index_x = low;
}else{
index_x = high;
}
}
//Display marker and tooltip
if(data.series[0].data[index_x]){
var x = data.series[0].gridData[index_x][0];
var y = data.series[0].gridData[index_x][1];
var r = 5;
var highlightCanvas = $(".jqplot-highlight-canvas")[0];
var context = highlightCanvas.getContext('2d');
context.clearRect(0,0,highlightCanvas.width,highlightCanvas.height);
context.strokeStyle = 'rgba(47,164,255,1)';
context.fillStyle = 'rgba(47,164,255,1)';
context.beginPath();
context.arc(x,y,r,0,Math.PI*2,true);
context.closePath();
context.stroke();
context.fill();
//Display tooltip on nearest point
var highlightTooltip = $(".jqplot-highlighter-tooltip");
var val_x = data.data[0][index_x][0];
var val_y = data.data[0][index_x][1];
highlightTooltip.html("X : "+val_x+"<br/>Y : "+val_y);
highlightTooltip.css({'left': x+'px', 'top': (y-10)+'px', 'display': 'block'});
}
});
Feel please to use it and to modify it as you need it.
Try a bar graph series on top of everything else that has alpha set to 0.

Resources