summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorTom Black <[email protected]>2015-10-04 20:12:32 -0400
committerTom Black <[email protected]>2015-10-04 20:12:32 -0400
commitd70e4d425eab3848226215d42b3a81b8e191de1a (patch)
tree197bd95fe7bf5a6479aceb646a8549cb1cf57183
downloadruby2d-d70e4d425eab3848226215d42b3a81b8e191de1a.tar.gz
ruby2d-d70e4d425eab3848226215d42b3a81b8e191de1a.zip
First!
-rw-r--r--.gitignore2
-rw-r--r--.gitmodules3
-rw-r--r--Gemfile3
-rw-r--r--Rakefile34
-rw-r--r--assets/app.icnsbin0 -> 55701 bytes
-rwxr-xr-xbin/ruby2d116
-rw-r--r--examples/input.rb50
-rw-r--r--examples/simple.rb13
-rw-r--r--examples/triangle.rb10
-rw-r--r--ext/ruby2d/extconf.rb61
-rw-r--r--ext/ruby2d/ruby2d.c235
-rw-r--r--lib/ruby2d.rb17
-rw-r--r--lib/ruby2d/application.rb39
-rw-r--r--lib/ruby2d/color.rb72
-rw-r--r--lib/ruby2d/dsl.rb27
-rw-r--r--lib/ruby2d/exceptions.rb12
-rw-r--r--lib/ruby2d/image.rb23
-rw-r--r--lib/ruby2d/quad.rb50
-rw-r--r--lib/ruby2d/rectangle.rb57
-rw-r--r--lib/ruby2d/sound.rb19
-rw-r--r--lib/ruby2d/square.rb26
-rw-r--r--lib/ruby2d/text.rb51
-rw-r--r--lib/ruby2d/triangle.rb45
-rw-r--r--lib/ruby2d/version.rb5
-rw-r--r--lib/ruby2d/window.rb132
-rw-r--r--ruby2d.gemspec20
-rw-r--r--tests/testcard.rb134
27 files changed, 1256 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..cec3cb5
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+*.gem
+Gemfile.lock
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..f1ba4c1
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "tests/media"]
+ path = tests/media
+ url = https://github.com/simple2d/test_media.git
diff --git a/Gemfile b/Gemfile
new file mode 100644
index 0000000..fa75df1
--- /dev/null
+++ b/Gemfile
@@ -0,0 +1,3 @@
+source 'https://rubygems.org'
+
+gemspec
diff --git a/Rakefile b/Rakefile
new file mode 100644
index 0000000..022193e
--- /dev/null
+++ b/Rakefile
@@ -0,0 +1,34 @@
+# require 'rake/testtask'
+
+task default: 'all'
+
+# Rake::TestTask.new do |t|
+# t.test_files = FileList['test/*_test.rb']
+# t.verbose = true
+# end
+
+desc "Build Gem"
+task :build do
+ puts "==> uninstall gem"
+ system "gem uninstall ruby2d --executables"
+
+ puts "==> build gem"
+ system "gem build ruby2d.gemspec --verbose"
+
+ puts "==> install gem"
+ system "gem install ruby2d-0.0.0.gem --local" # --verbose
+end
+
+
+desc "Run Testcard"
+task :testcard do
+ Rake::Task['build'].invoke
+ system '( cd tests/ ; ruby testcard.rb )'
+end
+
+
+desc "Test and Build"
+task :all do
+ # Rake::Task['test'].invoke
+ Rake::Task['build'].invoke
+end
diff --git a/assets/app.icns b/assets/app.icns
new file mode 100644
index 0000000..8485724
--- /dev/null
+++ b/assets/app.icns
Binary files differ
diff --git a/bin/ruby2d b/bin/ruby2d
new file mode 100755
index 0000000..f131ab1
--- /dev/null
+++ b/bin/ruby2d
@@ -0,0 +1,116 @@
+#!/usr/bin/env ruby
+require 'ruby2d/version'
+
+class String
+ def colorize(c); "\e[#{c}m#{self}\e[0m" end
+ def error; colorize('1;31') end
+ def bold; colorize('1') end
+end
+
+
+def build_native(rb_file)
+ if `which mruby`.empty?
+ puts "",
+ " mruby not found!".error, "",
+ " mruby is needed to build a native Ruby 2D applications.".bold, "",
+ " Install using Homebrew on OS X:",
+ " brew install mruby", ""
+ exit
+ end
+
+ `mkdir -p build`
+ `mrbc -Bruby2d_app_symbol -obuild/app.c #{rb_file}`
+
+ open('build/app.c', 'a') do |f|
+ f << %(
+#include "mruby.h"
+#include "mruby/irep.h"
+
+int main(void) {
+ mrb_state *mrb = mrb_open();
+ if (!mrb) { /* handle error */ }
+ mrb_load_irep(mrb, ruby2d_app_symbol);
+ mrb_close(mrb);
+}
+ )
+ end
+
+ `cc -std=c99 -lmruby build/app.c -o build/app`
+end
+
+
+def build_package
+ require 'fileutils'
+
+ icon_path = "#{Gem::Specification.find_by_name('ruby2d').gem_dir}/assets/app.icns"
+
+ FileUtils.mkpath "build/App.app/Contents/MacOS"
+ FileUtils.mkpath "build/App.app/Contents/Resources"
+ FileUtils.cp icon_path, "build/App.app/Contents/Resources"
+
+ info_plist = %(
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleExecutable</key>
+ <string>app</string>
+ <key>CFBundleIconFile</key>
+ <string>app.icns</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>1.0</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1.0</string>
+</dict>
+</plist>
+)
+
+ File.open("build/App.app/Contents/Info.plist", 'w') { |f| f.write(info_plist) }
+
+ FileUtils.cp "build/app", "build/App.app/Contents/MacOS/"
+
+ puts "App written to `build/App.app`."
+end
+
+
+# Command line usage and tree ##################################################
+
+usage = "
+Usage: ruby2d [-v|--version]
+ <command> <options>
+
+Summary of commands and options:
+ build Build the application...
+ native ...compile with mruby.
+ web ...for web platform.
+ package Create application package.
+ -v|--version Prints the installed version.
+
+"
+
+case ARGV[0]
+when 'build'
+ puts "Running build..."
+ case ARGV[1]
+ when 'native'
+ puts "Running build native..."
+ build_native ARGV[2]
+ when 'web'
+ puts "Running build web..."
+ else
+ puts usage
+ end
+when 'package'
+ puts "Running package..."
+ build_package
+when '-v', '--version'
+ puts Ruby2D::VERSION
+else
+ puts usage
+end
+
+exit
diff --git a/examples/input.rb b/examples/input.rb
new file mode 100644
index 0000000..a39a29c
--- /dev/null
+++ b/examples/input.rb
@@ -0,0 +1,50 @@
+require 'ruby2d'
+
+set width: 200, height: 100
+
+on key: 'a' do
+ puts "a pressed!"
+end
+
+show
+
+# on :mouse 'up' do
+#
+# end
+#
+# on :mouse 'down' do
+#
+# end
+#
+# on :key 'a' do
+#
+# end
+#
+# on :key_down 'a' do
+#
+# end
+#
+# on :mouse 'click' do
+#
+# end
+#
+# on :mouse 'right' do
+#
+# end
+#
+# on :mouse 'left' do
+#
+# end
+#
+#
+# on key: 'any' do
+#
+# end
+#
+# on key: 'a' do
+#
+# end
+#
+# on key: 'shift' do
+#
+# end
diff --git a/examples/simple.rb b/examples/simple.rb
new file mode 100644
index 0000000..ca8e99f
--- /dev/null
+++ b/examples/simple.rb
@@ -0,0 +1,13 @@
+require 'ruby2d'
+
+# Set the window size
+set width: 300, height: 200
+
+# Create a new shape
+s = Square.new
+
+# Give it some color
+s.color = 'red'
+
+# Show the window
+show
diff --git a/examples/triangle.rb b/examples/triangle.rb
new file mode 100644
index 0000000..71f0bbf
--- /dev/null
+++ b/examples/triangle.rb
@@ -0,0 +1,10 @@
+require 'ruby2d'
+
+Triangle.new(320, 50, 540, 430, 100, 430,
+[
+ [1.0, 0, 0, 1.0],
+ [0, 1.0, 0, 1.0],
+ [0, 0, 1.0, 1.0]
+])
+
+show
diff --git a/ext/ruby2d/extconf.rb b/ext/ruby2d/extconf.rb
new file mode 100644
index 0000000..96c9eb4
--- /dev/null
+++ b/ext/ruby2d/extconf.rb
@@ -0,0 +1,61 @@
+require 'mkmf'
+
+errors = []
+
+class String
+ def colorize(c); "\e[#{c}m#{self}\e[0m" end
+ def error; colorize('1;31') end
+ def bold; colorize('1') end
+end
+
+def print_errors(errors)
+ puts "
+#{"== Ruby 2D Installation Errors =============================".bold}
+
+ Ruby 2D found some problems and was not installed:
+
+ #{errors.join("\n ").error}
+
+#{"============================================================".bold}
+
+"
+end
+
+
+# Add space to flags
+$LDFLAGS << ' '
+
+# Mac OS
+if RUBY_PLATFORM =~ /darwin/
+
+ # if `which simple2d`.empty? && has brew
+ # brew tap simple2d/tap
+ # brew install simple2d
+ # end
+
+ if `which simple2d`.empty?
+ errors << "Simple 2D not found!"
+ print_errors(errors)
+ exit
+ end
+
+ $LDFLAGS << `simple2d --libs`
+
+# Windows
+elsif RUBY_PLATFORM =~ /mingw/
+
+# Linux
+else
+
+ # if `which simple2d`.empty?
+ # install simple2d using script
+ # end
+
+ $LDFLAGS << `simple2d --libs`
+
+end
+
+# Remove any newlines in flags
+$LDFLAGS.gsub!(/\n/, ' ')
+
+create_makefile('ruby2d/ruby2d')
diff --git a/ext/ruby2d/ruby2d.c b/ext/ruby2d/ruby2d.c
new file mode 100644
index 0000000..1c4442a
--- /dev/null
+++ b/ext/ruby2d/ruby2d.c
@@ -0,0 +1,235 @@
+#include <ruby.h>
+#include <simple2d.h>
+
+#define TRIANGLE 1
+#define QUAD 2
+#define IMAGE 3
+#define TEXT 4
+
+static VALUE ruby2d_module;
+static VALUE ruby2d_window_klass;
+static VALUE c_data_klass;
+
+VALUE self;
+Window *window;
+
+
+struct image_data {
+ Image img;
+};
+
+struct text_data {
+ Text txt;
+};
+
+struct sound_data {
+ Sound snd;
+};
+
+
+static void free_image(struct image_data *data) {
+ S2D_FreeImage(data->img);
+}
+
+static VALUE init_image(char *path) {
+ struct image_data *data = ALLOC(struct image_data);
+ data->img = S2D_CreateImage(path);
+ return Data_Wrap_Struct(c_data_klass, NULL, free_image, data);
+}
+
+
+
+static void free_text(struct text_data *data) {
+ S2D_FreeText(data->txt);
+}
+
+static VALUE init_text(char *font, char *msg, int size) {
+ struct text_data *data = ALLOC(struct text_data);
+ data->txt = S2D_CreateText(font, msg, size);
+ return Data_Wrap_Struct(c_data_klass, NULL, free_text, data);
+}
+
+
+
+void on_key(const char *key) {
+ rb_funcall(self, rb_intern("key_callback"), 1, rb_str_new2(key));
+}
+
+void on_key_down(const char *key) {
+ rb_funcall(self, rb_intern("key_down_callback"), 1, rb_str_new2(key));
+}
+
+
+void update() {
+ // Set the cursor
+ rb_iv_set(self, "@mouse_x", INT2NUM(window->mouse.x));
+ rb_iv_set(self, "@mouse_y", INT2NUM(window->mouse.y));
+
+ // Call update proc, `window.update`
+ rb_funcall(self, rb_intern("update_callback"), 0);
+}
+
+
+void render() {
+
+ // Read window objects
+ VALUE objects = rb_iv_get(self, "@objects");
+ int num_objects = NUM2INT(rb_funcall(objects, rb_intern("count"), 0));
+
+ // Switch on each object type
+ for (int i = 0; i < num_objects; ++i) {
+
+ VALUE el = rb_ary_entry(objects, i);
+ int type_id = NUM2INT(rb_iv_get(el, "@type_id"));
+
+ // Switch on the object's type_id
+ switch(type_id) {
+
+ case TRIANGLE: {
+ VALUE c1 = rb_iv_get(el, "@c1");
+ VALUE c2 = rb_iv_get(el, "@c2");
+ VALUE c3 = rb_iv_get(el, "@c3");
+
+ S2D_DrawTriangle(
+ NUM2DBL(rb_iv_get(el, "@x1")),
+ NUM2DBL(rb_iv_get(el, "@y1")),
+ NUM2DBL(rb_iv_get(c1, "@r")),
+ NUM2DBL(rb_iv_get(c1, "@g")),
+ NUM2DBL(rb_iv_get(c1, "@b")),
+ NUM2DBL(rb_iv_get(c1, "@a")),
+
+ NUM2DBL(rb_iv_get(el, "@x2")),
+ NUM2DBL(rb_iv_get(el, "@y2")),
+ NUM2DBL(rb_iv_get(c2, "@r")),
+ NUM2DBL(rb_iv_get(c2, "@g")),
+ NUM2DBL(rb_iv_get(c2, "@b")),
+ NUM2DBL(rb_iv_get(c2, "@a")),
+
+ NUM2DBL(rb_iv_get(el, "@x3")),
+ NUM2DBL(rb_iv_get(el, "@y3")),
+ NUM2DBL(rb_iv_get(c3, "@r")),
+ NUM2DBL(rb_iv_get(c3, "@g")),
+ NUM2DBL(rb_iv_get(c3, "@b")),
+ NUM2DBL(rb_iv_get(c3, "@a"))
+ );
+ }
+ break;
+
+ case QUAD: {
+ VALUE c1 = rb_iv_get(el, "@c1");
+ VALUE c2 = rb_iv_get(el, "@c2");
+ VALUE c3 = rb_iv_get(el, "@c3");
+ VALUE c4 = rb_iv_get(el, "@c4");
+
+ S2D_DrawQuad(
+ NUM2DBL(rb_iv_get(el, "@x1")),
+ NUM2DBL(rb_iv_get(el, "@y1")),
+ NUM2DBL(rb_iv_get(c1, "@r")),
+ NUM2DBL(rb_iv_get(c1, "@g")),
+ NUM2DBL(rb_iv_get(c1, "@b")),
+ NUM2DBL(rb_iv_get(c1, "@a")),
+
+ NUM2DBL(rb_iv_get(el, "@x2")),
+ NUM2DBL(rb_iv_get(el, "@y2")),
+ NUM2DBL(rb_iv_get(c2, "@r")),
+ NUM2DBL(rb_iv_get(c2, "@g")),
+ NUM2DBL(rb_iv_get(c2, "@b")),
+ NUM2DBL(rb_iv_get(c2, "@a")),
+
+ NUM2DBL(rb_iv_get(el, "@x3")),
+ NUM2DBL(rb_iv_get(el, "@y3")),
+ NUM2DBL(rb_iv_get(c3, "@r")),
+ NUM2DBL(rb_iv_get(c3, "@g")),
+ NUM2DBL(rb_iv_get(c3, "@b")),
+ NUM2DBL(rb_iv_get(c3, "@a")),
+
+ NUM2DBL(rb_iv_get(el, "@x4")),
+ NUM2DBL(rb_iv_get(el, "@y4")),
+ NUM2DBL(rb_iv_get(c4, "@r")),
+ NUM2DBL(rb_iv_get(c4, "@g")),
+ NUM2DBL(rb_iv_get(c4, "@b")),
+ NUM2DBL(rb_iv_get(c4, "@a"))
+ );
+ }
+ break;
+
+ case IMAGE: {
+ if (rb_iv_get(el, "@data") == Qnil) {
+ VALUE data = init_image(RSTRING_PTR(rb_iv_get(el, "@path")));
+ rb_iv_set(el, "@data", data);
+ }
+
+ struct image_data *data;
+ Data_Get_Struct(rb_iv_get(el, "@data"), struct image_data, data);
+
+ data->img.x = NUM2DBL(rb_iv_get(el, "@x"));
+ data->img.y = NUM2DBL(rb_iv_get(el, "@y"));
+ S2D_DrawImage(data->img);
+ }
+ break;
+
+ case TEXT: {
+ if (rb_iv_get(el, "@data") == Qnil) {
+
+ VALUE data = init_text(
+ RSTRING_PTR(rb_iv_get(el, "@font")),
+ RSTRING_PTR(rb_iv_get(el, "@text")),
+ NUM2DBL(rb_iv_get(el, "@size"))
+ );
+
+ rb_iv_set(el, "@data", data);
+ }
+
+ struct text_data *data;
+ Data_Get_Struct(rb_iv_get(el, "@data"), struct text_data, data);
+
+ data->txt.x = NUM2DBL(rb_iv_get(el, "@x"));
+ data->txt.y = NUM2DBL(rb_iv_get(el, "@y"));
+ S2D_DrawText(data->txt);
+ }
+ break;
+ }
+ }
+}
+
+
+/*
+ * Ruby2D::Window#show
+ */
+static VALUE ruby2d_show(VALUE s) {
+ self = s;
+
+ char *title = RSTRING_PTR(rb_iv_get(self, "@title"));
+ int width = NUM2INT(rb_iv_get(self, "@width"));
+ int height = NUM2INT(rb_iv_get(self, "@height"));
+
+ window = S2D_CreateWindow(
+ title, width, height, update, render
+ );
+
+ window->on_key = on_key;
+ window->on_key_down = on_key_down;
+
+ S2D_Show(window);
+
+ return Qnil;
+}
+
+
+/*
+ * Ruby C extension init
+ */
+void Init_ruby2d() {
+
+ // Ruby2D
+ ruby2d_module = rb_define_module("Ruby2D");
+
+ // Ruby2D::Window
+ ruby2d_window_klass = rb_define_class_under(ruby2d_module, "Window", rb_cObject);
+
+ // Ruby2D::Window#show
+ rb_define_method(ruby2d_window_klass, "show", ruby2d_show, 0);
+
+ // Ruby2D::CData
+ c_data_klass = rb_define_class_under(ruby2d_module, "CData", rb_cObject);
+}
diff --git a/lib/ruby2d.rb b/lib/ruby2d.rb
new file mode 100644
index 0000000..3e8ab78
--- /dev/null
+++ b/lib/ruby2d.rb
@@ -0,0 +1,17 @@
+# ruby2d.rb
+
+require 'ruby2d/window'
+require 'ruby2d/application'
+require 'ruby2d/color'
+require 'ruby2d/quad'
+require 'ruby2d/rectangle'
+require 'ruby2d/square'
+require 'ruby2d/triangle'
+require 'ruby2d/image'
+require 'ruby2d/text'
+require 'ruby2d/dsl'
+require 'ruby2d/exceptions'
+require 'ruby2d/ruby2d' # load native extension
+
+include Ruby2D::DSL
+include Ruby2D
diff --git a/lib/ruby2d/application.rb b/lib/ruby2d/application.rb
new file mode 100644
index 0000000..ebef2b5
--- /dev/null
+++ b/lib/ruby2d/application.rb
@@ -0,0 +1,39 @@
+# application.rb
+
+module Ruby2D::Application
+ class << self
+ @@window = Ruby2D::Window.new
+
+ def get(sym)
+ @@window.get(sym)
+ end
+
+ def set(opts)
+ @@window.set(opts)
+ end
+
+ def on(mouse: nil, key: nil, &proc)
+ @@window.on(mouse: mouse, key: key, &proc)
+ end
+
+ def add(o)
+ @@window.add(o)
+ end
+
+ def remove(o)
+ @@window.remove(o)
+ end
+
+ def clear
+ @@window.clear
+ end
+
+ def update(&proc)
+ @@window.update(&proc)
+ end
+
+ def show
+ @@window.show
+ end
+ end
+end
diff --git a/lib/ruby2d/color.rb b/lib/ruby2d/color.rb
new file mode 100644
index 0000000..9c7bc32
--- /dev/null
+++ b/lib/ruby2d/color.rb
@@ -0,0 +1,72 @@
+# color.rb
+
+module Ruby2D
+ class Color
+
+ attr_reader :r, :g, :b, :a
+
+ def initialize(c)
+ case c
+ when 'black'
+ @r, @g, @b, @a = to_f([0, 0, 0, 255])
+ when 'gray'
+ @r, @g, @b, @a = to_f([170, 170, 170, 255])
+ when 'silver'
+ @r, @g, @b, @a = to_f([221, 221, 221, 255])
+ when 'white'
+ @r, @g, @b, @a = to_f([255, 255, 255, 255])
+ when 'navy'
+ @r, @g, @b, @a = to_f([0, 31, 63, 255])
+ when 'blue'
+ @r, @g, @b, @a = to_f([0, 116, 217, 255])
+ when 'aqua'
+ @r, @g, @b, @a = to_f([127, 219, 255, 255])
+ when 'teal'
+ @r, @g, @b, @a = to_f([57, 204, 204, 255])
+ when 'olive'
+ @r, @g, @b, @a = to_f([61, 153, 112, 255])
+ when 'green'
+ @r, @g, @b, @a = to_f([46, 204, 64, 255])
+ when 'lime'
+ @r, @g, @b, @a = to_f([1, 255, 112, 255])
+ when 'yellow'
+ @r, @g, @b, @a = to_f([255, 220, 0, 255])
+ when 'orange'
+ @r, @g, @b, @a = to_f([255, 133, 27, 255])
+ when 'red'
+ @r, @g, @b, @a = to_f([255, 65, 54, 255])
+ when 'maroon'
+ @r, @g, @b, @a = to_f([133, 20, 75, 255])
+ when 'fuchsia'
+ @r, @g, @b, @a = to_f([240, 18, 190, 255])
+ when 'purple'
+ @r, @g, @b, @a = to_f([177, 13, 201, 255])
+ when 'brown'
+ @r, @g, @b, @a = to_f([102, 51, 0, 255])
+ when 'random'
+ @r, @g, @b, @a = rand(0..1.0), rand(0..1.0), rand(0..1.0), 1.0
+ when Array
+ @r, @g, @b, @a = to_f([c[0], c[1], c[2], c[3]])
+ else
+ # raise Error, "Color does not exist!"
+ puts "Error! Bad color."
+ end
+ end
+
+ private
+
+ # Convert from Uint8 (0..255) to Float (0..1.0)
+ def to_f(a)
+ b = []
+ a.each do |n|
+ if n.class == Fixnum
+ b.push(n / 255.0)
+ else
+ b.push(n)
+ end
+ end
+ return b
+ end
+
+ end
+end
diff --git a/lib/ruby2d/dsl.rb b/lib/ruby2d/dsl.rb
new file mode 100644
index 0000000..c5bed09
--- /dev/null
+++ b/lib/ruby2d/dsl.rb
@@ -0,0 +1,27 @@
+# dsl.rb
+
+module Ruby2D::DSL
+ def hello
+ puts "hi"
+ end
+
+ def get(sym)
+ Ruby2D::Application.get(sym)
+ end
+
+ def set(opts)
+ Ruby2D::Application.set(opts)
+ end
+
+ def on(mouse: nil, key: nil, &proc)
+ Ruby2D::Application.on(mouse: mouse, key: key, &proc)
+ end
+
+ def update(&proc)
+ Ruby2D::Application.update(&proc)
+ end
+
+ def show
+ Ruby2D::Application.show
+ end
+end
diff --git a/lib/ruby2d/exceptions.rb b/lib/ruby2d/exceptions.rb
new file mode 100644
index 0000000..6f8416b
--- /dev/null
+++ b/lib/ruby2d/exceptions.rb
@@ -0,0 +1,12 @@
+# exceptions.rb
+
+module Ruby2D
+ class Error < StandardError
+ def initialize(msg)
+ super(msg)
+ puts msg
+ puts "Occurred in:"
+ puts " " + caller.last, "\n"
+ end
+ end
+end
diff --git a/lib/ruby2d/image.rb b/lib/ruby2d/image.rb
new file mode 100644
index 0000000..280d742
--- /dev/null
+++ b/lib/ruby2d/image.rb
@@ -0,0 +1,23 @@
+# image.rb
+
+module Ruby2D
+ class Image
+
+ attr_accessor :x, :y
+
+ def initialize(x, y, path)
+
+ unless File.exists? path
+ raise Ruby2D::Error, "Cannot find image file!"
+ end
+
+ @type_id = 3
+ @x, @y, @path = x, y, path
+
+ if defined? Ruby2D::DSL
+ Ruby2D::Application.add(self)
+ end
+ end
+
+ end
+end
diff --git a/lib/ruby2d/quad.rb b/lib/ruby2d/quad.rb
new file mode 100644
index 0000000..f496cc1
--- /dev/null
+++ b/lib/ruby2d/quad.rb
@@ -0,0 +1,50 @@
+# quad.rb
+
+module Ruby2D
+ class Quad
+ # Coordinates in clockwise order, starting at top left:
+ # x1,y1 == top left
+ # x2,y2 == top right
+ # x3,y3 == bottom right
+ # x4,y4 == bottom left
+ attr_accessor :x1, :y1, :c1,
+ :x2, :y2, :c2,
+ :x3, :y3, :c3,
+ :x4, :y4, :c4
+
+ attr_reader :color
+
+ def initialize(x1, y1, x2, y2, x3, y3, x4, y4, c='white')
+ @type_id = 2
+ @x1, @y1, @x2, @y2, @x3, @y3, @x4, @y4 = x1, y1, x2, y2, x3, y3, x4, y4
+ @color = c
+ update_color(c)
+
+ if defined? Ruby2D::DSL
+ Ruby2D::Application.add(self)
+ end
+ end
+
+ def color=(c)
+ @color = c
+ update_color(c)
+ end
+
+ private
+
+ def update_color(c)
+ # If a 2D array
+ if c.class == Array && c.all? { |el| el.class == Array }
+ @c1 = Ruby2D::Color.new(c[0])
+ @c2 = Ruby2D::Color.new(c[1])
+ @c3 = Ruby2D::Color.new(c[2])
+ @c4 = Ruby2D::Color.new(c[3])
+ else
+ @c1 = Ruby2D::Color.new(c)
+ @c2 = Ruby2D::Color.new(c)
+ @c3 = Ruby2D::Color.new(c)
+ @c4 = Ruby2D::Color.new(c)
+ end
+ end
+ end
+end
diff --git a/lib/ruby2d/rectangle.rb b/lib/ruby2d/rectangle.rb
new file mode 100644
index 0000000..45a0cee
--- /dev/null
+++ b/lib/ruby2d/rectangle.rb
@@ -0,0 +1,57 @@
+# rectangle.rb
+
+module Ruby2D
+ class Rectangle < Ruby2D::Quad
+
+ attr_reader :x, :y, :width, :height
+
+ def initialize(x, y, w, h, c='white')
+ @type_id = 2
+ @x, @y, @width, @height, @color = x, y, w, h, c
+ update_coords(x, y, w, h)
+ update_color(c)
+
+ if defined? Ruby2D::DSL
+ Ruby2D::Application.add(self)
+ end
+ end
+
+ def x=(x)
+ @x = @x1 = x
+ @x2 = x + @width
+ @x3 = x + @width
+ @x4 = x
+ end
+
+ def y=(y)
+ @y = @y1 = y
+ @y2 = y
+ @y3 = y + @height
+ @y4 = y + @height
+ end
+
+ def width=(w)
+ @width = w
+ update_coords(@x, @y, w, @height)
+ end
+
+ def height=(h)
+ @height = h
+ update_coords(@x, @y, @width, h)
+ end
+
+ private
+
+ def update_coords(x, y, w, h)
+ @x1 = x
+ @y1 = y
+ @x2 = x + w
+ @y2 = y
+ @x4 = x
+ @y4 = y + h
+ @x3 = x + w
+ @y3 = y + h
+ end
+
+ end
+end
diff --git a/lib/ruby2d/sound.rb b/lib/ruby2d/sound.rb
new file mode 100644
index 0000000..edbbef9
--- /dev/null
+++ b/lib/ruby2d/sound.rb
@@ -0,0 +1,19 @@
+# sound.rb
+
+module Ruby2D
+ class Sound
+
+ def initialize(window, path)
+ unless File.exists? path
+ raise Error, "Cannot find sound file!"
+ end
+ window.create_audio(self, path)
+ @window, @path = window, path
+ end
+
+ def play
+ @window.play_audio(self)
+ end
+
+ end
+end
diff --git a/lib/ruby2d/square.rb b/lib/ruby2d/square.rb
new file mode 100644
index 0000000..6614c06
--- /dev/null
+++ b/lib/ruby2d/square.rb
@@ -0,0 +1,26 @@
+# square.rb
+
+module Ruby2D
+ class Square < Ruby2D::Rectangle
+
+ attr_reader :size
+
+ def initialize(x=0, y=0, s=100, c='white')
+ @type_id = 2
+ @x, @y, @color = x, y, c
+ @width = @height = @size = s
+ update_coords(x, y, s, s)
+ update_color(c)
+
+ if defined? Ruby2D::DSL
+ Ruby2D::Application.add(self)
+ end
+ end
+
+ def size=(s)
+ self.width = self.height = @size = s
+ end
+
+ private :width=, :height=
+ end
+end
diff --git a/lib/ruby2d/text.rb b/lib/ruby2d/text.rb
new file mode 100644
index 0000000..bd4637f
--- /dev/null
+++ b/lib/ruby2d/text.rb
@@ -0,0 +1,51 @@
+# text.rb
+
+module Ruby2D
+ class Text
+
+ attr_accessor :x, :y, :size, :text
+
+ def initialize(x=0, y=0, size=20, text="Hello World!", font="Arial", c="white")
+ if font.include? '.'
+ unless File.exists? font
+ raise Error, "Cannot find font file!"
+ else
+ @font = font
+ end
+ else
+ @font = resolve_path(font)
+ end
+
+ @type_id = 4
+ @x, @y, @size = x, y, size
+ @text, @color = text, c
+ update_color(c)
+
+ if defined? Ruby2D::DSL
+ Ruby2D::Application.add(self)
+ end
+ end
+
+ def color=(c)
+ @color = c
+ update_color(c)
+ end
+
+ private
+
+ def resolve_path(font)
+ if RUBY_PLATFORM =~ /darwin/
+ font_path = "/Library/Fonts/#{font}.ttf"
+ unless File.exists? font_path
+ raise Error, "Cannot find system font!"
+ end
+ end
+ font_path
+ end
+
+ def update_color(c)
+ @c = Ruby2D::Color.new(c)
+ end
+
+ end
+end
diff --git a/lib/ruby2d/triangle.rb b/lib/ruby2d/triangle.rb
new file mode 100644
index 0000000..a4754ed
--- /dev/null
+++ b/lib/ruby2d/triangle.rb
@@ -0,0 +1,45 @@
+# triangle.rb
+
+module Ruby2D
+ class Triangle
+
+ attr_accessor :x1, :y1, :c1,
+ :x2, :y2, :c2,
+ :x3, :y3, :c3
+ attr_reader :color
+
+ def initialize(x1, y1, x2, y2, x3, y3, c='white')
+ @type_id = 1
+ @x1, @y1 = x1, y1
+ @x2, @y2 = x2, y2
+ @x3, @y3 = x3, y3
+ @color = c
+ update_color(c)
+
+ if defined? Ruby2D::DSL
+ Ruby2D::Application.add(self)
+ end
+ end
+
+ def color=(c)
+ update_color(c)
+ @color = c
+ end
+
+ private
+
+ def update_color(c)
+ # If a 2D array
+ if c.class == Array && c.all? { |el| el.class == Array }
+ @c1 = Ruby2D::Color.new(c[0])
+ @c2 = Ruby2D::Color.new(c[1])
+ @c3 = Ruby2D::Color.new(c[2])
+ else
+ @c1 = Ruby2D::Color.new(c)
+ @c2 = Ruby2D::Color.new(c)
+ @c3 = Ruby2D::Color.new(c)
+ end
+ end
+
+ end
+end
diff --git a/lib/ruby2d/version.rb b/lib/ruby2d/version.rb
new file mode 100644
index 0000000..a6ab2f1
--- /dev/null
+++ b/lib/ruby2d/version.rb
@@ -0,0 +1,5 @@
+# version.rb
+
+module Ruby2D
+ VERSION = '0.0.0'
+end
diff --git a/lib/ruby2d/window.rb b/lib/ruby2d/window.rb
new file mode 100644
index 0000000..6011d42
--- /dev/null
+++ b/lib/ruby2d/window.rb
@@ -0,0 +1,132 @@
+# window.rb
+
+module Ruby2D
+ class Window
+ attr_reader :title, :width, :height, :mouse_x, :mouse_y
+
+ def initialize(width: 640, height: 480, title: "Ruby 2D", fps: 60, vsync: true)
+ @width, @height, @title = width, height, title
+ @mouse_x = @mouse_y = 0
+ @fps_cap = fps
+ @vsync = vsync
+
+ @objects = []
+ @keys = {}
+ @keys_down = {}
+ @update_proc = Proc.new {}
+ end
+
+ def get(sym)
+ case sym
+ when :window
+ return self
+ when :title
+ return @title
+ when :width
+ return @width
+ when :height
+ return @height
+ when :mouse_x
+ return @mouse_x
+ when :mouse_y
+ return @mouse_y
+ end
+ end
+
+ def set(opts)
+ if opts.include? :title
+ @title = opts[:title]
+ end
+
+ if opts.include? :width
+ @width = opts[:width]
+ end
+
+ if opts.include? :height
+ @height = opts[:height]
+ end
+ end
+
+ def on(mouse: nil, key: nil, &proc)
+ puts "mouse: #{mouse}"
+ puts "key: #{key}"
+ # proc.call
+ key(key, &proc)
+ end
+
+ def add(o)
+ case o
+ when nil
+ raise Error, "Cannot add '#{o.class}' to window!"
+ when Array
+ o.each { |x| add_object(x) }
+ else
+ add_object(o)
+ end
+ end
+
+ def remove(o)
+ if o == nil
+ raise Error, "Cannot remove '#{o.class}' from window!"
+ end
+
+ if i = @objects.index(o)
+ @objects.slice!(i)
+ true
+ else
+ false
+ end
+ end
+
+ def clear
+ @objects.clear
+ end
+
+ # Register key string with proc
+ def key(key, &proc)
+ @keys[key] = proc
+ true
+ end
+
+ def key_callback(key)
+ key.downcase!
+ if @keys.has_key? key
+ @keys[key].call
+ end
+ end
+
+ # Register key string with proc
+ def key_down(key, &proc)
+ @keys_down[key] = proc
+ true
+ end
+
+ def key_down_callback(key)
+ key.downcase!
+ if @keys_down.has_key? key
+ @keys_down[key].call
+ end
+ end
+
+ def update(&proc)
+ @update_proc = proc
+ true
+ end
+
+ def update_callback
+ @update_proc.call
+ end
+
+ private
+
+ def add_object(o)
+ @objects.push(o)
+ true
+ else
+ false
+ end
+ end
+
+ end
+end
diff --git a/ruby2d.gemspec b/ruby2d.gemspec
new file mode 100644
index 0000000..df03f5a
--- /dev/null
+++ b/ruby2d.gemspec
@@ -0,0 +1,20 @@
+$:.push File.expand_path('../lib', __FILE__)
+require 'ruby2d/version'
+
+Gem::Specification.new do |s|
+ s.name = 'ruby2d'
+ s.version = Ruby2D::VERSION
+ s.date = '2015-10-01'
+ s.author = 'Tom Black'
+ s.email = '@blacktm'
+ s.summary = 'Ruby 2D'
+ s.description = 'Make cross-platform 2D applications in Ruby.'
+ s.homepage = 'http://www.ruby2d.com'
+ s.license = 'MIT'
+ s.files = Dir.glob('lib/**/*') +
+ Dir.glob('assets/**/*') +
+ Dir.glob('ext/**/*.{c,rb}')
+ s.extensions = ['ext/ruby2d/extconf.rb']
+ s.executables << 'ruby2d'
+ s.required_ruby_version = '>= 2.2.0'
+end
diff --git a/tests/testcard.rb b/tests/testcard.rb
new file mode 100644
index 0000000..09bc40c
--- /dev/null
+++ b/tests/testcard.rb
@@ -0,0 +1,134 @@
+require 'ruby2d'
+
+set width: 700, height: 500, title: "Ruby 2D – Testcard"
+
+# Read window attributes
+puts "
+=== Window Attributes ===
+Title: #{get :title}
+Width: #{get :width}
+Height: #{get :height}
+Self: #{get :window}\n\n"
+
+# Primary colors
+Rectangle.new(0, 0, 50, 100, [1.0, 0, 0, 1.0])
+Rectangle.new(50, 0, 50, 100, [0, 1.0, 0, 1.0])
+Rectangle.new(100, 0, 50, 100, [0, 0, 1.0, 1.0])
+
+# Color strings
+Rectangle.new(150, 0, 50, 50, 'black')
+Rectangle.new(200, 0, 50, 50, 'gray')
+Rectangle.new(250, 0, 50, 50, 'silver')
+Rectangle.new(300, 0, 50, 50, 'white')
+Rectangle.new(350, 0, 50, 50, 'navy')
+Rectangle.new(400, 0, 50, 50, 'blue')
+Rectangle.new(450, 0, 50, 50, 'aqua')
+Rectangle.new(500, 0, 50, 50, 'teal')
+Rectangle.new(550, 0, 50, 50, 'olive')
+
+Rectangle.new(150, 50, 50, 50, 'green')
+Rectangle.new(200, 50, 50, 50, 'lime')
+Rectangle.new(250, 50, 50, 50, 'yellow')
+Rectangle.new(300, 50, 50, 50, 'orange')
+Rectangle.new(350, 50, 50, 50, 'red')
+Rectangle.new(400, 50, 50, 50, 'maroon')
+Rectangle.new(450, 50, 50, 50, 'fuchsia')
+Rectangle.new(500, 50, 50, 50, 'purple')
+Rectangle.new(550, 50, 50, 50, 'brown')
+
+Rectangle.new(600, 0, 50, 50, 'random')
+Rectangle.new(650, 0, 50, 50, 'random')
+Rectangle.new(600, 50, 50, 50, 'random')
+Rectangle.new(650, 50, 50, 50, 'random')
+
+# White to black gradient
+Rectangle.new(0, 100, 700, 25,
+[
+ [1.0, 1.0, 1.0, 1.0],
+ [0.0, 0.0, 0.0, 0.0], # testing Float
+ [ 0, 0, 0, 0], # testing Fixnum
+ [1.0, 1.0, 1.0, 1.0]
+])
+
+# Color gradient
+Rectangle.new(0, 125, 700, 50,
+[
+ [1.0, 0.0, 0.0, 1.0],
+ [0.0, 1.0, 0.0, 1.0],
+ [0.0, 0.0, 1.0, 1.0],
+ [1.0, 1.0, 0.0, 1.0]
+])
+
+# Transparancy
+Rectangle.new(0, 165, 700, 35,
+[
+ [1.0, 1.0, 1.0, 0.0],
+ [1.0, 1.0, 1.0, 1.0],
+ [1.0, 1.0, 1.0, 1.0],
+ [1.0, 1.0, 1.0, 0.0]
+])
+
+# Triangles
+Triangle.new(25, 200, 50, 250, 0, 250, [1.0, 0, 0, 1.0])
+Triangle.new(75, 200, 100, 250, 50, 250, [0, 1.0, 1, 1.0])
+Triangle.new(125, 200, 150, 250, 100, 250, [0, 0, 1.0, 1.0])
+Triangle.new(175, 200, 200, 250, 150, 250,
+[
+ [1.0, 0, 0, 1.0],
+ [0, 1.0, 0, 1.0],
+ [0, 0, 1.0, 1.0]
+])
+Rectangle.new(200, 200, 50, 50, 'gray') # add background for transparancy
+Triangle.new(225, 200, 250, 250, 200, 250,
+[
+ [1.0, 1.0, 1.0, 1.0],
+ [0.0, 0.0, 0.0, 1.0],
+ [1.0, 1.0, 1.0, 0.0]
+])
+
+# Quadrilaterals
+Quad.new(
+ 300, 200,
+ 350, 200,
+ 300, 250,
+ 250, 250,
+ [
+ [1.0, 0.0, 0.0, 1.0],
+ [0.0, 1.0, 0.0, 1.0],
+ [0.0, 0.0, 1.0, 1.0],
+ [1.0, 1.0, 0.0, 1.0]
+ ]
+)
+
+Quad.new(
+ 250, 200,
+ 300, 200,
+ 350, 250,
+ 300, 250,
+ [
+ [1.0, 1.0, 1.0, 0.0],
+ [1.0, 1.0, 1.0, 0.0],
+ [1.0, 1.0, 1.0, 1.0],
+ [1.0, 1.0, 1.0, 0.0]
+ ]
+)
+
+# Images
+Image.new(580, 180, "media/image.png")
+Image.new(580, 290, "media/image.jpg")
+Image.new(580, 400, "media/image.bmp")
+
+# Text
+Text.new(0, 300)
+# Text.new(0, 350, 30, "Hello Ruby 2D!")
+
+
+# Pointer for mouse
+pointer = Square.new(0, 0, 10, 'red')
+
+update do
+ pointer.x = (get :mouse_x) - 5
+ pointer.y = (get :mouse_y) - 7
+end
+
+show