Personal JRubyArt blog
Monkstone
Ruby-Processing group blog
Ruby Processing Group
Experiments with ruby-processing (processing-2.2.1) and JRubyArt for processing-3.0
# fraction_sums.rb, by Martin Prout attr_reader :f, :two, :three, :four, :five, :six def setup sketch_title 'Math Blackboard' @f = createFont('Arial', 24, true) half = 1 / 2r # since ruby 2.1.0 (and jruby-9.0.0.0) third = 1 / 3r quarter = 1 / 4r fifth = 1 / 5r sixth = 1 / 6r seventh = 1 / 7r format2 = '%s + %s = %s' format3 = '%s + %s + %s = %s' format4 = '%s + %s + %s + %s = %s' format5 = '%s + %s + %s + %s + %s = %s' format6 = '%s + %s + %s + %s + %s + %s = %s' sum2 = half + third result2 = numeric_to_mixed_number(sum2) @two = format(format2, half, third, result2) sum3 = half + third + quarter result3 = numeric_to_mixed_number(sum3) @three = format(format3, half, third, quarter, result3) sum4 = half + third + quarter + fifth result4 = numeric_to_mixed_number(sum4) @four = format(format4, half, third, quarter, fifth, result4) sum5 = half + third + quarter + fifth + sixth result5 = numeric_to_mixed_number(sum5) @five = format(format5, half, third, quarter, fifth, sixth, result5) sum6 = half + third + quarter + fifth + sixth + seventh result6 = numeric_to_mixed_number(sum6) @six = format(format6, half, third, quarter, fifth, sixth, seventh, result6) end def draw background 10 text_font(f, 24) fill(220) text('Math Blackboard JRubyArt', 110, 50) text(two, 100, 80) text(three, 100, 105) text(four, 100, 130) text(five, 100, 155) text(six, 100, 180) end def numeric_to_mixed_number(amount) amount_as_integer = amount.to_i if (amount_as_integer != amount.to_f) && (amount_as_integer > 0) fraction = amount - amount_as_integer format('%s %s', amount_as_integer, fraction) else amount.to_s end end def settings size 640, 250 smooth 4 end
# ruby-processing sketch # http://qiita.com/norioc/items/99514cb659aad03ab7d7 # http://aidiary.hatenablog.com/entry/20050402/1251514618 N = 15 attr_reader :rows, :cols def setup sketch_title 'Bresenham`s Algorithm' @rows = height / N @cols = width / N end def settings size 400, 400 end def build_line(from:, to:) next_x = from.x next_y = from.y delta_x = to.x - from.x delta_y = to.y - from.y step_x = delta_x < 0 ? -1 : 1 step_y = delta_y < 0 ? -1 : 1 delta_x = (delta_x * 2).abs delta_y = (delta_y * 2).abs b_line = Array.new(1, Vec2D.new(next_x, next_y)) if delta_x > delta_y fraction = delta_y - delta_x / 2 while next_x != to.x if fraction >= 0 next_y += step_y fraction -= delta_x end next_x += step_x fraction += delta_y b_line << Vec2D.new(next_x, next_y) end else fraction = delta_x - delta_y / 2 while next_y != to.y if fraction >= 0 next_x += step_x fraction -= delta_y end next_y += step_y fraction += delta_x b_line << Vec2D.new(next_x, next_y) end end b_line end def draw background 255 translate width / 2, height / 2 rect_mode CENTER no_fill stroke 60 (0..rows).each do |i| (0..cols).each do |j| rect((j - cols / 2) * N, (i - rows / 2) * N, N, N) end end x = mouse_x - width / 2 + N / 2 y = mouse_y - height / 2 + N / 2 b_line = build_line(from: Vec2D.new, to: Vec2D.new(x / N, y / N)) unless b_line.empty? fill color('#C7B097') b_line.each { |v| rect(v.x * N, v.y * N, N, N) } end stroke color('#0000FF') stroke_width 2 line 0, 0, mouse_x - width / 2, mouse_y - height / 2 stroke 0 stroke_width 1 end
def settings size(400, 400) end def setup sketch_title 'Resizable Surface' surface.set_resizable(true) end def draw background(255) stroke_weight 4 line(100, 100, width - 100, height - 100) end def key_pressed surface.set_size(rand(200..500).floor, rand(200..500).floor) end
def settings size(400, 400) end def setup sketch_title 'Resizable Surface' resizable(true) end def draw background(255) stroke_weight 4 line(100, 100, width - 100, height - 100) end def key_pressed sketch_size(rand(200..500).floor, rand(200..500).floor) endOK a bit more experimentation also works with FX2D, blows up with P2D, but that's the same with vanilla processing (threading error).
# Earth model with bump mapping, specular texture and dynamic cloud layer. # Adapted from the THREE.js tutorial to processing by Andres Colubri, # translated to JRubyArt by Martin Prout: # http://learningthreejs.com/blog/2013/09/16/how-to-make-the-earth-in-webgl/ attr_reader :earth, :clouds, :earth_shader, :cloud_shader, :earth_rotation attr_reader :clouds_rotation, :target_angle def setup sketch_title 'Blue Marble' @earth_rotation = 0 @clouds_rotation = 0 earth_tex = load_image('earthmap1k.jpg') cloud_tex = load_image('earthcloudmap.jpg') alpha_tex = load_image('earthcloudmaptrans.jpg') bump_map = load_image('earthbump1k.jpg') spec_map = load_image('earthspec1k.jpg') @earth_shader = load_shader('EarthFrag.glsl', 'EarthVert.glsl') earth_shader.set('texMap', earth_tex) earth_shader.set('bumpMap', bump_map) earth_shader.set('specularMap', spec_map) earth_shader.set('bumpScale', 0.05) @cloud_shader = load_shader('CloudFrag.glsl', 'CloudVert.glsl') cloud_shader.set('texMap', cloud_tex) cloud_shader.set('alphaMap', alpha_tex) @earth = create_shape(SPHERE, 200) earth.setStroke(false) earth.setSpecular(color(125)) earth.setShininess(10) @clouds = create_shape(SPHERE, 201) clouds.setStroke(false) end def draw background(0) translate(width / 2, height / 2) point_light(255, 255, 255, 300, 0, 500) target_angle = map1d(mouse_x, (0..width), (0..TWO_PI)) @earth_rotation += 0.05 * (target_angle - earth_rotation) shader(earth_shader) push_matrix rotate_y(earth_rotation) shape(earth) pop_matrix shader(cloud_shader) push_matrix rotate_y(earth_rotation + clouds_rotation) shape(clouds) pop_matrix @clouds_rotation += 0.001 end def settings size(600, 600, P3D) end
// Java seven public static void createVec2(final Ruby runtime) { RubyClass vec2Cls = runtime.defineClass("Vec2D", runtime.getObject(), new ObjectAllocator() { @Override public IRubyObject allocate(Ruby runtime, RubyClass rubyClass) { return new Vec2(runtime, rubyClass); } }); vec2Cls.defineAnnotatedMethods(Vec2.class); } // Java eight public static void createVec2(final Ruby runtime) { RubyClass vec2Cls = runtime.defineClass("Vec2D", runtime.getObject(), (Ruby runtime1, RubyClass rubyClass) -> new Vec2(runtime1, rubyClass)); vec2Cls.defineAnnotatedMethods(Vec2.class); }Update 22 August 2015 just released JRubyArt 0.5.0 featuring java8lambda in production...
# Axis aligned bounding box class (AABB would clash with Toxicgem) class AaBb attr_reader :center, :extent def initialize(center:, extent:) @center = center @extent = extent end def self.from_min_max(min:, max:) new(center: (min + max) * 0.5, extent: max - min) end def position(vec) return @center = vec unless block_given? @center = vec if yield end def scale(d) @extent *= d end def contains?(vec) rad = extent * 0.5 return false unless (center.x - rad.x..center.x + rad.x).cover? vec.x (center.y - rad.y..center.y + rad.y).cover? vec.y end end
# Click on the box and drag it across the screen. attr_reader :block, :block_locked, :over_block, :renderer, :bounds BLOCK_WIDTH = 150 def setup sketch_title 'AaBb Example' @block = Block.new( center: Vec2D.new(width / 2, height / 2), size: Vec2D.new(BLOCK_WIDTH, BLOCK_WIDTH)) @locked = false @over_block = false @bounds = AaBb.new( center: Vec2D.new(width / 2, height / 2), extent: Vec2D.new(width - BLOCK_WIDTH, height - BLOCK_WIDTH)) @renderer = AppRender.new(self) end def draw background 0 fill 153 if block.over?(Vec2D.new(mouse_x, mouse_y)) @over_block = true stroke 255 fill 255 if block_locked? else @over_block = false stroke 153 end # Draw the box as a shape begin_shape block.points_array.each { |vec| vec.to_vertex(renderer) } end_shape(CLOSE) end def block_locked? block_locked end def over_block? over_block end def mouse_pressed if over_block? @block_locked = true fill 255 else @block_locked = false end end def mouse_dragged return unless block_locked? position = Vec2D.new(mouse_x, mouse_y) block.new_position(position) { bounds.contains? position } end def mouse_released @block_locked = false end def settings size 640, 360 smooth 4 end # Use class to contain block behaviour class Block attr_reader :aabb def initialize(center:, size:) @aabb = AaBb.new(center: center, extent: size) end def new_position(center, &block) @aabb.position(center.dup, &block) end def over?(vec) aabb.contains? vec end # use for shape def points_array a = aabb.center - aabb.extent * 0.5 c = aabb.center + aabb.extent * 0.5 b = Vec2D.new(c.x, a.y) d = Vec2D.new(a.x, c.y) [a, b, c, d] end # use for rect def points [aabb.center, aabb.extent] end end
# We extend the language of conditionals by adding the # keyword "elsif". This allows conditionals to ask # two or more sequential questions, each with a different # action. def setup background 0 2.step(by: 2, to: width - 2) do |i| # If 'i' divides by 20 with no remainder # draw the first line.. # else if 'i' divides by 10 with no remainder # draw second line, else draw third line if (i % 20) == 0 stroke 255 line i, 80, i, height / 2 elsif (i % 10) == 0 stroke 153 line i, 20, i, 180 else stroke 102 line i, height / 2, i, height - 20 end end end def settings size 640, 360 end
# An pry shell for live coding. # Will start with your sketch. require_relative 'base' Processing.load_and_run_sketch ARGV.clear # So that pry doesn't try to load them as files. require 'pry' $app.pry
class Fred < Processing::App def setup sketch_title 'Fred' end def draw end def settings size 300, 300, FX2D # smooth # here end end
# experimental ArcBall class class ArcBall attr_reader :applet, :x, :y, :r def initialize(app:, center_x: nil, center_y: nil, radius: nil) @applet = app @x = (center_x.nil?) ? app.width * 0.5 : center_x @y = (center_y.nil?) ? app.height * 0.5 : center_y @r = (radius.nil?) ? app.width * 0.8 : radius end def to_s format('ArcBall.new(applet: %s, centre_x: %d, centre_y: %d, radius: %d)', applet, x, y, r) end end Applet = Struct.new(:width, :height) do def to_s 'MyApplet' end end applet = Applet.new(800, 600) puts ArcBall.new(app: applet).to_s puts ArcBall.new(app: applet, center_x: 300, center_y: 200, radius: 800).to_s
ArcBall.new(applet: MyApplet, centre_x: 400, centre_y: 300, radius: 640) ArcBall.new(applet: MyApplet, centre_x: 300, centre_y: 200, radius: 800)
load_library :arcball def setup sketch_title 'Arcball Box' ArcBall.new(app: self) fill 180 end def draw background 50 box 300, 300, 300 end def settings size 600, 600, P3D smooth 8 end # experimental ArcBall class class ArcBall include_package 'arcball' attr_reader :applet, :x, :y, :r def initialize(app:, center_x: nil, center_y: nil, radius: nil) @applet = app @x = (center_x.nil?) ? app.width * 0.5 : center_x @y = (center_y.nil?) ? app.height * 0.5 : center_y @r = (radius.nil?) ? app.width * 0.8 : radius app = JarcBall.new applet.to_java, x.to_java(:float), y.to_java(:float), r.to_java(:float) app.set_active true end def to_s format('ArcBall.new(applet: %s, centre_x: %d, centre_y: %d, radius: %d)', applet, x, y, r) end end
load_library :hemesh # Module used to include java packages module MS include_package 'wblut.math' include_package 'wblut.processing' include_package 'wblut.hemesh' include_package 'wblut.geom' end NUM = 50 attr_reader :mesh, :render, :points def setup sketch_title 'Sponge' ArcBall.init(self) create_mesh @render = MS::WB_Render3D.new(self) end def draw background(255) directional_light(255, 255, 255, 1, 1, -1) directional_light(127, 127, 127, -1, -1, 1) stroke(0) render.draw_edges(mesh) no_stroke render.draw_faces(mesh) end def mouse_pressed create_mesh end def create_mesh rs = MS::WB_RandomOnSphere.new creator = MS::HEC_ConvexHull.new @points = (0..NUM).map { rs.next_point.mul_self(300.0) } creator.set_points(points) creator.setN(NUM) @mesh = MS::HE_Mesh.new(creator) @mesh = MS::HE_Mesh.new(MS::HEC_Dual.new(mesh)) ext = MS::HEM_Extrude.new.set_chamfer(25).set_relative(false) mesh.modify(ext) sel = ext.extruded ext = MS::HEM_Extrude.new.set_distance(-40) mesh.modify_selected(ext, sel) mesh.smooth(2) end def settings size(800, 800, P3D) smooth(8) end
jruby -Djava.library.path="/home/tux/lib/linux64" trefoil.rbI needed to change the build to include the lwjgl jars and app.rb to accommodate the changes to processing but also unlike vanilla processing the sketch was also modified, replacing the applet convention of size(width, height, renderer) in setup with 3 methods (not required to be called in setup):-
# Old def setup size(1024, 768, P3D) end #New def sketch_width 1024 end def sketch_height 768 end def sketch_renderer P3D end
def setup @num_chars = 26 @key_scale = 200.0 / @num_chars - 1.0 @rect_width = width / 4 no_stroke background 0 end def draw return unless key_pressed? && ('A'..'z').include?(key) key_index = key.ord - (key <= 'Z' ? 'A'.ord : 'a'.ord) fill millis % 255 begin_rect = map1d(key_index, (0..25), (0..width - @rect_width)) rect begin_rect, 0, @rect_width, height end def sketch_width; 640; end def sketch_height; 360; end def sketch_renderer; JAVA2D; end
require 'jruby_art' require 'toxiclibs' require_relative 'cluster' require_relative 'node' # # Copyright (c) 2010 Daniel Shiffman # # This demo & library is free software you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation either # version 2.1 of the License, or (at your option) any later version. # # http://creativecommons.org/licenses/LGPL/2.1/ # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA class ForceDirectedGraph < Processing::App attr_reader :physics, :clusters, :show_physics, :show_particles, :f def setup size(640, 360) @f = create_font('Georgia', 12, true) @show_physics = true @show_particles = true # Initialize the physics @physics = Physics::VerletPhysics2D.new physics.set_world_bounds(Toxi::Rect.new(10, 10, width - 20, height - 20)) # Spawn a new random graph new_graph end # Spawn a new random graph def new_graph # Clear physics physics.clear center = TVec2D.new(width / 2, height / 2) @clusters = (0..8).map { Cluster.new(physics, rand(3..8), rand(20..100), center) } # All clusters connect to all clusters clusters.each_with_index do |ci, i| clusters[i + 1..clusters.size - 1].each do |cj| ci.connect(cj) end end end def draw # Update the physics world physics.update background(255) # Display all points clusters.each(&:display) if show_particles # If we want to see the physics if show_physics clusters.each_with_index do |ci, i| ci.internal_connections self # Cluster connections to other clusters clusters[1 + i..clusters.size - 1].each do |cj| ci.show_connections(self, cj) end end end # Instructions fill(0) text_font(f) text("'p' to display or hide particles\n'c' to display or hide connections\n'n' for new graph", 10, 20) end # Key press commands def key_pressed case key when 'c' @show_physics = !show_physics @show_particles = true unless show_physics when 'p' @show_particles = !show_particles @show_physics = true unless show_particles when 'n' new_graph end end end ForceDirectedGraph.new(title: 'Force directed graphs')cluster.rb
# # Copyright (c) 2010 Daniel Shiffman # # This demo & library is free software you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation either # version 2.1 of the License, or (at your option) any later version. # # http://creativecommons.org/licenses/LGPL/2.1/ # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA class Cluster attr_reader :nodes, :diameter, :physics # We initialize a with a number of nodes, a diameter, and centerpoint def initialize(physics, n, d, center) @physics = physics @diameter = d @nodes = (0..n).map { Node.new(center.add(TVec2D.random_vector)) } # Connect all the nodes with a Spring nodes[1..nodes.size - 1].each_with_index do |pi, i| nodes[0..i].each do |pj| physics.add_spring(Physics::VerletSpring2D.new(pi, pj, diameter, 0.01)) end end end def display nodes.each(&:display) end # This functons connects one cluster to another # Each point of one cluster connects to each point of the other cluster # The connection is a "VerletMinDistanceSpring" # A VerletMinDistanceSpring is a spring which only enforces its rest length if the # current distance is less than its rest length. This is handy if you just want to # ensure objects are at least a certain distance from each other, but don't # care if it's bigger than the enforced minimum. def connect(other) other_nodes = other.nodes nodes.each do |pi| other_nodes.each do |pj| physics.add_spring(Physics::VerletMinDistanceSpring2D.new(pi, pj, (diameter + other.diameter) * 0.5, 0.05)) end end end # Draw all the internal connections def internal_connections(app) app.stroke(200, 0, 0, 80) nodes[0..nodes.size - 1].each_with_index do |pi, i| nodes[i + 1..nodes.size - 1].each do |pj| app.line(pi.x, pi.y, pj.x, pj.y) end end end # Draw all the connections between this and another Cluster def show_connections(app, other) app.stroke(200, 200, 0, 20) app.stroke_weight(2) other_nodes = other.nodes nodes.each do |pi| other_nodes[0..other_nodes.size - 1].each do |pj| app.line(pi.x, pi.y, pj.x, pj.y) end end end end
require 'forwardable' # The Nature of Code # Daniel Shiffman # http://natureofcode.com # Force directed graph # Heavily based on: http://code.google.com/p/fidgen/ # Notice how we are using inheritance here! # We could have just stored a reference to a VerletParticle object # inside the Node class, but inheritance is a nice alternative class Node < Physics::VerletParticle2D extend Forwardable def_delegators(:@app, :fill, :stroke, :stroke_weight, :ellipse) def initialize(pos) super(pos) @app = $app end # All we're doing really is adding a display function to a VerletParticle def display fill(50, 200, 200, 150) stroke(50, 200, 200) stroke_weight(2) ellipse(x, y, 16, 16) end end
# # This example implements a custom VolumetricSpace uMath.sing an implicit function # to calculate each voxel. This is slower than the default array or HashMap # based implementations, but also has much less memory requirements and so might # be an interesting and more viable approach for very highres voxel spaces # (e.g. >32 million voxels). This implementation here also demonstrates how to # achieve an upper boundary on the iso value (in addition to the one given and # acting as lower threshold when computing the iso surface) # # Usage: # drag mouse to rotate camera # mouse wheel zoom in/out # l: apply laplacian mesh smooth # # # # Copyright (c) 2010 Karsten Schmidt & ruby-procesMath.sing version Martin Prout 2013 # This sketch relies on a custom ruby-procesMath.sing mesh_to_vbo library # # This library is free software you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation either # version 2.1 of the License, or (at your option) any later version. # # http://creativecommons.org/licenses/LGPL/2.1/ # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # require 'toxiclibs' load_library :mesh_to_vbo RES = 64 ISO = 0.2 MAX_ISO = 0.66 attr_reader :mesh, :vbo, :curr_zoom, :implicit def setup size(720,720, P3D) Processing::ArcBall.init(self) @vbo = MeshToVBO.new(self) @curr_zoom = 1 vol = EvaluatingVolume.new(TVec3D.new(400,400,400), RES, RES, RES, MAX_ISO) surface = Volume::HashIsoSurface.new(vol) @mesh = Toxi::WETriangleMesh.new surface.compute_surface_mesh(mesh, ISO) @is_wire_frame = false no_stroke @implicit = vbo.meshToVBO(mesh, true) implicit.setFill(color(222, 222, 222)) implicit.setAmbient(color(50, 50, 50)) implicit.setShininess(color(10, 10, 10)) implicit.setSpecular(color(50, 50, 50)) end def draw background(0) lights define_lights shape(implicit) end def key_pressed case key when 'l', 'L' Toxi::LaplacianSmooth.new.filter(mesh, 1) @implicit = vbo.meshToVBO(mesh, true) # new mesh so need to set finish implicit.setFill(color(222, 222, 222)) implicit.setAmbient(color(50, 50, 50)) implicit.setShininess(color(10, 10, 10)) implicit.setSpecular(color(50, 50, 50)) when 's', 'S' save_frame("implicit.png") end end def define_lights ambient_light(50, 50, 50) point_light(30, 30, 30, 200, -150, 0) directional_light(0, 30, 50, 1, 0, 0) spot_light(30, 30, 30, 0, 40, 200, 0, -0.5, -0.5, PI / 2, 2) end class EvaluatingVolume < Volume::VolumetricSpace attr_reader :upper_bound, :lut FREQ = Math::PI * 3.8 def initialize(scal_vec, resX, resY, resZ, upper_limit) super(scal_vec, resX, resY, resZ) @upper_bound = upper_limit end def clear # nothing to do here end def getVoxelAt(i) getVoxel(i % resX, (i % sliceRes) / resX, i / sliceRes) end def getVoxel(x, y, z) # can't overload so we renamed val = 0 if (x > 0 && x < resX1 && y > 0 && y < resY1 && z > 0 && z < resZ1) xx = x * 1.0 / resX - 0.5 # NB: careful about integer division !!! yy = y * 1.0 / resY - 0.5 zz = z * 1.0 / resZ - 0.5 #val = Math.sin(xx * FREQ) + Math.cos(yy * FREQ) + Math.sin(zz * FREQ) val = Math.cos(xx * FREQ) * Math.sin(yy* FREQ) + Math.cos(yy* FREQ) * Math.sin(zz* FREQ) + Math.cos(zz* FREQ)* Math.sin(xx* FREQ) if (val > upper_bound) val = 0 end end return val end end
############################################ # mesh_to_vbo.rb # a ruby library to convert toxi.mesh object # to vbo (PShape) written by Martin Prout ############################################ class MeshToVBO PShape = Java::ProcessingCore::PShape attr_reader :parent def initialize(parent) @parent = parent end def meshToVBO(mesh, smth) retained = parent.create_shape retained.begin_shape(PShape::TRIANGLES) if smth mesh.compute_vertex_normals mesh.getFaces.each do |f| retained.normal(f.a.normal.x, f.a.normal.y, f.a.normal.z) retained.vertex(f.a.x, f.a.y, f.a.z) retained.normal(f.b.normal.x, f.b.normal.y, f.b.normal.z) retained.vertex(f.b.x, f.b.y, f.b.z) retained.normal(f.c.normal.x, f.c.normal.y, f.c.normal.z) retained.vertex(f.c.x, f.c.y, f.c.z) end else mesh.get_faces.each do |f| retained.normal(f.normal.x, f.normal.y, f.normal.z) retained.vertex(f.a.x, f.a.y, f.a.z) retained.vertex(f.b.x, f.b.y, f.b.z) retained.vertex(f.c.x, f.c.y, f.c.z) end end retained.end_shape retained end # variant # input array of meshes, output an array of shapes def meshToRetained(mesh, smth) mesh.map { |m| meshToVBO(m, smth) } end end
# A ruby processing sketch (needs re-factoring for jruby_art) # # # This example implements a custom VolumetricSpace using an implicit function # to calculate each voxel. This is slower than the default array or HashMap # based implementations, but also has much less memory requirements and so might # be an interesting and more viable approach for very highres voxel spaces # (e.g. >32 million voxels). This implementation here also demonstrates how to # achieve an upper boundary on the iso value (in addition to the one given and # acting as lower threshold when computing the iso surface) # # Usage: # drag mouse to rotate camera # w: toggle wireframe on/off # mouse wheel to zoom in/out # l: apply laplacian mesh smooth # # # # Copyright (c) 2010 Karsten Schmidt & ruby-processing version Martin Prout 2012 # This sketch relies on a custom toxiclibscore library for PovRAY export # # This library is free software you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation either # version 2.1 of the License, or (at your option) any later version. # # http://creativecommons.org/licenses/LGPL/2.1/ # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # require 'toxiclibs' load_library 'vecmath' # uncomment this line for ruby-processing RES = 64 ISO = 0.2 MAX_ISO = 0.66 attr_reader :mesh, :gfx, :curr_zoom, :is_wire_frame def setup size(720, 720, P3D) ArcBall.init(self) @gfx = Gfx::ToxiclibsSupport.new(self) vol = EvaluatingVolume.new(Toxi::Vec3D.new(400, 400, 400), RES, RES, RES, MAX_ISO) surface = Volume::HashIsoSurface.new(vol) @mesh = Toxi::WETriangleMesh.new surface.compute_surface_mesh(mesh, ISO) @is_wire_frame = false end def draw background(0) if is_wire_frame no_fill stroke(255) else fill(255) no_stroke define_lights lights end @gfx.mesh(mesh, true) end def key_pressed case key when 'w', 'W' @is_wire_frame = !is_wire_frame when 'l', 'L' Toxi::LaplacianSmooth.new.filter(mesh, 1) when 's', 'S' save_frame('implicit.png') end end def define_lights ambient_light(50, 50, 50) point_light(30, 30, 30, 200, -150, 0) directional_light(0, 30, 50, 1, 0, 0) spot_light(30, 30, 30, 0, 40, 200, 0, -0.5, -0.5, PI / 2, 2) end # Creating a volumetric space class # class EvaluatingVolume < Volume::VolumetricSpace include Processing::Proxy attr_reader :upper_bound FREQ = PI * 3.8 def initialize(scal_vec, resX, resY, resZ, upper_limit) super(scal_vec, resX, resY, resZ) @upper_bound = upper_limit end def clear # nothing to do here end def getVoxelAt(i) getVoxel(i % resX, (i % sliceRes) / resX, i / sliceRes) end def getVoxel(x, y, z) # can't overload so we renamed val = 0 if x > 0 && x < resX1 && y > 0 && y < resY1 && z > 0 && z < resZ1 xx = x * 1.0 / resX - 0.5 # NB: careful about integer division !!! yy = y * 1.0 / resY - 0.5 zz = z * 1.0 / resZ - 0.5 val = cos(xx * FREQ) * sin(yy * FREQ) + cos(yy * FREQ) * sin(zz* FREQ) + cos(zz * FREQ) * sin(xx * FREQ) # val = sin(xx * FREQ) + cos(yy * FREQ) + sin(zz * FREQ) # val = sin(xx * FREQ) * (xx * FREQ) + sin(yy * FREQ) * (yy * FREQ) + sin(zz * FREQ) * (zz * FREQ) val = 0 if val > upper_bound end val end end