Intersection points between circle and line with straights off - javascript

I am using intersection objects to show 2 intersection points between a circle and a line that is not infinite (straightFirst and straightLast are false).
JSX graph is treating that line as infinite even though I set alwaysIntersect false, here's my script:
var line = app.board.create('line', [[-2,-2],[1,1]], {straightFirst:false, straightLast:false })
var circle = app.board.create('circle', [[0,0],[-2,0]], { })
app.board.create('intersection', [line, circle, 0], {alwaysIntersect:false})
app.board.create('intersection', [line, circle, 1], {alwaysIntersect:false})
Output:
Expected output (it should show one point only, because the other one is not intersected)
Thank you in advance.

Related

Drawing faces over a country without overlapping borders in Three.js

My problem is that when creating faces for a country, there are faces which overlap the border.
Below is the image when all coordinates are used when creating the faces
image with all values (coordinates)
values_axis1 contains all the X coordinates [63.0613691022453, 65.1611029239906, 66.0721609548093, 68.8109022381195, 71.1822098033678..]
values_axis2 contains all the Y coordinates [34.37065245791981, 35.30003249470688, 38.11207931425505, 38.228254752215044..]
values_axis3 contains all the Z coordinates
for (var i = 0; i < values_axis1.length; i ++) {
object_geometry.vertices.push(new THREE.Vector3(values_axis1[i], values_axis2[i], values_axis3[i]));
object_geometry.faces.push(new THREE.Face3(0, i + 1, i));
}
Buy changing i++ to i+=2 we get this image of skipping some values
Arrow obviously shows the point zero where all triangles are drawn from.
Below is the part of code for that.
object_geometry.faces.push(new THREE.Face3(DRAW_FROM_HERE, i + 1, i));
This isn't per say a programming problem but more like a 'is there a algorithm for this'.
I could check for faces that collide with a border from point zero and skip them but then there would be holes and that would need to be filled by drawing from some other location. That could be done manually but doing that for all the countries in the world would take ages. I'm sure this could be done by somehow calculating where the holes are but I have no idea how that would be done.
If someone comes up even with a really bad solution performance wise it would be great! I'm planning on putting all the results in a file which could then be loaded and drawn since there is really no need to do this processing every time since the borders never move.
EDIT: ShapeGeometry was suggested and has been tested. I was wrong when I said that Z coordinates are not relevant but obviously they are due to the curvature of the globe.
Image of the shapeGeometry. 2d but otherwise perfect.
Question edited.
Edit: Solution
Thanks to #Gilles-Philippe Paillé for suggesting ear clipping. Found a great library https://github.com/mapbox/earcut
Here is updated code for others who might have the same issue.
function createVertexForEachPoint(object_geometry, values_axis1, values_axis2,
values_axis3) {
var values_x_y = []; // add x and y values to the same list [x,y,x,y,x,y..]
for (var i = 0; i < values_axis1.length; i++) {
values_x_y.push(values_axis1[i], values_axis2[i]);
}
// https://github.com/mapbox/earcut
var triangles = earcut(values_x_y);
// triangles = [37, 36, 35, 35, 34, 33, 32, 31, 30, 30, …] [a,b,c,a,b,c,a,b,c..]
// triangles contain only the verticies that are needed
// previously all possible faces were added which resulted in overlapping borders
// add all points, in this case coordinates. Same as before
for (var i = 0; i < values_axis1.length; i++) {
object_geometry.vertices.push(new THREE.Vector3(values_axis1[i], values_axis2[i], values_axis3[i]));
}
// go through the list and pick the corners (a,b,c) of the triangles and add them to faces
for (var i = 0; i < triangles.length; i += 3) {
var point_1 = triangles[i];
var point_2 = triangles[i+1];
var point_3 = triangles[i+2];
object_geometry.faces.push(new THREE.Face3(point_1, point_2, point_3));
}
}
I use a geoJSON which has 'polygon' and 'multipolygon' shapes. This code works for polygons atm but shouldn't need too much tweaking to work with multipolygons since the library supports also holes earcut(vertices[, holes, dimensions = 2]).
image of the result

d3 scaleBy from the left instead of the center

We're using d3 to build a graph and it has two custom buttons to handle the zooming as well as the standard zooming via the mousewheel/trackpad.
let local = this;
let zoomIn = d3.select("#zoom_in");
let zoomOut = d3.select("#zoom_out");
let reset = d3.select("#reset");
zoomIn.on("click", function() {
zoom.scaleBy(local.chart.transition().duration(500), 1.2);
});
zoomOut.on("click", function() {
zoom.scaleBy(local.chart.transition().duration(500), 0.8)
});
This zooms the graph fine (including the brush). However it zooms from the centre.
According to the documentation: https://github.com/d3/d3-zoom#zoom_scaleBy scaling from the centre is the default behaviour unless a position is provided... but we haven't been able to figure out what this parameter should actually be...
We have tried:
zoom.scaleBy(local.chart.transition().duration(500), 0.8, 0);
and also tried to use translateBy: https://github.com/d3/d3-zoom#zoom_translateBy to move it back to the left AFTER the scale:
zoom.scaleBy(local.chart.transition().duration(500), 0.8, 0);
zoom.translateBy(local.chart.transition().duration(500), 0, 0);
But this cancels out the zoom...
Is there any examples of using scaleBy to zoom from a position?
The API docs on zoom.scaleBy() have you covered (emphasis mine):
# zoom.scaleBy(selection, k[, p])
[…]
the p point may be specified either as a two-element array [px,py] or a function.
By providing a two-element array as the third argument to the call you can specify the center for the zooming. For your code this could be specified as follows:
zoom.scaleBy(local.chart.transition().duration(500), 1.2, [0, height / 2]);
// ↑ ↑
// [px,py ]
Similarly, this holds true for zoom.scaleTo(), zoom.translateBy() and zoom.translateTo().

Drawn polygon does not show in openlayers

I have to draw a polygon on openlayers Map. This is my code:
draw = new Draw({
source: this.vectorSource,
type: 'Polygon'
})
draw.on('drawend', e => {
// sol 1, result is not as required
let coords = e.feature.getGeometry().getCoordinates()
//sol 2, give correct results, but drawn polygon gone
let coords = e..feature.getGeometry().transform('EPSG:3857', 'EPSG:4326').getCoordinates()
}
this.olmap.addInteraction(draw)
I have to store the transformed coordinates in DB, but solution #2 does not maintain the visibility of drawn poloygon.
In case of solution #1, it does not gives the required formated coordinates, if I try to transform them later using
transform(coords, 'EPSG:3857', 'EPSG:4326')
it does not return formated coordinates.
please guide me where i am wrong to maintain the visibility of polygon and get the transformed coordinates.
You need to clone the geometry
let coords = e..feature.getGeometry().clone().transform('EPSG:3857', 'EPSG:4326').getCoordinates();
otherwise you wil move the feature somewhere close to point [0, 0] in view cooordinates

D3 Bounded Panning

I've been at this for a few days now and I can't get this chart to obey panning bounds. Initially, the data could be pulled off the page both negatively and positively, but I've been able to stop the negative by following this blog post. I'll paste what I believe is the relevant code here, but the file is way to long to include.
The chart in question is an elevation chart made up of concatenated area objects that are colored according to their gradient.
There's a commented out line that is the one giving trouble. I've put question marks in place of what is supposed to be a max bound. For some reason, I can't find the max bound of the data area.
Here's a Plunkr
// Set up the size of the chart relative to the div
var x = d3.scale.linear().range([0, (width-80)]);
var y = d3.scale.linear().range([height, 0]);
var y1 = d3.scale.linear().range([height, 0]);
// Define the look of the axis
var xAxis = d3.svg.axis().scale(x).orient("bottom").ticks(5);
var yAxis = d3.svg.axis().scale(y).orient("left").ticks(5);
var yAxisRight = d3.svg.axis().scale(y1).orient("left").ticks(5);
// Areas are segments of the chart filled with color
var area = d3.svg.area()
.x(function(d) { return x(d.distance); })
.y0(height)
.y1(function(d) { return y(d.elevation); });
// Functions for handling zoom events
var gradientZoomListener = d3.behavior.zoom()
.scaleExtent([1, 10])
.on("zoom", gradientZoomHandler);
function gradientZoomHandler() {
var t = gradientZoomListener.translate(),
tx = t[0],
ty = t[1];
tx = Math.min(tx, 0);
// tx = Math.max(tx, ??);
gradientZoomListener.translate([tx,ty])
gradientChart.select(".x.axis").call(xAxis);
gradientChart.select(".y.axis").call(yAxis);
gradientChart.selectAll('.area').attr('d', area);
}
The most intuitive way to do it in this case is to check what the scale maps the bounds of the input domain to, rather than checking the pixel values. The idea is that the lower bound of the domain should map to 0 (the lower bound of the output range) or less, and the upper bound to the upper bound of the output range or more.
If the lower bound maps to less than 0, the value is to the left of the graph, i.e. the lowest shown value is more than the bound. If it is larger than 0, there must be a gap between the y axis and the first value. Similarly for the upper bound, if it maps to less than the upper bound of the output range, there must be a gap between it and the end of the graph.
In code, this looks as follows.
if(x(xExtent[0]) > 0) {
tx = 0;
} else if(x(xExtent[1]) < x.range()[1]) {
tx -= x(xExtent[1]) - x.range()[1];
}
The only non-trivial thing is the adjusting of the translation value if there's a gap between the largest value and the end of the graph. The size of this gap is the difference between where the largest input value is projected to and the largest output value. The gap is then subtracted from the current translation value to close it.
Complete example here. Note that I've moved some code around to get access to the values which are only known when the data has been read.
It works the same way for the y axis.

Comparing lines on a canvas

I have a canvas on which i'm drawing 4 lines.
I need to then compare the accuracy of those lines drawn against the coordinates of 4 predefined lines.
Can i do this with canvas?
thanks.
As JJPA says, give it a try!
One route you could take would be to store the coordinates of the drawn line and the expected line. Each line forms a vector. You can take the Euclidean distance between the lines using a simple formula. The closer the distance, the more accurate the lines.
I just wrote this.
var expected = {from: {x:10, y:10}, to:{x:20, y:20}}
var identical = {from: {x:10, y:10}, to:{x:20, y:20}}
var close = {from: {x:9, y:9}, to:{x:21, y:21}}
var lessClose = {from: {x:8, y:13}, to:{x:25, y:15}}
var distance = function(a, b) {
return Math.sqrt(Math.pow(a.from.x - b.from.y, 2) +
Math.pow(a.to.x - b.to.y, 2))
}
console.log("identical", distance(expected, identical))
console.log("close", distance(expected, close))
console.log("lessclose", distance(expected, lessClose))
gives
identical 0
close 1.4142135623730951
lessclose 5.830951894845301
If you don't know which way round the lines are, you can swap start and end points and take the minimum distance.
Well, you can get the pixels of your canvas in an rgb-array in javascript:
var pixelarray= canvas.getImageData(x, y, width, height).data;
then pixelarray[0] as integer is the red-value of the first pixel, pixelarray[3] the red-value of the second and so on.
In a canvas of 100px width (pixel-index 0 to 299 in first line), pixelarray[299] would be the blue-value of the last pixel in the first line and pixelarray[300] the red-value of the first in second line.
Then you could get either the functions representing the 4 lines you want to compare to and do something like
function isblack(x,y,width,height){
return pixelarray[(y*width+x)*3]==255&&pixelarray[(y*width+x)*3+1]==255&&pixelarray[(y*width+x)*3+2]==255||x<0||x>width||y<0||y>height;
}
function f(x){return a*x+b;}
var lineblackness=0;
for(var x=0;x<picturewidth;x++)if(isblack(x,f(x),picturewidth,pictureheight))lineblackness++;
if(lineblackness==picturewidth)line_is_drawn();
Where fx(x) would represent the line ...
well, if you use a picture or coordinates for the line, it might get easier:
getcoord(data){
var ar=new array();
for(var i=0;i<data.length/3;i++)if(data[i*3]==255)ar.push(i);
return ar;
}
var comparecoords=getcoord(comparecanvas.getImageData(x, y, width, height).data);
var thisdata=canvas.getImageData(x, y, width, height).data;
var linehits=0;
for(var i=0;i<comparecoords.length;i++){if(thisdata[comparecoords[i]*3]==255)linehits++;}
if(comparecoords.length==linehits)all_coords_are_hit();
Here I only compared the red-value, assuming you have a black&White-image.
If you want to compare lines that aren't exactly fits, you can change the ==255 with a few additional ifs e.g.
function isblack(x,y,width,height,blurr,treshhold){
for(var i=-blurr;i<blurr;i++)if(x-i>0&&x+i<width&&pixelarray[(y*width+(x+i))*3]>=treshhold)return true;
for(var i=-blurr;i<blurr;i++)if(y-i>0&&y+i<height&&pixelarray[((y+i)*width+(x))*3]>=treshhold)return true;
return false;
}
This function uses also only red, but uses a treshhold to let also gray be valid. it also accepts a blurr-value for checking in x-direction before/after the pixel and then again in y-direction.
You could also combine the two loops for checking in an rectangle around the pixel.
Changing the return true into return i will also give you the distance from the pixel you want to check. if you combine the two loops into an i and a j-value, return sqrt(i^2+j^2) and sum all checks up, you can get an indicator how accurate your line is.
To implement sqareroot-error, only comparing the y-distance and summing up the squared distance i^2 should be enough.
Please note that these programs are suggestions, better implementations may exists and my codes could still contain errors.

Categories

Resources