Math-3Space
view release on metacpan or search on metacpan
if ((field= hv_fetch(attrs, "yv", 2, 0)) && *field && SvOK(*field))
m3s_read_vector_from_sv(SPACE_YV(space), *field, NULL, NULL);
else
SPACE_YV(space)[1]= 1;
if ((field= hv_fetch(attrs, "zv", 2, 0)) && *field && SvOK(*field))
m3s_read_vector_from_sv(SPACE_ZV(space), *field, NULL, NULL);
else
SPACE_ZV(space)[2]= 1;
if ((field= hv_fetch(attrs, "origin", 6, 0)) && *field && SvOK(*field))
m3s_read_vector_from_sv(SPACE_ORIGIN(space), *field, NULL, NULL);
space->is_normal= -1;
} else
croak("Invalid source for _init");
}
SV*
clone(obj)
SV *obj
INIT:
m3s_space_t *space= m3s_get_magic_space(obj, OR_DIE), *space2;
HV *clone_hv;
CODE:
if (SvTYPE(SvRV(obj)) != SVt_PVHV)
croak("Invalid source object"); // just to be really sure before next line
clone_hv= newHVhv((HV*)SvRV(obj));
RETVAL= newRV_noinc((SV*)clone_hv);
sv_bless(RETVAL, gv_stashpv(sv_reftype(SvRV(obj), 1), GV_ADD));
space2= m3s_get_magic_space(RETVAL, AUTOCREATE);
memcpy(space2, space, sizeof(*space2));
OUTPUT:
RETVAL
SV*
space(parent=NULL)
SV *parent
INIT:
m3s_space_t *space;
CODE:
if (parent && SvOK(parent) && !m3s_get_magic_space(parent, 0))
croak("Invalid parent, must be instance of Math::3Space");
Newx(space, 1, m3s_space_t);
m3s_space_init(space);
RETVAL= m3s_wrap_space(space);
if (parent && SvOK(parent))
hv_store((HV*)SvRV(RETVAL), "parent", 6, newSVsv(parent), 0);
OUTPUT:
RETVAL
void
xv(space, x_or_vec=NULL, y=NULL, z=NULL)
m3s_space_t *space
SV *x_or_vec
SV *y
SV *z
ALIAS:
Math::3Space::yv = 1
Math::3Space::zv = 2
Math::3Space::origin = 3
INIT:
NV *vec= space->mat + ix * 3;
PPCODE:
if (x_or_vec) {
M3S_VECLOAD(vec,x_or_vec,y,z,0);
if (ix < 3) space->is_normal= -1;
// leave $self on stack as return value
} else {
ST(0)= sv_2mortal(m3s_wrap_vector(vec));
}
XSRETURN(1);
bool
is_normal(space)
m3s_space_t *space
CODE:
if (space->is_normal == -1)
m3s_space_check_normal(space);
RETVAL= space->is_normal;
OUTPUT:
RETVAL
int
parent_count(space)
SV *space
CODE:
m3s_space_recache_parent(space);
RETVAL= m3s_get_magic_space(space, OR_DIE)->n_parents;
OUTPUT:
RETVAL
void
reparent(space, parent)
SV *space
SV *parent
INIT:
m3s_space_t *sp3= m3s_get_magic_space(space, OR_DIE), *psp3=NULL, *cur;
PPCODE:
m3s_space_recache_parent(space);
if (SvOK(parent)) {
psp3= m3s_get_magic_space(parent, OR_DIE);
m3s_space_recache_parent(parent);
// Make sure this doesn't create a cycle
for (cur= psp3; cur; cur= cur->parent)
if (cur == sp3)
croak("Attempt to create a cycle: new 'parent' is a child of this space");
}
m3s_space_reparent(sp3, psp3);
hv_store((HV*) SvRV(space), "parent", 6, newSVsv(parent), 0);
XSRETURN(1);
void
translate(space, x_or_vec, y=NULL, z=NULL)
m3s_space_t *space
SV *x_or_vec
SV *y
SV *z
ALIAS:
Math::3Space::tr = 0
Math::3Space::travel = 1
Math::3Space::go = 1
INIT:
NV vec[3], *matp;
PPCODE:
M3S_VECLOAD(vec,x_or_vec,y,z,0);
if (ix) {
matp= space->mat;
matp[9] += vec[0] * matp[0] + vec[1] * matp[3] + vec[2] * matp[6];
++matp;
matp[9] += vec[0] * matp[0] + vec[1] * matp[3] + vec[2] * matp[6];
++matp;
matp[9] += vec[0] * matp[0] + vec[1] * matp[3] + vec[2] * matp[6];
} else {
matp= SPACE_ORIGIN(space);
*matp++ += vec[0];
*matp++ += vec[1];
*matp++ += vec[2];
}
XSRETURN(1);
void
scale(space, xscale_or_vec, yscale=NULL, zscale=NULL)
m3s_space_t *space
SV *xscale_or_vec
SV *yscale
SV *zscale
ALIAS:
Math::3Space::set_scale = 1
INIT:
NV vec[3], s, m, *matp= SPACE_XV(space);
size_t i;
PPCODE:
if (SvROK(xscale_or_vec) && yscale == NULL) {
m3s_read_vector_from_sv(vec, xscale_or_vec, NULL, NULL);
} else {
vec[0]= SvNV(xscale_or_vec);
vec[1]= yscale? SvNV(yscale) : vec[0];
vec[2]= zscale? SvNV(zscale) : vec[0];
}
for (i= 0; i < 3; i++) {
s= vec[i];
if (ix == 1) {
m= sqrt(m3s_vector_dotprod(matp,matp));
if (m > 0)
s /= m;
else
warn("can't scale magnitude=0 vector");
}
*matp++ *= s;
*matp++ *= s;
*matp++ *= s;
}
space->is_normal= -1;
XSRETURN(1);
void
rotate(space, angle, x_or_vec, y=NULL, z=NULL)
m3s_space_t *space
NV angle
SV *x_or_vec
SV *y
SV *z
INIT:
m3s_vector_t vec;
ALIAS:
Math::3Space::rot = 0
PPCODE:
if (y) {
if (!z) croak("Missing z coordinate in space->rotate(angle, x, y, z)");
vec[0]= SvNV(x_or_vec);
vec[1]= SvNV(y);
vec[2]= SvNV(z);
} else {
m3s_read_vector_from_sv(vec, x_or_vec, NULL, NULL);
}
m3s_space_rotate(space, sin(angle * 2 * M_PI), cos(angle * 2 * M_PI), vec);
// return $self
XSRETURN(1);
void
rot_x(space, angle)
m3s_space_t *space
NV angle
ALIAS:
Math::3Space::rot_y = 1
Math::3Space::rot_z = 2
Math::3Space::rot_xv = 3
Math::3Space::rot_yv = 4
Math::3Space::rot_zv = 5
INIT:
NV *matp, tmp1, tmp2;
size_t ofs1, ofs2;
NV s= sin(angle * 2 * M_PI), c= cos(angle * 2 * M_PI);
PPCODE:
if (ix < 3) // Rotate around axis of parent
m2s_space_parent_axis_rotate(space, s, c, ix);
else
m3s_space_self_axis_rotate(space, s, c, ix - 3);
XSRETURN(1);
void
project_vector(space, ...)
m3s_space_t *space
INIT:
m3s_vector_t vec;
int i, vectype, count;
AV *vec_av;
HV *vec_hv;
SV *pdl_origin= NULL, *pdl_matrix= NULL;
size_t pdl_dims[3];
ALIAS:
Math::3Space::project = 1
Math::3Space::unproject_vector = 2
Math::3Space::unproject = 3
PPCODE:
for (i= 1; i < items; i++) {
vectype= m3s_read_vector_from_sv(vec, ST(i), pdl_dims, NULL);
if (vectype == M3S_VECTYPE_PDLMULTI) {
dSP;
if (!pdl_origin) {
pdl_origin= sv_2mortal(m3s_pdl_vector(SPACE_ORIGIN(space), 3));
pdl_matrix= sv_2mortal(m3s_pdl_matrix(SPACE_XV(space), !(ix&2) /*transpose bool*/));
}
ENTER;
SAVETMPS;
PUSHMARK(SP);
// first, clone the input. Then modify it in place.
EXTEND(SP, 4);
PUSHs(ST(i));
PUTBACK;
count= call_method("copy", G_SCALAR);
SPAGAIN;
if (count != 1) croak("PDL->copy failed?");
// project point subtracts origin
PUSHs(ix == 3? pdl_origin : &PL_sv_undef);
PUSHs(pdl_matrix);
// unproject point adds origin
PUSHs(ix == 1? pdl_origin : &PL_sv_undef);
PUTBACK;
count= call_pv("Math::3Space::_pdl_project_inplace", G_DISCARD);
FREETMPS;
LEAVE;
ST(i-1)= ST(i);
}
else {
switch (ix) {
case 0: m3s_space_project_vector(space, vec); break;
case 1: m3s_space_project_point(space, vec); break;
case 2: m3s_space_unproject_vector(space, vec); break;
default: m3s_space_unproject_point(space, vec);
}
switch (vectype) {
case M3S_VECTYPE_ARRAY:
vec_av= newAV();
av_extend(vec_av, 2);
av_push(vec_av, newSVnv(vec[0]));
av_push(vec_av, newSVnv(vec[1]));
av_push(vec_av, newSVnv(vec[2]));
ST(i-1)= sv_2mortal(newRV_noinc((SV*)vec_av));
break;
case M3S_VECTYPE_HASH:
vec_hv= newHV();
hv_stores(vec_hv, "x", newSVnv(vec[0]));
hv_stores(vec_hv, "y", newSVnv(vec[1]));
hv_stores(vec_hv, "z", newSVnv(vec[2]));
ST(i-1)= sv_2mortal(newRV_noinc((SV*)vec_hv));
break;
case M3S_VECTYPE_PDL:
ST(i-1)= sv_2mortal(m3s_pdl_vector(vec, pdl_dims[0]));
break;
default:
ST(i-1)= sv_2mortal(m3s_wrap_vector(vec));
}
}
}
XSRETURN(items-1);
void
project_vector_inplace(space, ...)
m3s_space_t *space
INIT:
m3s_vector_t vec;
m3s_vector_p vecp;
size_t i, j, n;
int vectype;
AV *vec_av;
SV **item, *x, *y, *z, *pdl_origin= NULL, *pdl_matrix= NULL;
size_t pdl_dims[3];
SV *component_sv[3];
ALIAS:
Math::3Space::project_inplace = 1
Math::3Space::unproject_vector_inplace = 2
Math::3Space::unproject_inplace = 3
PPCODE:
for (i= 1; i < items; i++) {
if (!SvROK(ST(i)))
croak("Expected vector at $_[%d]", (int)(i-1));
vectype= m3s_read_vector_from_sv(vec, ST(i), pdl_dims, component_sv);
switch (vectype) {
case M3S_VECTYPE_VECOBJ:
vecp= m3s_vector_get_array(ST(i));
switch (ix) {
case 0: m3s_space_project_vector(space, vecp); break;
case 1: m3s_space_project_point(space, vecp); break;
case 2: m3s_space_unproject_vector(space, vecp); break;
default: m3s_space_unproject_point(space, vecp);
}
break;
case M3S_VECTYPE_ARRAY:
case M3S_VECTYPE_HASH:
switch (ix) {
case 0: m3s_space_project_vector(space, vec); break;
case 1: m3s_space_project_point(space, vec); break;
case 2: m3s_space_unproject_vector(space, vec); break;
default: m3s_space_unproject_point(space, vec);
}
for (j=0; j < 3; j++)
if (component_sv[j])
sv_setnv(component_sv[j], vec[j]);
break;
case M3S_VECTYPE_PDL:
case M3S_VECTYPE_PDLMULTI:
{
int count;
dSP;
if (!pdl_origin) {
pdl_origin= sv_2mortal(m3s_pdl_vector(SPACE_ORIGIN(space), 3));
pdl_matrix= sv_2mortal(m3s_pdl_matrix(SPACE_XV(space), !(ix&2) /*transpose bool*/));
}
ENTER;
SAVETMPS;
PUSHMARK(SP);
EXTEND(SP, 4);
PUSHs(ST(i));
// project point subtracts origin
PUSHs(ix == 3? pdl_origin : &PL_sv_undef);
PUSHs(pdl_matrix);
// unproject point adds origin
PUSHs(ix == 1? pdl_origin : &PL_sv_undef);
PUTBACK;
count= call_pv("Math::3Space::_pdl_project_inplace", G_DISCARD);
FREETMPS;
LEAVE;
}
break;
default:
croak("bug: unhandled vec type");
}
}
// return $self
XSRETURN(1);
void
get_gl_matrix(space, buffer=NULL)
m3s_space_t *space
SV *buffer
INIT:
NV *src;
double *dst;
PPCODE:
if (buffer) {
dst= (double*) m3s_make_aligned_buffer(buffer, sizeof(double)*16);
src= space->mat;
dst[ 0] = src[ 0]; dst[ 1] = src[ 1]; dst[ 2] = src[ 2]; dst[ 3] = 0;
dst[ 4] = src[ 3]; dst[ 5] = src[ 4]; dst[ 6] = src[ 5]; dst[ 7] = 0;
dst[ 8] = src[ 6]; dst[ 9] = src[ 7]; dst[10] = src[ 8]; dst[11] = 0;
dst[12] = src[ 9]; dst[13] = src[10]; dst[14] = src[11]; dst[15] = 1;
XSRETURN(0);
} else {
EXTEND(SP, 16);
mPUSHn(SPACE_XV(space)[0]); mPUSHn(SPACE_XV(space)[1]); mPUSHn(SPACE_XV(space)[2]); mPUSHn(0);
mPUSHn(SPACE_YV(space)[0]); mPUSHn(SPACE_YV(space)[1]); mPUSHn(SPACE_YV(space)[2]); mPUSHn(0);
mPUSHn(SPACE_ZV(space)[0]); mPUSHn(SPACE_ZV(space)[1]); mPUSHn(SPACE_ZV(space)[2]); mPUSHn(0);
mPUSHn(SPACE_ORIGIN(space)[0]); mPUSHn(SPACE_ORIGIN(space)[1]); mPUSHn(SPACE_ORIGIN(space)[2]); mPUSHn(1);
XSRETURN(16);
}
#**********************************************************************************************
# Math::3Space::Projection
#**********************************************************************************************
MODULE = Math::3Space PACKAGE = Math::3Space::Projection
SV *
_frustum(left, right, bottom, top, near_z, far_z)
double left
double right
double bottom
double top
double near_z
double far_z
INIT:
m3s_4space_projection_t proj;
double w, h, d, w_1, h_1, d_1;
CODE:
w= right - left;
h= top - bottom;
d= far_z - near_z;
if (fabs(w) < double_tolerance || fabs(h) < double_tolerance || fabs(d) < double_tolerance)
croak("Described frustum has a zero-sized dimension");
w_1= 1/w;
h_1= 1/h;
d_1= 1/d;
proj.frustum.m00= near_z * 2 * w_1;
proj.frustum.m11= near_z * 2 * h_1;
proj.frustum.m20= (right+left) * w_1;
proj.frustum.m21= (top+bottom) * h_1;
proj.frustum.m22= -(near_z+far_z) * d_1;
proj.frustum.m32= -2 * near_z * far_z * d_1;
// use optimized version if m20 and m21 are zero
proj.frustum.centered= fabs(proj.frustum.m20) < double_tolerance && fabs(proj.frustum.m21) < double_tolerance;
RETVAL= m3s_wrap_projection(&proj,
"Math::3Space::Projection::Frustum"
);
OUTPUT:
RETVAL
SV *
_perspective(vertical_field_of_view, aspect, near_z, far_z)
double vertical_field_of_view
double aspect
double near_z
double far_z
INIT:
m3s_4space_projection_t proj;
double f= tan(M_PI_2 - vertical_field_of_view * M_PI),
neg_inv_range_z= -1 / (far_z - near_z);
CODE:
proj.frustum.m00= f / aspect;
proj.frustum.m11= f;
proj.frustum.m20= 0;
proj.frustum.m21= 0;
proj.frustum.m22= (near_z+far_z) * neg_inv_range_z;
proj.frustum.m32= 2 * near_z * far_z * neg_inv_range_z;
proj.frustum.centered= true;
RETVAL= m3s_wrap_projection(&proj, "Math::3Space::Projection::Frustum");
OUTPUT:
RETVAL
MODULE = Math::3Space PACKAGE = Math::3Space::Projection::Frustum
# This is an optimized matrix multiplication taking advantage of all the
# zeroes and ones in both the 3Space matrix and the projection matrix.
void
matrix_colmajor(proj, space=NULL)
m3s_4space_projection_t *proj
m3s_space_t *space
ALIAS:
get_gl_matrix = 0
matrix_pack_float = 1
matrix_pack_double = 2
INIT:
double dst[16];
struct m3s_4space_frustum_projection *f= &proj->frustum;
PPCODE:
if (!space) { /* user just wants the matrix itself */
dst[ 0]= f->m00; dst[ 4]= 0; dst[ 8]= f->m20; dst[12]= 0;
dst[ 1]= 0; dst[ 5]= f->m11; dst[ 9]= f->m21; dst[13]= 0;
dst[ 2]= 0; dst[ 6]= 0; dst[10]= f->m22; dst[14]= f->m32;
dst[ 3]= 0; dst[ 7]= 0; dst[11]= -1; dst[15]= 0;
}
else if (proj->frustum.centered) { /* centered frustum, optimize by assuming m20 and m21 are zero */
dst[ 0]= f->m00 * SPACE_XV(space)[0];
dst[ 1]= f->m11 * SPACE_XV(space)[1];
dst[ 2]= f->m22 * SPACE_XV(space)[2];
dst[ 3]= -SPACE_XV(space)[2];
dst[ 4]= f->m00 * SPACE_YV(space)[0];
dst[ 5]= f->m11 * SPACE_YV(space)[1];
dst[ 6]= f->m22 * SPACE_YV(space)[2];
dst[ 7]= -SPACE_YV(space)[2];
dst[ 8]= f->m00 * SPACE_ZV(space)[0];
dst[ 9]= f->m11 * SPACE_ZV(space)[1];
dst[10]= f->m22 * SPACE_ZV(space)[2];
dst[11]= -SPACE_ZV(space)[2];
dst[12]= f->m00 * SPACE_ORIGIN(space)[0];
dst[13]= f->m11 * SPACE_ORIGIN(space)[1];
dst[14]= f->m22 * SPACE_ORIGIN(space)[2] + f->m32;
dst[15]= -SPACE_ORIGIN(space)[2];
} else {
dst[ 0]= f->m00 * SPACE_XV(space)[0] + f->m20 * SPACE_XV(space)[2];
dst[ 1]= f->m11 * SPACE_XV(space)[1] + f->m21 * SPACE_XV(space)[2];
dst[ 2]= f->m22 * SPACE_XV(space)[2];
dst[ 3]= -SPACE_XV(space)[2];
dst[ 4]= f->m00 * SPACE_YV(space)[0] + f->m20 * SPACE_YV(space)[2];
dst[ 5]= f->m11 * SPACE_YV(space)[1] + f->m21 * SPACE_YV(space)[2];
dst[ 6]= f->m22 * SPACE_YV(space)[2];
dst[ 7]= -SPACE_YV(space)[2];
dst[ 8]= f->m00 * SPACE_ZV(space)[0] + f->m20 * SPACE_ZV(space)[2];
dst[ 9]= f->m11 * SPACE_ZV(space)[1] + f->m21 * SPACE_ZV(space)[2];
dst[10]= f->m22 * SPACE_ZV(space)[2];
dst[11]= -SPACE_ZV(space)[2];
dst[12]= f->m00 * SPACE_ORIGIN(space)[0] + f->m20 * SPACE_ORIGIN(space)[2];
dst[13]= f->m11 * SPACE_ORIGIN(space)[1] + f->m21 * SPACE_ORIGIN(space)[2];
dst[14]= f->m22 * SPACE_ORIGIN(space)[2] + f->m32;
dst[15]= -SPACE_ORIGIN(space)[2];
}
if (ix & 3) { /* packed something */
ST(0)= sv_newmortal();
if (ix & 1) { /* packed floats */
float *buf;
int i;
buf= (float*) m3s_make_aligned_buffer(ST(0), sizeof(float)*16);
for (i= 0; i < 16; i++) buf[i]= (float) dst[i];
} else {
memcpy(m3s_make_aligned_buffer(ST(0), sizeof(double)*16), dst, sizeof(double)*16);
}
XSRETURN(1);
} else {
int i;
EXTEND(SP, 16);
for (i= 0; i < 16; i++)
mPUSHn(dst[i]);
XSRETURN(16);
}
#**********************************************************************************************
# Math::3Space::Vector
#**********************************************************************************************
MODULE = Math::3Space PACKAGE = Math::3Space::Vector
m3s_vector_p
vec3(vec_or_x, y=NULL, z=NULL)
SV* vec_or_x
SV* y
SV* z
INIT:
m3s_vector_t vec;
CODE:
M3S_VECLOAD(vec,vec_or_x,y,z,0);
RETVAL = vec;
OUTPUT:
RETVAL
m3s_vector_p
new(pkg, ...)
SV *pkg
INIT:
m3s_vector_t vec= { 0, 0, 0 };
const char *key;
IV i, ofs;
CODE:
if (items == 2 && SvROK(ST(1))) {
m3s_read_vector_from_sv(vec, ST(1), NULL, NULL);
}
else if (items & 1) {
for (i= 1; i < items; i+= 2) {
key= SvOK(ST(i))? SvPV_nolen(ST(i)) : "";
if (strcmp(key, "x") == 0) ofs= 0;
else if (strcmp(key, "y") == 0) ofs= 1;
else if (strcmp(key, "z") == 0) ofs= 2;
else croak("Unknown attribute '%s'", key);
if (!looks_like_number(ST(i+1)))
croak("Expected attribute '%s' value, but got '%s'", SvPV_nolen(ST(i)), SvPV_nolen(ST(i+1)));
vec[ofs]= SvNV(ST(i+1));
}
}
else
croak("Expected hashref, arrayref, or even-length list of attribute/value pairs");
RETVAL = vec;
OUTPUT:
RETVAL
void
x(vec, newval=NULL)
m3s_vector_p vec
SV *newval
ALIAS:
Math::3Space::Vector::y = 1
Math::3Space::Vector::z = 2
PPCODE:
if (newval) {
vec[ix]= SvNV(newval);
} else {
ST(0)= sv_2mortal(newSVnv(vec[ix]));
}
XSRETURN(1);
void
xyz(vec)
m3s_vector_p vec
PPCODE:
EXTEND(SP, 3);
PUSHs(sv_2mortal(newSVnv(vec[0])));
PUSHs(sv_2mortal(newSVnv(vec[1])));
PUSHs(sv_2mortal(newSVnv(vec[2])));
void
magnitude(vec, scale=NULL)
m3s_vector_p vec
SV *scale
INIT:
NV s, m= sqrt(m3s_vector_dotprod(vec,vec));
PPCODE:
if (scale) {
if (m > 0) {
s= SvNV(scale) / m;
vec[0] *= s;
vec[1] *= s;
vec[2] *= s;
} else
warn("can't scale magnitude=0 vector");
// return $self
} else {
ST(0)= sv_2mortal(newSVnv(m));
}
XSRETURN(1);
void
set(vec1, vec2_or_x, y=NULL, z=NULL)
m3s_vector_p vec1
SV *vec2_or_x
SV *y
SV *z
ALIAS:
Math::3Space::Vector::add = 1
Math::3Space::Vector::sub = 2
INIT:
NV vec2[3];
PPCODE:
M3S_VECLOAD(vec2,vec2_or_x,y,z,0);
if (ix == 0) {
vec1[0]= vec2[0];
vec1[1]= vec2[1];
vec1[2]= vec2[2];
} else if (ix == 1) {
vec1[0]+= vec2[0];
vec1[1]+= vec2[1];
vec1[2]+= vec2[2];
} else {
vec1[0]-= vec2[0];
vec1[1]-= vec2[1];
vec1[2]-= vec2[2];
}
XSRETURN(1);
void
scale(vec1, vec2_or_x, y=NULL, z=NULL)
m3s_vector_p vec1
SV *vec2_or_x
SV *y
SV *z
INIT:
NV vec2[3];
PPCODE:
// single value should be treated as ($x,$x,$x) instead of ($x,0,0)
if (looks_like_number(vec2_or_x)) {
vec2[0]= SvNV(vec2_or_x);
vec2[1]= y? SvNV(y) : vec2[0];
vec2[2]= z? SvNV(z) : y? 1 : vec2[0];
}
else {
m3s_read_vector_from_sv(vec2, vec2_or_x, NULL, NULL);
}
vec1[0]*= vec2[0];
vec1[1]*= vec2[1];
vec1[2]*= vec2[2];
XSRETURN(1);
NV
dot(vec1, vec2_or_x, y=NULL, z=NULL)
m3s_vector_p vec1
SV *vec2_or_x
SV *y
SV *z
INIT:
NV vec2[3];
CODE:
M3S_VECLOAD(vec2,vec2_or_x,y,z,0);
RETVAL= m3s_vector_dotprod(vec1, vec2);
OUTPUT:
RETVAL
NV
cos(vec1, vec2_or_x, y=NULL, z=NULL)
m3s_vector_p vec1
SV *vec2_or_x
SV *y
SV *z
INIT:
NV vec2[3];
CODE:
M3S_VECLOAD(vec2,vec2_or_x,y,z,0);
RETVAL= m3s_vector_cosine(vec1, vec2);
OUTPUT:
RETVAL
void
cross(vec1, vec2_or_x, vec3_or_y=NULL, z=NULL)
m3s_vector_p vec1
SV *vec2_or_x
SV *vec3_or_y
SV *z
INIT:
m3s_vector_t vec2, vec3;
PPCODE:
if (!vec3_or_y) { // RET = vec1->cross(vec2)
m3s_read_vector_from_sv(vec2, vec2_or_x, NULL, NULL);
m3s_vector_cross(vec3, vec1, vec2);
ST(0)= sv_2mortal(m3s_wrap_vector(vec3));
} else if (z || !SvROK(vec2_or_x) || looks_like_number(vec2_or_x)) { // RET = vec1->cross(x,y,z)
vec2[0]= SvNV(vec2_or_x);
vec2[1]= SvNV(vec3_or_y);
vec2[2]= z? SvNV(z) : 0;
m3s_vector_cross(vec3, vec1, vec2);
ST(0)= sv_2mortal(m3s_wrap_vector(vec3));
} else {
m3s_read_vector_from_sv(vec2, vec2_or_x, NULL, NULL);
m3s_read_vector_from_sv(vec3, vec3_or_y, NULL, NULL);
m3s_vector_cross(vec1, vec2, vec3);
// leave $self on stack
}
XSRETURN(1);
BOOT:
HV *inc= get_hv("INC", GV_ADD);
AV *isa;
hv_stores(inc, "Math::3Space::Projection", newSVpvs("Math/3Space.pm"));
hv_stores(inc, "Math::3Space::Projection::Frustum", newSVpvs("Math/3Space.pm"));
isa= get_av("Math::3Space::Projection::Frustum::ISA", GV_ADD);
av_push(isa, newSVpvs("Math::3Space::Projection"));
( run in 0.822 second using v1.01-cache-2.11-cpan-71847e10f99 )