mirror of
git://git.9front.org/plan9front/plan9front
synced 2025-01-12 11:10:06 +00:00
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:
parent
33572bd895
commit
9bbbc13ce2
3 changed files with 99 additions and 207 deletions
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue