libgeometry: simplify rframes

i got rid of redundant code, and added routines
to get the xform matrix out of an rframe, so it
can be stored and used separately or as part of
a composition of xforms.

also replaced the big example in the man page for
more concise, useful ones.
This commit is contained in:
rodri 2024-05-30 21:11:33 +00:00
parent 33572bd895
commit 9bbbc13ce2
3 changed files with 99 additions and 207 deletions

View file

@ -124,6 +124,8 @@ Quaternion slerp(Quaternion, Quaternion, double);
Point3 qrotate(Point3, Point3, double);
/* RFrame */
void rframematrix(Matrix, RFrame);
void rframematrix3(Matrix3, RFrame3);
Point2 rframexform(Point2, RFrame);
Point3 rframexform3(Point3, RFrame3);
Point2 invrframexform(Point2, RFrame);

View file

@ -1,6 +1,6 @@
.TH GEOMETRY 2
.SH NAME
Flerp, fberp, fclamp, Pt2, Vec2, addpt2, subpt2, mulpt2, divpt2, lerp2, berp2, dotvec2, vec2len, normvec2, edgeptcmp, ptinpoly, Pt3, Vec3, addpt3, subpt3, mulpt3, divpt3, lerp3, berp3, dotvec3, crossvec3, vec3len, normvec3, identity, addm, subm, mulm, smulm, transposem, detm, tracem, minorm, cofactorm, adjm, invm, xform, identity3, addm3, subm3, mulm3, smulm3, transposem3, detm3, tracem3, minorm3, cofactorm3, adjm3, invm3, xform3, Quat, Quatvec, addq, subq, mulq, smulq, sdivq, dotq, invq, qlen, normq, slerp, qrotate, rframexform, rframexform3, invrframexform, invrframexform3, centroid, barycoords, centroid3, vfmt, Vfmt, GEOMfmtinstall \- computational geometry library
Flerp, fberp, fclamp, Pt2, Vec2, addpt2, subpt2, mulpt2, divpt2, lerp2, berp2, dotvec2, vec2len, normvec2, edgeptcmp, ptinpoly, Pt3, Vec3, addpt3, subpt3, mulpt3, divpt3, lerp3, berp3, dotvec3, crossvec3, vec3len, normvec3, identity, addm, subm, mulm, smulm, transposem, detm, tracem, minorm, cofactorm, adjm, invm, xform, identity3, addm3, subm3, mulm3, smulm3, transposem3, detm3, tracem3, minorm3, cofactorm3, adjm3, invm3, xform3, Quat, Quatvec, addq, subq, mulq, smulq, sdivq, dotq, invq, qlen, normq, slerp, qrotate, rframematrix, rframematrix3, rframexform, rframexform3, invrframexform, invrframexform3, centroid, barycoords, centroid3, vfmt, Vfmt, GEOMfmtinstall \- computational geometry library
.SH SYNOPSIS
.de PB
.PP
@ -135,6 +135,8 @@ Quaternion slerp(Quaternion q, Quaternion r, double t);
Point3 qrotate(Point3 p, Point3 axis, double θ);
.PB
/* RFrame */
void rframematrix(Matrix, RFrame);
void rframematrix3(Matrix3, RFrame3);
Point2 rframexform(Point2 p, RFrame rf);
Point3 rframexform3(Point3 p, RFrame3 rf);
Point2 invrframexform(Point2 p, RFrame rf);
@ -615,7 +617,7 @@ by
.I θ
radians.
.SS Frames of reference
A frame of reference in a
A frame of reference (or rframe) in a
.IR n -dimensional
space is made out of n+1 points, one being the origin
.IR p ,
@ -623,33 +625,33 @@ and the remaining being the
basis vectors
.I b1,⋯,bn
that define the metric within that frame.
.PP
The origin point and the bases are all defined in terms of an origin
frame of reference O. Applying a forward transformation
.RI ( rframexform
and
.IR rframexform3 )
to a point relative to O will result in a point relative to the new frame.
Applying an inverse transformation
.RI ( invrframexform
and
.IR invrframexform3 )
to that same point—now defined in terms of the new frame—will bring it back to O.
.TP
Name
Description
.TP
.B rframematrix(\fIm\fP,\fIrf\fP)
Initializes
.I m
as an rframe transformation matrix based on
.IR rf .
.TP
.B rframematrix3(\fIm\fP,\fIrf\fP)
Initializes
.I m
as an rframe transformation matrix based on
.IR rf .
.TP
.B rframexform(\fIp\fP,\fIrf\fP)
Transforms the point
.IR p ,
relative to some origin frame of reference O, into the frame of reference
relative to some origin frame of reference O, into the rframe
.IR rf .
It then returns the new 2D point.
.TP
.B rframexform3(\fIp\fP,\fIrf\fP)
Transforms the point
.IR p ,
relative to some origin frame of reference O, into the frame of reference
relative to some origin frame of reference O, into the rframe
.IR rf .
It then returns the new 3D point.
.TP
@ -658,7 +660,7 @@ Transforms the point
.IR p ,
relative to
.IR rf ,
into a point relative to the origin frame of reference O.
into a point relative to the origin rframe O.
It then returns the new 2D point.
.TP
.B invrframexform3(\fIp\fP,\fIrf\fP)
@ -666,7 +668,7 @@ Transforms the point
.IR p ,
relative to
.IR rf ,
into a point relative to the origin frame of reference O.
into a point relative to the origin rframe O.
It then returns the new 3D point.
.SS Triangles
.TP
@ -689,7 +691,53 @@ relative to the triangle
Returns the geometric center of
.B Triangle3
.IR t .
.SH EXAMPLE
.SH EXAMPLES
.PP
Rotate a point p by θ, scale it 2x, then translate it by vector v:
.IP
.EX
Matrix R = {
cos(θ), -sin(θ), 0,
sin(θ), cos(θ), 0,
0, 0, 1,
}, S = {
2, 0, 0,
0, 2, 0,
0, 0, 1,
}, T = {
1, 0, v.x,
0, 1, v.y,
0, 0, 1,
};
mulm(S, R);
mulm(T, S);
p = xform(p, T); /* p' = T·S·R·p */
.EE
.PP
Given a space with two observers, A and B, and a point p relative to
A, find its location relative to B:
.IP
.EX
pb = rframexform(invrframexform(p, A), B);
.EE
.PP
Now let's say observer C is located relative to A; find the point's
location in C:
.IP
.EX
pc = rframexform(p, C);
.EE
.PP
Finally, to obtain its location relative to the space itself (its
global position):
.IP
.EX
pg = invrframexform(p, A);
.EE
.PP
The following is a common example of an
.B RFrame
being used to define the coordinate system of a
@ -701,13 +749,8 @@ pointing upwards, to contrast with the window system where
.IR y -values
grow downwards (see
.IR graphics (2)).
.PP
.IP
.EX
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <geometry.h>
RFrame worldrf;
/* from screen... */
@ -733,112 +776,6 @@ main(void)
worldrf.by = Vec2(0,-1);
.EE
.PP
The following snippet shows how to use the
.B RFrame
declared earlier to locate and draw a ship based on its orientation,
for which we use matrix translation
.B T
and rotation
.BR R
transformations.
.PP
.EX
typedef struct Ship Ship;
typedef struct Shipmdl Shipmdl;
struct Ship
{
RFrame;
double θ; /* orientation (yaw) */
Shipmdl mdl;
};
struct Shipmdl
{
Point2 pts[3]; /* a free-form triangle */
};
Ship *ship;
void
redraw(void)
{
int i;
Point pts[3+1];
Point2 *p;
Matrix T = {
1, 0, ship->p.x,
0, 1, ship->p.y,
0, 0, 1,
}, R = {
cos(ship->θ), -sin(ship->θ), 0,
sin(ship->θ), cos(ship->θ), 0,
0, 0, 1,
};
mulm(T, R); /* rotate, then translate */
p = ship->mdl.pts;
for(i = 0; i < nelem(pts)-1; i++)
pts[i] = fromworld(xform(p[i], T));
pts[i] = pts[0];
draw(screen, screen->r, display->white, nil, ZP);
poly(screen, pts, nelem(pts), 0, 0, 0, display->black, ZP);
}
main(void)
ship = malloc(sizeof(Ship));
ship->p = Pt2(0,0,1); /* place it at the origin */
ship->θ = 45*DEG; /* counter-clockwise */
ship->mdl.pts[0] = Pt2( 10, 0,1);
ship->mdl.pts[1] = Pt2(-10, 5,1);
ship->mdl.pts[2] = Pt2(-10,-5,1);
redraw();
.EE
.PP
Notice how we could've used the
.B RFrame
embedded in the
.B ship
to transform the
.B Shipmdl
into the window. Instead of applying the matrices to every point, the
ship's local frame of reference can be rotated, effectively changing
the model coordinates after an
.IR invrframexform .
We are also getting rid of the
.B θ
variable, since it's no longer needed.
.PP
.EX
struct Ship
{
RFrame;
Shipmdl mdl;
};
redraw(void)
pts[i] = fromworld(invrframexform(p[i], *ship));
main(void)
Matrix R = {
cos(45*DEG), -sin(45*DEG), 0,
sin(45*DEG), cos(45*DEG), 0,
0, 0, 1,
};
//ship->θ = 45*DEG; /* counter-clockwise */
ship->bx = xform(ship->bx, R);
ship->by = xform(ship->by, R);
.EE
.SH SOURCE
.B /sys/src/libgeometry
.SH SEE ALSO
@ -857,6 +794,8 @@ Jonathan Blow,
The Inner Product, April 2004.
.br
https://www.3dgep.com/understanding-quaternions/
.br
https://motion.cs.illinois.edu/RoboticSystems/CoordinateTransformations.html
.SH BUGS
No care is taken to avoid numeric overflows.
.SH HISTORY

View file

@ -2,106 +2,57 @@
#include <libc.h>
#include <geometry.h>
/*
* implicit identity origin rframes
*
* static RFrame IRF2 = {
* .p = {0,0,1},
* .bx = {1,0,0},
* .by = {0,1,0},
* };
*
* static RFrame3 IRF3 = {
* .p = {0,0,0,1},
* .bx = {1,0,0,0},
* .by = {0,1,0,0},
* .bz = {0,0,1,0},
* };
*
* these rframes are used on every xform to keep the points in the
* correct plane (i.e. with proper w values); they are written here as a
* reference for future changes. the bases are ignored since they turn
* into an unnecessary identity xform.
*
* the implicitness comes from the fact that using the _irf* filters
* makes the rframexform equivalent to:
* rframexform(invrframexform(p, IRF), rf);
* and the invrframexform to:
* rframexform(invrframexform(p, rf), IRF);
*/
static Point2
_irfxform(Point2 p)
void
rframematrix(Matrix m, RFrame rf)
{
p.w--;
return p;
m[0][0] = rf.bx.x; m[0][1] = rf.by.x; m[0][2] = rf.p.x;
m[1][0] = rf.bx.y; m[1][1] = rf.by.y; m[1][2] = rf.p.y;
m[2][0] = 0; m[2][1] = 0; m[2][2] = 1;
}
static Point2
_irfxform¹(Point2 p)
void
rframematrix3(Matrix3 m, RFrame3 rf)
{
p.w++;
return p;
}
static Point3
_irfxform3(Point3 p)
{
p.w--;
return p;
}
static Point3
_irfxform3¹(Point3 p)
{
p.w++;
return p;
m[0][0] = rf.bx.x; m[0][1] = rf.by.x; m[0][2] = rf.bz.x; m[0][3] = rf.p.x;
m[1][0] = rf.bx.y; m[1][1] = rf.by.y; m[1][2] = rf.bz.y; m[1][3] = rf.p.y;
m[2][0] = rf.bx.z; m[2][1] = rf.by.z; m[2][2] = rf.bz.z; m[2][3] = rf.p.z;
m[3][0] = 0; m[3][1] = 0; m[3][2] = 0; m[3][3] = 1;
}
Point2
rframexform(Point2 p, RFrame rf)
{
Matrix m = {
rf.bx.x, rf.by.x, 0,
rf.bx.y, rf.by.y, 0,
0, 0, 1,
};
Matrix m;
rframematrix(m, rf);
invm(m);
return xform(subpt2(_irfxform¹(p), rf.p), m);
return xform(p, m);
}
Point3
rframexform3(Point3 p, RFrame3 rf)
{
Matrix3 m = {
rf.bx.x, rf.by.x, rf.bz.x, 0,
rf.bx.y, rf.by.y, rf.bz.y, 0,
rf.bx.z, rf.by.z, rf.bz.z, 0,
0, 0, 0, 1,
};
Matrix3 m;
rframematrix3(m, rf);
invm3(m);
return xform3(subpt3(_irfxform3¹(p), rf.p), m);
return xform3(p, m);
}
Point2
invrframexform(Point2 p, RFrame rf)
{
Matrix m = {
rf.bx.x, rf.by.x, 0,
rf.bx.y, rf.by.y, 0,
0, 0, 1,
};
return _irfxform(addpt2(xform(p, m), rf.p));
Matrix m;
rframematrix(m, rf);
return xform(p, m);
}
Point3
invrframexform3(Point3 p, RFrame3 rf)
{
Matrix3 m = {
rf.bx.x, rf.by.x, rf.bz.x, 0,
rf.bx.y, rf.by.y, rf.bz.y, 0,
rf.bx.z, rf.by.z, rf.bz.z, 0,
0, 0, 0, 1,
};
return _irfxform3(addpt3(xform3(p, m), rf.p));
Matrix3 m;
rframematrix3(m, rf);
return xform3(p, m);
}