Can anyone explain this small offset from ray casting by mouse click? - wpf

I'm using SharpGL in a WPF window and have navigation and plotting of a survey point cloud with custom shaders for distance-based point size reduction. All working well but I've recently attempted to add the ability to select a point nearest to the ray cast by a mouse click. I used the steps 0 to 4 in this link: http://antongerdelan.net/opengl/raycasting.html
The problem I'm facing is that the ray seems to be very slightly shifted towards the camera vector causing me to miss the points I'm clicking on and only scrape their inside edge. I'm drawing the ray starting from the camera position too so initially the ray should appear as a dot before moving the camera (looking down the trajectory line) but I can see it clearly drawn to the middle of the screen. Here's a screenshot showing where the ray misses the point when I click on its center:
This is the code I'm using to calculate the ray start and end coordinates:
public void CreateVerticesForMouseCoord(OpenGL gl, float winx, float winy)
{
int[] viewport = new int[4];
gl.GetInteger(OpenGL.GL_VIEWPORT, viewport);
float window_width = viewport[2];
float window_height = viewport[3];
Console.WriteLine("Window size: " + window_width + "," + window_height);
vec4 clipPosition = new vec4(0.0f, 0.0f, 1.0f, 1.0f);
// Normalize the x and y coordinates
clipPosition.x = 2.0f * (winx / window_width) - 1.0f;
clipPosition.y = -2.0f * (winy / window_height) + 1.0f;
mat4 viewInv = glm.inverse(viewMatrix);
mat4 projInv = glm.inverse(projectionMatrix);
vec4 camPosition = projInv * clipPosition;
camPosition.w = 0;
camPosition.z = -1;
vec4 worldPosition = viewInv * camPosition;
vec4 camCamPos = new vec4(0.0f, 0.0f, 0.0f, 1.0f);
vec4 objCamPos = glm.inverse(viewMatrix) * camCamPos;
vec4 camCamDir = new vec4(0.0f, 0.0f, -1.0f, 0.0f);
vec4 objCamDir = glm.inverse(viewMatrix) * camCamDir;
float x1 = objCamPos.x + 30.0f * worldPosition.x;
float y1 = objCamPos.y + 30.0f * worldPosition.y;
float z1 = objCamPos.z + 30.0f * worldPosition.z;
float x2 = objCamPos.x - 0.0f * worldPosition.x;
float y2 = objCamPos.y - 0.0f * worldPosition.y;
float z2 = objCamPos.z - 0.0f * worldPosition.z;
Console.WriteLine("Screen coordinates: " + winx + ","+winy);
Console.WriteLine("Clip coordinates: " + clipPosition.x + "," + clipPosition.y + "," + clipPosition.z+ ","+ clipPosition.w);
Console.WriteLine("Camera coordinates: " + camPosition.x + "," + camPosition.y + "," + camPosition.z+","+ camPosition.w);
Console.WriteLine("World coordinates: " + worldPosition.x + "," + worldPosition.y + "," + worldPosition.z+","+ worldPosition.w);
Console.WriteLine("Camera Position: " + objCamPos.x + "," + objCamPos.y + "," + objCamPos.z + "," + objCamPos.w);
Console.WriteLine("Camera Direction: " + objCamDir.x + "," + objCamDir.y + "," + objCamDir.z + "," + objCamDir.w);
float[] mousePointArray = { x1, y1, z1, x2, y2, z2 };
The output looks like this:
Window size: 470,314
Screen coordinates: 316,184
Clip coordinates: 0.3446808,-0.1719745,1,1
Camera coordinates: 0.1877808,-0.06259361,-1,0
World coordinates: 0.1877808,-0.06259361,-1,0
Camera Position: 4,7,6,1
Camera Direction: 0,0,-1,0
The offset does appear to get bigger the further away from the center of the screen I click. Any help I can get diagnosing this would be much appreciated, I've spent two days reading articles and fiddling with the code to no avail.

Related

Apache Solr heatmap : How to convert in usable coordinate int2d array returned by heatmap

I'm using faceting heatmap on a spatial field which then returns a 2d array like this
"counts_ints2D",
[
null,
null,
null,
null,
[
0,
8,
4,
0,
0,
0,
0,
0,
0,
...
I want to locate those cluster on the map but the problem is that I don't know how to convert that 2d array in geo coordinates.
There's absolutely no documentation out there showing what to do with those integer.
Can somebody give some guidance ?
Going with the data you gave for Glasgow, and using the formula given in the comments, lets explore the coordinates in a python repl:
# setup
>>> minX = -180
>>> maxX = 180
>>> minY = -53.4375
>>> maxY = 74.53125
>>> columns = 256
>>> rows = 91
# calculate widths
>>> bucket_width = (maxX - minX) / columns
>>> bucket_width
1.40625
>>> bucket_height = (maxY - minY) / rows
>>> bucket_height
1.40625
# calculate area for bucket in heatmap facet for x = 124, y = 13
# point in lower left coordinate
>>> lower_left = {
... 'lat': maxY - (13 + 1) * bucket_height,
... 'lon': minX + 124 * bucket_width,
... }
>>> lower_left
{'lat': 54.84375, 'lon': -5.625}
# point in upper right
>>> upper_right = {
... 'lat': maxY - (13 + 1) * bucket_height + bucket_height,
... 'lon': minX + 124 * bucket_width + bucket_width,
... }
>>> upper_right
{'lat': 56.25, 'lon': -4.21875}
Let's graph these points on a map, courtesy of open street map. We generate a small CSV snippet we can import on umap (select the up arrow, choose 'csv' as the type and enter content into the text box). To our coordinates to show:
>>> bbox = [
... "lat,lon,description",
... str(lower_left['lat']) + "," + str(lower_left['lon']) + ",ll",
... str(upper_right['lat']) + "," + str(lower_left['lon']) + ",ul",
... str(upper_right['lat']) + "," + str(upper_right['lon']) + ",uu",
... str(lower_left['lat']) + "," + str(upper_right['lon']) + ",lu",
... ]
>>> print("\n".join(bbox))
lat,lon,description
54.84375,-5.625,ll
56.25,-5.625,ul
56.25,-4.21875,uu
54.84375,-4.21875,lu
After pasting these points into the import box creating the layer, we get this map:
Map based on Open Street Map data through uMap. This area encloses Glasgow as you expected.
Here's some code that takes 180th meridian (date line) wrapping into account:
$columns = $heatmap['columns'];
$rows = $heatmap['rows'];
$minX = $heatmap['minX'];
$maxX = $heatmap['maxX'];
$minY = $heatmap['minY'];
$maxY = $heatmap['maxY'];
$counts = $heatmap['counts_ints2D'];
// If our min longitude is greater than max longitude, we're crossing
// the 180th meridian (date line).
$crosses_meridian = $minX > $maxX;
// Bucket width needs to be calculated differently when crossing the
// meridian since it wraps.
$bucket_width = $crosses_meridian
? $bucket_width = (360 - abs($maxX - $minX)) / $columns
: $bucket_width = ($maxX - $minX) / $columns;
$bucket_height = ($maxY - $minY) / $rows;
$points = [];
foreach ($counts as $rowIndex => $row) {
if (!$row) continue;
foreach ($row as $columnIndex => $column) {
if (!$column) continue;
$point = []
$point['count'] = $column;
// Put the count in the middle of the bucket (adding a half height and width).
$point['lat'] = $maxY - (($rowIndex + 1) * $bucket_height) + ($bucket_height / 2);
$point['lng'] = $minX + ($columnIndex * $bucket_width) + ($bucket_width / 2);
// We crossed the meridian, so wrap back around to negative.
if ($point['lng'] > 180) {
$point['lng'] = -1 * (180 - ($point['lng'] % 180));
}
$points[] = $point;
}
}

Create bounding box from center coordinate

I'm trying to create a bounding box at a specific scale from a center coordinate. I'm trying to keep it within the aspect ratio of a 8.5x11inch piece of paper (612x792 pixels # 72dpi).
The code I'm using below mostly works, but the heigh seems a bit too tall for the aspect ratio of a letter. Am I not accounting for mercator projection? What am I missing here?
def bounding_box_from_point(center:, size:, scale_denominator:)
dpi = 72
inches_per_unit = 4374754
resolution = 1 / (scale_denominator * inches_per_unit * dpi)
half_width_deg = (size.width * resolution) / 2
half_height_deg = (size.height * resolution) / 2
BoundingBox.new(
north: center.lat + half_height_deg,
south: center.lat - half_height_deg,
east: center.lon + half_width_deg,
west: center.lon - half_width_deg
)
end
Calling bounding_box_from_point(center: center, size: size, scale_denominator: scale) with:
scale = 0.0008861342166177423 (i.e. 1/18055.955520)
center = Geometry::Location.new(lat: 37.806336, lon: -122.270625)
size.width = 612,
size.height = 792
It returns:
west: -122.27172131608657,
east: -122.26952868391342,
south: 37.804917238005615
north: 37.80775476199439
If you go to http://www.openstreetmap.org/export and enter those bounding box coordinates, you can see that the ratio does not match that of a 8.5x11in piece of paper...it's slightly too tall. What am I doing wrong here or not understanding?
This was my implementation that solved it!
def self.bounding_box_from_location(location:, scale:)
scale_meters_per_pixel = 0.0003
# scale.zoom is an integer between 0-19
# See http://wiki.openstreetmap.org/wiki/Zoom_levels for a description on `zoom`
# location.lat_rad is the latitude in radians, same for lon_rad
meters_per_pixel = EARTH_CIR * Math.cos(location.lat_rad) / (2 ** (scale.zoom + 8))
earth_meters_per_pictureMeters = meters_per_pixel / scale_meters_per_pixel
# height/width in meters is the height/width of the box you're applying this to
# In my case I'm using the heigh/width of a Letter of paper
# You can convert Pixels to meters (# 72dpi) with
# pixels * 0.00035277777777777776
meters_north = earth_meters_per_pictureMeters * scale.height_in_meters / 2
meters_east = earth_meters_per_pictureMeters * scale.width_in_meters / 2
meters_per_degree_lat = 111111.0
meters_per_degree_long = 111111.0 * Math.cos(location.lat_rad)
degrees_north = meters_north / meters_per_degree_lat
degrees_east = meters_east / meters_per_degree_long
BoundingBox.new(
north: (location.lat + degrees_north),
south: location.lat - degrees_north,
east: location.lon + degrees_east,
west: location.lon - degrees_east,
width: scale.width_in_pixels,
height: scale.height_in_pixels
)
end

Partial differentiation/gradient of anonymous function with array input

I have following anonymous function (with x as an array):
f = #(x) 312*x(2) - 240*x(1) + 30*x(3) - 24*x(4) + 282*x(1)*x(2) + 30*x(1)*x(3) + 18*x(1)*x(4) + 54*x(2)*x(3) + 6*x(2)*x(4) + 6*x(3)*x(4) + 638*x(1)^2 + 207*x(2)^2 + 6*x(3)^2 + 3*x(4)^2 + 4063
I want to make gradient of this function and save it for future use. Also with array input.
X = [ 0;...
0;...
0;...
0];
F = f(X)
G = g(X)
Is it possible to archive this with this type of function? Or maybe it is possible to somehow make it via diff command? Something like this:
g = [diff(f, x(1));...
diff(f, x(2));...
diff(f, x(3));...
diff(f, x(4))]
I guess the following is what you want. I'm afraid, you need the Symbolic Math Toolbox for a simple solution, otherwise I'd rather calculate the derivatives by hand.
x = [1 2 3 4];
%// define function
syms a b c d
f = 312*b - 240*a + 30*c - 24*d + 282*a*b + 30*a*c + 18*a*d + 54*b*c + ...
6*b*d + 6*c*d + 638*a^2 + 207*b^2 + 6*c^2 + 3*d^2 + 4063
%// symbolic gradient
g = gradient(f,[a,b,c,d])
%// eval symbolic function
F = subs(f,[a,b,c,d],x)
G = subs(g,[a,b,c,d],x)
%// convert symbolic value to double
Fd = double(F)
Gd = double(G)
or alternatively:
%// convert symbolic function to anonymous function
fd = matlabFunction(f)
gd = matlabFunction(g)
%// eval anonymous function
x = num2cell(x)
Fd = fd(x{:})
Gd = gd(x{:})
f =
638*a^2 + 282*a*b + 30*a*c + 18*a*d - 240*a + 207*b^2 + 54*b*c +
6*b*d + 312*b + 6*c^2 + 6*c*d + 30*c + 3*d^2 - 24*d + 4063
g =
1276*a + 282*b + 30*c + 18*d - 240
282*a + 414*b + 54*c + 6*d + 312
30*a + 54*b + 12*c + 6*d + 30
18*a + 6*b + 6*c + 6*d - 24
F =
7179
G =
1762
1608
228
48
fd =
#(a,b,c,d)a.*-2.4e2+b.*3.12e2+c.*3.0e1-d.*2.4e1+a.*b.*2.82e2+a.*c.*3.0e1+a.*d.*1.8e1+b.*c.*5.4e1+b.*d.*6.0+c.*d.*6.0+a.^2.*6.38e2+b.^2.*2.07e2+c.^2.*6.0+d.^2.*3.0+4.063e3
gd =
#(a,b,c,d)[a.*1.276e3+b.*2.82e2+c.*3.0e1+d.*1.8e1-2.4e2;a.*2.82e2+b.*4.14e2+c.*5.4e1+d.*6.0+3.12e2;a.*3.0e1+b.*5.4e1+c.*1.2e1+d.*6.0+3.0e1;a.*1.8e1+b.*6.0+c.*6.0+d.*6.0-2.4e1]
x =
[1] [2] [3] [4]
Fd =
7179
Gd =
1762
1608
228
48

opengl matrix math multiplication

I am writing a simple c 4x4 matrix math library and wanted some feedback, especially from people with opengl experience.
Typically there's two ways to do matrix multiplication. I tested this code and it works, according to results from wolfram alpha but my main concern is that this matrix is in the right order.
My matrix is just an array of 16 doubles.
The code to do the multiplication is below
out->m[0] = ( a->m[0] * b->m[0]) + (a->m[1] * b->m[4]) + (a->m[2] * b->m[8]) + (a->m[3] * b->m[12] );
out->m[4] = ( a->m[4] * b->m[0]) + (a->m[5] * b->m[4]) + (a->m[6] * b->m[8]) + (a->m[7] * b->m[12] );
out->m[8] = ( a->m[8] * b->m[0]) + (a->m[9] * b->m[4]) + (a->m[10] * b->m[8]) + (a->m[11] * b->m[12] );
out->m[12] = ( a->m[12] * b->m[0]) + (a->m[13] * b->m[4]) + (a->m[14] * b->m[8]) + (a->m[15] * b->m[12] );
out->m[1] = ( a->m[0] * b->m[1]) + (a->m[1] * b->m[5]) + (a->m[2] * b->m[9]) + (a->m[3] * b->m[13] );
out->m[5] = ( a->m[4] * b->m[1]) + (a->m[5] * b->m[5]) + (a->m[6] * b->m[9]) + (a->m[7] * b->m[13] );
out->m[9] = ( a->m[8] * b->m[1]) + (a->m[9] * b->m[5]) + (a->m[10] * b->m[9]) + (a->m[11] * b->m[13] );
out->m[13] = ( a->m[12] * b->m[1]) + (a->m[13] * b->m[5]) + (a->m[14] * b->m[9]) + (a->m[15] * b->m[13] );
out->m[2] = ( a->m[0] * b->m[2]) + (a->m[1] * b->m[6]) + (a->m[2] * b->m[10]) + (a->m[3] * b->m[14] );
out->m[6] = ( a->m[4] * b->m[2]) + (a->m[5] * b->m[6]) + (a->m[6] * b->m[10]) + (a->m[7] * b->m[14] );
out->m[10] = ( a->m[8] * b->m[2]) + (a->m[9] * b->m[6]) + (a->m[10] * b->m[10]) + (a->m[11] * b->m[14] );
out->m[14] = ( a->m[12] * b->m[2]) + (a->m[13] * b->m[6]) + (a->m[14] * b->m[10]) + (a->m[15] * b->m[14] );
out->m[3] = ( a->m[0] * b->m[3]) + (a->m[1] * b->m[7]) + (a->m[2] * b->m[11]) + (a->m[3] * b->m[15] );
out->m[7] = ( a->m[4] * b->m[3]) + (a->m[5] * b->m[7]) + (a->m[6] * b->m[11]) + (a->m[7] * b->m[15] );
out->m[11] = ( a->m[8] * b->m[3]) + (a->m[9] * b->m[7]) + (a->m[10] * b->m[11]) + (a->m[11] * b->m[15] );
out->m[15] = ( a->m[12] * b->m[3]) + (a->m[13] * b->m[7]) + (a->m[14] * b->m[11]) + (a->m[15] * b->m[15] );
I wanted to make sure that this will give me the correct results for setting up my transformation matrix.
matrix m = 1,3,4,-1,5,6,7,-1,8,8,8,-1,0,0,0,1
which is arranged in memory like this:
1,3,4,-1
5,6,7,-1
8,8,8,-1
0,0,0,1
which I think is the way opengl lays out it's matrix as 16 numbers.
using my code my answer comes out to be
[ 48.000000 53.000000 57.000000 -9.000000 ]
[ 91.000000 107.000000 118.000000 -19.000000 ]
[ 112.000000 136.000000 152.000000 -25.000000 ]
[ 0.000000 0.000000 0.000000 1.000000 ]
which is the transpose of wolfram alpha's answer.
(48 | 91 | 112 | 0
53 | 107 | 136 | 0
57 | 118 | 152 | 0
-9 | -19 | -25 | 1)
Typically it looks like this, vertex point v model, view, projection matrices
position = projection * view * model * v
I can't say you why your results differ but one help is, if you send the matrix into a GLSL uniform dMat4, you can use the build in transpose functionallity of OpenGL to get the right matrix alignment:
glUniformMatrix4fv( Uniform_Location, 1, GL_TRUE, MatrixPointer );
The third parameter means, if OpenGL should transpose the matrix before setting the uniform.

Calculating OpenGL Face Normals of Polygon

I am trying to add lighting to a polygon created from a point polygon file. My problem is when creating vectors from the points I only get a certain section of the polygons lit, then if I inverse the calculation for normals, (From A.x - C.x to C.x - A.x) it lights the section that was not previously lit. Code and pictures below.
h_vector V1;//= (p2 - p1);
// A = 0 B = 1 C = 2 D =3
V1.x = vertices[1].x - vertices[0].x;
V1.y = vertices[1].y - vertices[0].y;
V1.z = vertices[1].z - vertices[0].z;
h_vector V2;// = (p3 - p1);
V2.x = vertices[3].x - vertices[0].x;
V2.y = vertices[3].y - vertices[0].y;
V2.z = vertices[3].z - vertices[0].z;
/*
h_vector V1;//= (p2 - p1);
V1.x = vertices[0].x - vertices[1].x;
V1.y = vertices[0].y - vertices[1].y;
V1.z = vertices[0].z - vertices[1].z;
h_vector V2;// = (p3 - p1);
V2.x = vertices[0].x - vertices[3].x;
V2.y = vertices[0].y - vertices[3].y;
V2.z = vertices[0].z - vertices[3].z;
*/
surfaceNormal.x = (V1.y*V2.z) - (V1.z-V2.y);
surfaceNormal.y = - ( (V2.z * V1.x) - (V2.x * V1.z) );
surfaceNormal.z = (V1.x-V2.y) - (V1.y-V2.x);
float normalize = sqrtf((pow(surfaceNormal.x,2) + pow(surfaceNormal.y,2) + pow(surfaceNormal.z,2)));
surfaceNormal.x = surfaceNormal.x/normalize;
surfaceNormal.y = surfaceNormal.y/normalize;
surfaceNormal.z = surfaceNormal.z/normalize;
This cross product code is badly broken:
surfaceNormal.x = (V1.y*V2.z) - (V1.z-V2.y);
surfaceNormal.y = - ( (V2.z * V1.x) - (V2.x * V1.z) );
surfaceNormal.z = (V1.x-V2.y) - (V1.y-V2.x);
The expressions within the parentheses should all have a multiplication operator, but half of them are subtractions instead in the code above.
The cross product calculation should be:
surfaceNormal.x = V1.y * V2.z - V1.z * V2.y;
surfaceNormal.y = V1.z * V2.x - V1.x * V2.z;
surfaceNormal.z = V1.x * V2.y - V1.y * V2.x;
You may want to look into using a matrix/vector library. There are many of them freely available if you do some searching.

Resources