Spline offset in three.js - javascript

I have to make 3d text from font glyphs. Yes, I know that I can use TextGeometry, but I need to draw this manually because I need to do offset on font splines.
At this moment I have splines with their points and I can draw letters.
From points I know: previousPoint, currentPoint and nextPoint and I need to compute bisector between previous and next points and I have no idea to do that.
Or if is another way to move spline points outer of initial position to do offset.
My idea:
Thank you!
EDIT:
With yours answers I obtained correct values for each splines from font, but only at 'o' and '0' I have a problem.
This method draw a weird tangent in bottom of the letter and I don't know to resolve this problem..
here is the result
Anybody know how to resolve this?
EDIT 2:
Finally I finished my project. And this is the final product ( .stl exporter )
final offset
Thank you for yours answers!

There are the result from: x = (prev_x + next_x) / 2 and y = (prev_y + next_y) / 2
wrong result
desired result
Here is my code where let points is all the points from the path:
getPathPoints(path) {
let points = path.getPoints();
console.log(points)
for (let i = 0; i < points.length; i++) {
let A = points.extended(i - 1); // previousPoint => where extends is a custom array prototype
let B = points.extended(i); // currentPoint
let C = points.extended(i + 1); // nextPoint
let x = (A.x + C.x) / 2;
let y = (A.y + C.y) / 2;
let bisector = new THREE.Vector2(x,y);
console.log(bisector);
}
}

What splines describe your glyphs?
I know that TTF fonts use quadratic Bezier curves. For Bezier direction vector in starting and ending points has direction onto control point. So difference
S = ControlPoint[1] - ControlPoint[0]
represents direction in the starting point, difference
E = ControlPoint[1] - ControlPoint[2]
represents direction in the ending point.
Normalize these vectors for two neighbour curves and add them - now you have bisector vector.
Bisector = E(i).Normalized + S(i+1).Normalized

Related

How to get the intersection points of an Arc and a line?

I need to place several number of line segments inside an arc, but to do that i need to have intersection points so that i can place the lines inside the arc perfectly;
I thought about a way to calculate the distance and check if it is less than radius, but the thing is i need to know the C,D & E points so that i can place the line segments, so i'm lost here, any one can help please?
EDIT
The radius is specified
Number of line segments may vary, but there are 2 lines at least
Lines start at starting border, end at the ending border; e.g: Start is C, end point is D
EDIT
In order to be clear about what i'm trying to do, i'm uploading another illustration;
I need to get the coordinates of [CD],[EI],[JK] lines,
Ok... Here we go. The following snippet should work for any arc (defined with an angle and a radius) and for any number of equally spaced segments you want.
Currently, it assumes that the arc is perfectly placed horizontally (like in your example), but it can be "easily" extended to allow translated/rotated arcs.
The getLinesCoords() function will return an object whose x and y properties contains arrays with the corresponding coordinates for each segment.
"y" coordinates are the "height" of the segment from the center (G in your image) and "x" are the start/end position, always from center (left/right depends on sign +/-).
If you have any question, please ask.
// *** ARC ***
const R = 100; // RADIUS
const PHI = 180; // ANGLE (DEG)
// *** LINES ***
const LINES = 10; // NUMBER OF LINES TO BE PLACED
// *** CALCULATIONS ***
const coords = getLinesCoords(R, PHI, LINES);
console.log(coords);
function getLinesCoords(radius, angle, linesNum) {
let i = 0;
let arcAvailHeight = 0;
let linesSep = 0;
let linesYCoords = [];
let linesXCoords = [];
let angleRad = angle * Math.PI / 180;
// GET AVAILABLE HEIGHT FOR PLACING LINES
arcAvailHeight = radius * (1 - Math.cos(angleRad / 2));
// GET LINES SEPARATION
linesSep = arcAvailHeight / (linesNum + 1);
// GET Y COORDINATES FOR LINES
for (i = 0; i < linesNum; i++) {
linesYCoords[i] = linesSep * (i + 1);
}
// GET CORRESPONDING X COORDINATES FOR LINES
linesYCoords.forEach((y) => {
linesXCoords.push(Math.sqrt(radius**2 - (radius * Math.cos(angleRad / 2) + y)**2));
});
return ({x: linesXCoords, y: linesYCoords});
}

Incorrect angle, wrong side calculated

I need to calculate the angle between 3 points. For this, I do the following:
Grab the 3 points (previous, current and next, it's within a loop)
Calculate the distance between the points with Pythagoras
Calculate the angle using Math.acos
This seems to work fine for shapes without angels of over 180 degrees, however if a shape has such an corner it calculates the short-side. Here's an illustration to show what I mean (the red values are wrong):
This is the code that does the calculations:
// Pythagoras for calculating distance between two points (2D)
pointDistance = function (p1x, p1y, p2x, p2y) {
return Math.sqrt((p1x - p2x)*(p1x - p2x) + (p1y - p2y)*(p1y - p2y));
};
// Get the distance between the previous, current and next points
// vprev, vcur and vnext are objects that look like this:
// { x:float, y:float, z:float }
lcn = pointDistance(vcur.x, vcur.z, vnext.x, vnext.z);
lnp = pointDistance(vnext.x, vnext.z, vprev.x, vprev.z);
lpc = pointDistance(vprev.x, vprev.z, vcur.x, vcur.z);
// Calculate and print the angle
Math.acos((lcn*lcn + lpc*lpc - lnp*lnp)/(2*lcn*lpc))*180/Math.PI
Is there something wrong in the code, did I forget to do something, or should it be done a completely different way?
HI there your math and calculations are perfect. Your running into the same problem most people do on calculators, which is orientation. What I would do is find out if the point lies to the left or right of the vector made by the first two points using this code, which I found from
Determine which side of a line a point lies
isLeft = function(ax,ay,bx,by,cx,cy){
return ((bx - ax)*(cy - ay) - (by - ay)*(cx - ax)) > 0;
}
Where ax and ay make up your first point bx by your second and cx cy your third.
if it is to the left just add 180 to your angle
I've got a working but not necessarily brief example of how this can work:
var point1x = 0, point1y = 0,
point2x = 10, point2y = 10,
point3x = 20, point3y = 10,
point4x = 10, point4y = 20;
var slope1 = Math.atan2(point2y-point1y,point2x-point1x)*180/Math.PI;
var slope2 = Math.atan2(point3y-point2y,point3x-point2x)*180/Math.PI;
var slope3 = Math.atan2(point4y-point3y,point4x-point3x)*180/Math.PI;
alert(slope1);
alert(slope2);
alert(slope3);
var Angle1 = slope1-slope2;
var Angle2 = slope2-slope3;
alert(180-Angle1);
alert(180-Angle2);
(see http://jsfiddle.net/ZUESt/1/)
To explain the multiple steps the slopeN variables are the slopes of the individual line segments. AngleN is the amount turned at each junction (ie point N+1). A positive angle is a right turn and a negative angle a left turn.
You can then subtract this angle from 180 to get the actual interior angle that you want.
It should be noted that this code can of course be compressed and that five lines are merely outputting variables to see what is going on. I'll let you worry about optimizing it for your own use with this being a proof of concept.
You need to check boundary conditions (apparently, if points are colinear) and apply the proper calculation to find the angle.
Also, a triangle can't have any (interior) angle greater than 180 degress. Sum of angle of triangle is 180 degrees.

Connecting dots without crossing lines

I am trying to create a Javascript web application where a user clicks on a canvas to drop an infinite amount of dots. There is solve button, that when clicked draws lines between the dots so that all dots are connected by exactly 2 other dots, and no lines can cross. With my code so far, there are certain instances where the lines still cross, and I can't programmatically figure out logic that will connect all the dots without any lines ever crossing.
So far, I collect all the points (X-Y coordinates) and put them in a JavaScript array of objects. I then need to sort the array so that it is in the correct order to be drawn. Everything works at this point except the order does not always satisfy the requirements.
My Question: Does anyone have any ideas on a set of rules that will order these points (X-Y coordinates) so that they all connect but never cross, that will work in every scenario?
Thanks for your help.
var centroid = get_polygon_centroid($points);
$points = _.sortBy($points, function(p){
var dx = p.coords.x-centroid.x;
var dy = p.coords.y-centroid.y;
return dx*dx + dy*dy;
});
$points = _.sortBy($points, function(p){
var dx = p.coords.x-centroid.x;
var dy = p.coords.y-centroid.y;
return Math.atan2(dy, dx);
});
$points.push($points[0]);
Here's an algorithm:
Find the center of mass ( O(n) time, where n is the number of points)
For each point, compute the angle from the center to that point ( O(n) time). This can be done with Math.atan2(p.y-c.y, p.x-c.x) in JS where p is the current point and c is the center.
Sort by angle ( O(n log n) time). For any angles that are exactly the same, sort by radius next, smallest to largest.
Connect pairs of points ai to ai+1 for every i from 0 to n-1 and also an-1 to a0
This should result in a connected graph where no two lines intersect in O(n log n) time.
Update:
This code should work.
//iterate through all points and calculate the center, c
var c = {x:0, y:0}, p;
for (p : points) {
c.x+=p.coords.x;
c.y+=p.coords.y;
}
c.x/=points.length;
c.y/=points.length;
points.sort(function(p1, p2){
var dx1 = p1.coords.x-c.x;
var dy1 = p1.coords.y-c.y;
var a1 = Math.atan2(dy1, dx1);
var dx2 = p2.coords.x-c.x;
var dy2 = p2.coords.y-c.y;
var a2 = Math.atan2(dy2, dx2);
//If angles are the same, sort by length
if (a1===a2){
var d1 = dx1*dx1 + dy1*dy1;
var d2 = dx2*dx2 + dy2*dy2;
return d1-d2;
}
//otherwise sort by angle
return a1-a2;
}
//Iterate through all Points and draw lines between them
var i;
for (i=0;i<n;i++){
//This is not real code \/
line(p[i], p[(i+1)%n]);
}

Detect if a set of points in an array that are the vertices of a complex polygon were defined in a clockwise or counterclockwise order?

EDIT: I updated the program with the answer and it works great!
I am making a program (feel free to try it out) that lets users draw polygons which it then triangulates. They can click to add vertices and hit enter to triangulate. Anyways, the algorithm works fine as long as I tell it if the points were drawn in a clockwise or counterclockwise fashion (right now I have it set only to work with clockwise polygons). I have been trying to figure this out for days, but have no idea how to determine whether the points are clockwise or counterclockwise. Try drawing shapes with the program mentioned earlier to get a better idea, you can experience what I am talking about better than I can try to explain it.
Here is how the points are defined:
function Point(x, y) {
this.x = x;
this.y = y;
}
var vertices = [];
// Called on click
function addPoint(mouseX, mouseY) {
vertices.push(new Point(mouseX, mouseY));
}
Here is an image of a clockwise polygon:
Here is an image of a counterclockwise polygon:
If you could help me figure out how to determine the "clockwise-ness" of the points, I would be very grateful!
Compute the polygon area using the shoelace formula, but without the absolute value sign. If the result is positive, the points are ordered counterclockwise, and if negative - clockwise.
function polygonArea() {
var area = 0;
for (var i = 0; i < vertices.length; i++) {
j = (i + 1) % vertices.length;
area += vertices[i].x * vertices[j].y;
area -= vertices[j].x * vertices[i].y;
}
return area / 2;
}
var clockwise = polygonArea() > 0;
In case someone is using three.js the ShapeUtils comes with an inbuilt isClockWise method which internally uses the area method to determine the sign of the calculated area.
isClockWise: function ( pts ) {
return ShapeUtils.area( pts ) < 0;
}
The ShapeUtils.isClockWise Method can be found here.
area: function ( contour ) {
var n = contour.length;
var a = 0.0;
for ( var p = n - 1, q = 0; q < n; p = q ++ ) {
a += contour[ p ].x * contour[ q ].y - contour[ q ].x * contour[ p ].y;
}
return a * 0.5;
},
The ShapeUtils.area Method can be found here.
A general idea would be to take a look at the convex hull of your polygone and guess the orientation from there. However, I think that you do not need to build the whole hull to find the orientation, but just one segment belonging to it.
So:
Find two points of your polygones so that all the other points are on one side of this line.
If all the points are on the left (just check one of the points), it's counterclockwise. If they are on the right, it's clockwise.
Example:
On the top figure: 4-5 let the figure on the right, 5-11 let the figure on the right, ...
On the bottom figure: 6-7 let the figure on the left, 7-14 let the figure on the left, ...
Warning: While "walking" on your polygon, do not restart the numeration, otherwise it will be wrong. On the top figure, 4-(n-1) let the figure on the left!
Your intuitive definition of clockwisedness is not well defined. For example, If I draw a horseshoe:
/---a-b--\
/ _d_c_ \
/ / \ \
| | | |
| | | |
\ \ / /
\ \ / /
-- --
If 0 = a < b < b < d and I look at a and b I would conclude from your description that the shape has been drawn clockwise, but if 0 = c < d < a < b I would conclude that the shape has been drawn anticlockwise. Since both of these scenarios involve the same direction in which the points were drawn, just from different starting points, I can only conclude that your definition is lacking.
The horseshoe I drew isn't the best; the idea is that it is almost a circle with just a small hole at the bottom, to allow the other side to be drawn in the opposite direction.
If you are interested in defining things more strictly, then I suggest something along the following lines:
Considering any finite simple polygon as separating the plane into two distinct areas (one finite and one infinite), we can always consider the finite area to be the interior of the polygon. In such a scenario we define a vertex ordering to be clockwise iff the order of the points runs with the exterior along its right-hand side. This is called curve orientation.
Once you have this more solid definition, implementation can be as simple as counting the winding number. Take the midpoint of any ordered pair, say 0 and 1, take a line segment to the right of the ordered pair (at any angle, say perpendicular), and count how many intersections it has with other line segments: The curve is clockwise iff the number is odd.
This is simple to implement, linear in time O(n), and adds constant space O(1).
This a function function that specialized for OpenLayers. As You Can See The Condition Of Clockwise Polygon Is area<0 This Reference Confirm It.
function IsClockwise(feature)
{
if(feature.geometry==null)return -1;
var vertices=feature.geometry.getVertices();
var area=0;
for (var i = 0; i < (vertices.length); i++)
{
j = (i + 1) % vertices.length;
area += vertices[i].x * vertices[j].y;
area -= vertices[j].x * vertices[i].y;
// console.log(area);
}
return (area < 0);
}

How can I generate random points on a circles circumference in javascript

I am trying to write a function that will randomly return an (x,y) co-ordinates around a given circumference
so if I have a point that's at (0,0) (being the center of the div) how can I write a function that randomly places other entities that appear among the outer edge of a circle.
All I need is the equation i know it has something to do with getting the distance from the center to the circumference edge just no idea how to calculate it and randomize it so it looks good.
Just get a random angle:
var angle = Math.random()*Math.PI*2;
Then
x = Math.cos(angle)*radius;
y = Math.sin(angle)*radius;
Done.
You can also avoid computing sin and cos via this formula:
// Generate 2 random numbers in the [-1, 1] interval
const u = Math.random()*2 - 1;
const v = Math.random()*2 - 1;
const u2 = u*u;
const v2 = v*v;
const r = u2 + v2;
if (r <= 1) {
x = (u2 - v2)/r;
y = (2*u*v)/r;
}
if r > 1 you need to re-try, but the expected number of tries until you get a valid point is ~1.27. So this algorithm is very efficient and avoids the complex trigonometric functions.
Visualization: https://observablehq.com/#kunigami/circles-and-randomness

Categories

Resources