Project

rays

0.0
A long-lived project that still receives updates
This library helps you to develop graphics application with OpenGL.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies

Runtime

~> 0.3.13
~> 0.3.13
 Project Readme

Rays - A 2D drawing engine on OpenGL

Ask DeepWiki License Build Status Gem Version

⚠️ Notice

This repository is a read-only mirror of our monorepo. We do not accept pull requests or direct contributions here.

🔄 Where to Contribute?

All development happens in our xord/all monorepo, which contains all our main libraries. If you'd like to contribute, please submit your changes there.

For more details, check out our Contribution Guidelines.

Thanks for your support! 🙌

🚀 About

Rays is a hardware-accelerated 2D drawing engine for Ruby. It is built on OpenGL and exposes a retained-mode-friendly API: build Polygon, Polyline, Image, Shader, and Font objects, then paint them into an off-screen Image (or a window provided by Reflex) through a Painter.

It is the rendering layer used by Reflex, Processing, RubySketch, and Reight. Like the rest of the xord/* family, it is primarily developed for our own use, but it also works as a standalone drawing gem.

📋 Requirements

  • Ruby 3.0.0 or later
  • A C++ compiler with C++20 support
  • Xot and Rucy (declared as runtime dependencies)
  • Platform graphics backend:
    • macOS — AppKit, OpenGL, AVFoundation (bundled with the OS)
    • iOS — UIKit, OpenGL ES, AVFoundation (bundled with the OS)
    • Windows — GDI32, OpenGL32, GLEW (MINGW_PACKAGE_PREFIX-glew)
    • Linuxlibsdl2-dev, libsdl2-ttf-dev, libglew-dev

The following third-party libraries are cloned from GitHub and statically linked while the native extension is being built, so you do not need to install them separately:

Library Role
GLM Vector / matrix math used internally
Clipper Polygon Boolean operations (+, -, &, |, ^)
earcut.hpp Polygon triangulation
splines-lib Curve / spline math
stb (Windows / Linux only) Image file loading

📦 Installation

Add this line to your Gemfile:

gem 'rays'

Then install:

$ bundle install

Or install it directly:

$ gem install rays

require 'rays' automatically calls Rays.init! and registers Rays.fin! at exit. Set $RAYS_NOAUTOINIT = true before requiring if you want to manage the lifetime yourself.

Rays needs a current OpenGL context. When used through Reflex, the window creates and binds a context for you. To use Rays standalone for off-screen rendering, it allocates a hidden context automatically.

📚 What's Included

Geometry and color types

Class Purpose
Rays::Point 2D / 3D point with arithmetic operators
Rays::Bounds Axis-aligned rectangle (position + size)
Rays::Color RGBA color in floating-point components
Rays::ColorSpace Pixel format / color space descriptor (RGBA, ARGB, GRAY, ...)
Rays::Matrix 4×4 transformation matrix

Drawing primitives

Class Purpose
Rays::Polyline A single open or closed polyline; expandable into a stroked polygon
Rays::Polygon One or more polylines forming a closed shape; supports Boolean ops via +, -, &, |, ^
Rays::Image A renderable texture with an associated Painter for off-screen drawing
Rays::Bitmap CPU-side pixel buffer that can be uploaded to / downloaded from an Image
Rays::Font Text rendering — created from a system font name and a size
Rays::Shader GLSL fragment / vertex shader with set_uniform / uniform for parameters
Rays::Camera Live camera capture (per platform) rendered into an Image

The Painter

Rays::Painter is the immediate-mode drawing surface. Obtain one through Image#painter, then between painter.paint do |p| ... end (or begin_paint / end_paint) issue draw calls:

  • Shapespoint, line, rect, ellipse, curve, bezier, triangle, quad, polygon
  • Bitmaps & textimage, text
  • Statefill, stroke, background, stroke_width, stroke_cap, stroke_join, blend_mode, clip, font, texture, shader
  • Transformstranslate, scale, rotate, set_matrix
  • Push / poppush(:state, :matrix) / pop, or the block form push(fill: ..., stroke: ...) { ... }

stroke_cap, stroke_join, blend_mode, texcoord_mode, texcoord_wrap accept symbols (e.g. :round, :miter, :multiply).

Top-level helpers

  • Rays.init! / Rays.fin! — explicit lifecycle (called automatically on require)
  • Rays::Image.load(path) / Rays::Image#save(path) — image file I/O
  • Rays.renderer_info — debug info about the current OpenGL context

💡 Usage

Draw to an off-screen image and save it

require 'rays'

image = Rays::Image.new(200, 200)
image.paint do |p|
  p.background 0, 0, 0
  p.fill   1, 0.4, 0.1     # orange-ish
  p.stroke 1, 1, 1
  p.stroke_width 4

  p.rect 20, 20, 160, 160, round: 16
  p.ellipse 100, 100, 60, 60
end

image.save 'out.png'

Compose a polygon from Boolean operations

require 'rays'

a = Rays::Polygon.rect(0, 0, 100, 100)
b = Rays::Polygon.ellipse(50, 50, 80, 80)

union        = a | b   # outer outline of both
difference   = a - b   # rectangle with circular bite
intersection = a & b   # lens-shaped overlap
xor          = a ^ b   # everything except the overlap

Rays::Image.new(140, 140).paint {|p| p.fill 1; p.polygon difference }.save 'diff.png'

(The C++ operator names map to Ruby's |, -, &, ^.)

Use a fragment shader

require 'rays'

invert = Rays::Shader.new <<~GLSL
  uniform sampler2D texture;
  varying vec4 vTexCoord;
  void main() {
    vec4 c = texture2D(texture, vTexCoord.xy);
    gl_FragColor = vec4(1.0 - c.rgb, c.a);
  }
GLSL

src = Rays::Image.load('photo.png')
out = Rays::Image.new(src.width, src.height)
out.paint do |p|
  p.shader = invert
  p.image src
end
out.save 'photo-invert.png'

Drive push / pop with attributes

image.paint do |p|
  p.push(:state, :matrix, fill: [1, 0, 0]) do
    p.translate 50, 50
    p.rotate 30
    p.rect -20, -20, 40, 40
  end
  # fill, state, and matrix are restored here
end

🛠️ Development

$ rake vendor   # clone the external libraries into vendor/
$ rake lib      # build the native C++ library (librays)
$ rake ext      # build the Ruby C extension
$ rake test     # run the test suite
$ rake doc      # generate RDoc from C++ sources
$ rake          # default: builds the extension

The drawing tests render into off-screen images and compare pixels, so they require a working OpenGL context. The test_rays_init.rb test must run in its own process and is listed in TESTS_ALONE.

In the xord/all monorepo you can scope by module, e.g. rake rays test.

📜 License

Rays is licensed under the MIT License. See the LICENSE file for details.

The third-party libraries listed above retain their own licenses (all MIT-compatible: MIT, ISC, Boost-1.0, dual-licensed MIT/Happy-Bunny for GLM, Unlicense for splines-lib).