#include "rays/ruby/matrix.h" #include #include "rays/ruby/point.h" #include "defs.h" RUCY_DEFINE_VALUE_OR_ARRAY_FROM_TO(Rays::Matrix) #define THIS to(self) #define CHECK RUCY_CHECK_OBJ(Rays::Matrix, self) static VALUE alloc(VALUE klass) { return new_type(klass); } static VALUE initialize(VALUE self) { CHECK; check_arg_count(__FILE__, __LINE__, "Matrix#initialize", argc, 0, 1, 16); if (argc > 0) *THIS = to(argc, argv); return self; } static VALUE initialize_copy(VALUE self, VALUE obj) { CHECK; *THIS = to(obj); return self; } static VALUE reset(VALUE self) { CHECK; check_arg_count(__FILE__, __LINE__, "Matrix#reset", argc, 0, 1, 16); switch (argc) { case 0: THIS->reset(); break; case 1: THIS->reset(to(argv[0])); break; case 16: THIS->reset( to(argv[0]), to(argv[1]), to(argv[2]), to(argv[3]), to(argv[4]), to(argv[5]), to(argv[6]), to(argv[7]), to(argv[8]), to(argv[9]), to(argv[10]), to(argv[11]), to(argv[12]), to(argv[13]), to(argv[14]), to(argv[15])); break; } return self; } static VALUE translate(VALUE self) { CHECK; check_arg_count(__FILE__, __LINE__, "Matrix#translate", argc, 1, 2, 3); THIS->translate(to(argc, argv)); return self; } static VALUE scale(VALUE self) { CHECK; check_arg_count(__FILE__, __LINE__, "Matrix#scale", argc, 1, 2, 3); THIS->scale(to(argc, argv)); return self; } static VALUE rotate(VALUE self) { CHECK; check_arg_count(__FILE__, __LINE__, "Matrix#rotate", argc, 1, 2, 4); float degree = to(argv[0]); if (argc == 1) THIS->rotate(degree); else THIS->rotate(degree, to(argc - 1, argv + 1)); return self; } static VALUE to_a(VALUE self) { CHECK; return array( THIS->x0, THIS->y0, THIS->z0, THIS->w0, THIS->x1, THIS->y1, THIS->z1, THIS->w1, THIS->x2, THIS->y2, THIS->z2, THIS->w2, THIS->x3, THIS->y3, THIS->z3, THIS->w3); } static VALUE mult(VALUE self, VALUE val) { CHECK; if (val.is_kind_of(Rays::matrix_class())) return value(*THIS * to(val)); if (val.is_kind_of(Rays::point_class())) return value(*THIS * to(val)); if (val.is_array()) { if (val.size() == 16) return value(*THIS * to(val)); else return value(*THIS * to(val)); } argument_error(__FILE__, __LINE__); } static VALUE set_at(VALUE self, VALUE row, VALUE column, VALUE val) { CHECK; return value(THIS->at(row.as_i(), column.as_i()) = to(val)); } static VALUE get_at(VALUE self, VALUE row, VALUE column) { CHECK; return value(THIS->at(row.as_i(), column.as_i())); } static VALUE compare(VALUE self, VALUE other) { CHECK; const Rays::Matrix& a = *THIS; const Rays::Matrix& b = to(other); for (int i = 0; i < Rays::Matrix::NELEM; ++i) { if (a[i] == b[i]) continue; return value(a[i] < b[i] ? -1 : +1); } return value(0); } static VALUE inspect(VALUE self) { CHECK; return value(Xot::stringf("#", THIS->inspect().c_str())); } static VALUE s_translate(VALUE self) { return translate(argc, argv, value(Rays::Matrix())); } static VALUE s_scale(VALUE self) { return scale(argc, argv, value(Rays::Matrix())); } static VALUE s_rotate(VALUE self) { return rotate(argc, argv, value(Rays::Matrix())); } static Class cMatrix; void Init_rays_matrix () { Module mRays = rb_define_module("Rays"); cMatrix = rb_define_class_under(mRays, "Matrix", rb_cObject); rb_define_alloc_func(cMatrix, alloc); rb_define_private_method(cMatrix, "initialize", RUBY_METHOD_FUNC(initialize), -1); rb_define_private_method(cMatrix, "initialize_copy", RUBY_METHOD_FUNC(initialize_copy), 1); rb_define_method(cMatrix, "reset", RUBY_METHOD_FUNC(reset), -1); rb_define_method(cMatrix, "translate", RUBY_METHOD_FUNC(translate), -1); rb_define_method(cMatrix, "scale", RUBY_METHOD_FUNC(scale), -1); rb_define_method(cMatrix, "rotate", RUBY_METHOD_FUNC(rotate), -1); rb_define_method(cMatrix, "to_a", RUBY_METHOD_FUNC(to_a), 0); cMatrix.define_method("*", mult); cMatrix.define_method("[]=", set_at); cMatrix.define_method("[]", get_at); cMatrix.define_method("<=>", compare); rb_define_method(cMatrix, "inspect", RUBY_METHOD_FUNC(inspect), 0); rb_define_singleton_method(cMatrix, "translate", RUBY_METHOD_FUNC(s_translate), -1); rb_define_singleton_method(cMatrix, "scale", RUBY_METHOD_FUNC(s_scale), -1); rb_define_singleton_method(cMatrix, "rotate", RUBY_METHOD_FUNC(s_rotate), -1); } namespace Rucy { template <> Rays::Matrix value_to (int argc, const Value* argv, bool convert) { if (argc == 1 && argv->is_array()) { argc = argv->size(); argv = argv->as_array(); } assert(argc == 0 || (argc > 0 && argv)); if (convert) { if (argc == 0) return Rays::Matrix(); else if (argv->is_num()) { switch (argc) { #define V(i) argv[i].as_f(true) case 1: return Rays::Matrix(V(0)); case 16: return Rays::Matrix( V(0), V(1), V(2), V(3), V(4), V(5), V(6), V(7), V(8), V(9), V(10), V(11), V(12), V(13), V(14), V(15)); #undef V default: argument_error(__FILE__, __LINE__); } } } if (argc != 1) argument_error(__FILE__, __LINE__); return value_to(*argv, convert); } }// Rucy namespace Rays { Class matrix_class () { return cMatrix; } }// Rays