Revisiting the Handy library in JRubyArt

The handy library is very useful if you like that hand-drawn look:-
Here is a JRubyArt sketch using the handy library

load_library :handy
java_import 'org.gicentre.handy.HandyRenderer'
BALL_NUM = 6
BALL_COLORS = [[255, 0, 0], [255, 255, 0], [64, 64, 255]].freeze

attr_reader :handy, :balls

def settings
  size(400, 400)
end

def setup
  sketch_title 'Handy Ball Collision'
  HandyRenderer.__persistent__ = true # supress singleton warning 
  @handy = HandyRenderer.new(self)
  @balls = (0...BALL_NUM).map { |idx| Ball.new(self, idx) }
end

def draw
  background(234, 215, 182)
  fill(0, 255, 0)
  handy.instance_eval do
    rect(20, 20, 360, 20)
    rect(20, 360, 360, 20)
    rect(20, 40, 20, 320)
    rect(360, 40, 20, 320)
  end
  balls.each(&:draw)
end

# Bouncing ball
class Ball
  attr_reader :sketch, :position, :delta, :minimum, :maximum

  def initialize(sketch, id)
    @sketch = sketch
    @position = Vec2D.new rand(100..300), rand(100..300)
    @delta = Vec2D.new rand(-6..6), rand(-6..6)
    @size = rand(60..100)
    @radius = @size / 2.0
    @color = BALL_COLORS[id % BALL_COLORS.size]
    @minimum = 40 + @radius
    @maximum = 360 - @radius
  end

  def draw
    @position += delta
    if position.x < minimum
      position.x = minimum
      delta.x *= -1
    end
    if position.x > maximum
      position.x = maximum
      delta.x *= -1
    end
    if position.y < minimum
      position.y = minimum
      delta.y *= -1
    end
    if position.y > maximum
      position.y = maximum
      delta.y *= -0.99
    end
    sketch.fill(*@color)
    sketch.handy.ellipse(position.x, position.y, @size, @size)
  end
end

003

3 Likes

Yes, how can you apply that style to an image or photograph?
Thanks your help !

With difficulty I would say, you could perhaps start with applying an edge detecting glsl shader:-

#ifdef GL_ES
precision highp float;
#endif

uniform sampler2D texture; // iChannel0 in Shadertoy
uniform vec2 sketchSize; // iResolution in Shadertoy

#define EDGE_FUNC edge

//options are KAYYALI_NESW, KAYYALI_SENW, PREWITT, ROBERTSCROSS, SCHARR, or SOBEL
#define SOBEL

// Use these parameters to fiddle with settings
#ifdef SCHARR
#define STEP 0.15
#else
#define STEP 1.0
#endif


#ifdef KAYYALI_NESW
const mat3 kayyali_NESW = mat3(-6.0, 0.0, 6.0,
                               0.0, 0.0, 0.0,
                               6.0, 0.0, -6.0);
#endif
#ifdef KAYYALI_SENW
const mat3 kayyali_SENW = mat3(6.0, 0.0, -6.0,
                               0.0, 0.0, 0.0,
                               -6.0, 0.0, 6.0);
#endif
#ifdef PREWITT
// Prewitt masks (see http://en.wikipedia.org/wiki/Prewitt_operator)
const mat3 prewittKernelX = mat3(-1.0, 0.0, 1.0,
                                 -1.0, 0.0, 1.0,
                                 -1.0, 0.0, 1.0);

const mat3 prewittKernelY = mat3(1.0, 1.0, 1.0,
                                 0.0, 0.0, 0.0,
                                 -1.0, -1.0, -1.0);
#endif
#ifdef ROBERTSCROSS
// Roberts Cross masks (see http://en.wikipedia.org/wiki/Roberts_cross)
const mat3 robertsCrossKernelX = mat3(1.0, 0.0, 0.0,
                                      0.0, -1.0, 0.0,
                                      0.0, 0.0, 0.0);
const mat3 robertsCrossKernelY = mat3(0.0, 1.0, 0.0,
                                      -1.0, 0.0, 0.0,
                                      0.0, 0.0, 0.0);
#endif
#ifdef SCHARR
// Scharr masks (see http://en.wikipedia.org/wiki/Sobel_operator#Alternative_operators)
const mat3 scharrKernelX = mat3(3.0, 10.0, 3.0,
                                0.0, 0.0, 0.0,
                                -3.0, -10.0, -3.0);

const mat3 scharrKernelY = mat3(3.0, 0.0, -3.0,
                                10.0, 0.0, -10.0,
                                3.0, 0.0, -3.0);
#endif
#ifdef SOBEL
// Sobel masks (see http://en.wikipedia.org/wiki/Sobel_operator)
const mat3 sobelKernelX = mat3(1.0, 0.0, -1.0,
                               2.0, 0.0, -2.0,
                               1.0, 0.0, -1.0);

const mat3 sobelKernelY = mat3(-1.0, -2.0, -1.0,
                               0.0, 0.0, 0.0,
                               1.0, 2.0, 1.0);
#endif

//performs a convolution on an image with the given kernel
float convolve(mat3 kernel, mat3 image) {
    float result = 0.0;
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
            result += kernel[i][j]*image[i][j];
        }
    }
    return result;
}

//helper function for colorEdge()
float convolveComponent(mat3 kernelX, mat3 kernelY, mat3 image) {
    vec2 result;
    result.x = convolve(kernelX, image);
    result.y = convolve(kernelY, image);
    return clamp(length(result), 0.0, 255.0);
}

//returns color edges using the separated color components for the measure of intensity
//for each color component instead of using the same intensity for all three.  This results
//in false color edges when transitioning from one color to another, but true colors when
//the transition is from black to color (or color to black).
vec4 colorEdge(float stepx, float stepy, vec2 center, mat3 kernelX, mat3 kernelY) {
    //get samples around pixel
    vec4 colors[9];
    colors[0] = texture2D(texture,center + vec2(-stepx,stepy));
    colors[1] = texture2D(texture,center + vec2(0,stepy));
    colors[2] = texture2D(texture,center + vec2(stepx,stepy));
    colors[3] = texture2D(texture,center + vec2(-stepx,0));
    colors[4] = texture2D(texture,center);
    colors[5] = texture2D(texture,center + vec2(stepx,0));
    colors[6] = texture2D(texture,center + vec2(-stepx,-stepy));
    colors[7] = texture2D(texture,center + vec2(0,-stepy));
    colors[8] = texture2D(texture,center + vec2(stepx,-stepy));
    
    mat3 imageR, imageG, imageB, imageA;
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
            imageR[i][j] = colors[i*3+j].r;
            imageG[i][j] = colors[i*3+j].g;
            imageB[i][j] = colors[i*3+j].b;
            imageA[i][j] = colors[i*3+j].a;
        }
    }
    
    vec4 color;
    color.r = convolveComponent(kernelX, kernelY, imageR);
    color.g = convolveComponent(kernelX, kernelY, imageG);
    color.b = convolveComponent(kernelX, kernelY, imageB);
    color.a = convolveComponent(kernelX, kernelY, imageA);
    
    return color;
}

//finds edges where fragment intensity changes from a higher value to a lower one (or
//vice versa).
vec4 edge(float stepx, float stepy, vec2 center, mat3 kernelX, mat3 kernelY){
    // get samples around pixel
    mat3 image = mat3(length(texture2D(texture,center + vec2(-stepx,stepy)).rgb),
                      length(texture2D(texture,center + vec2(0,stepy)).rgb),
                      length(texture2D(texture,center + vec2(stepx,stepy)).rgb),
                      length(texture2D(texture,center + vec2(-stepx,0)).rgb),
                      length(texture2D(texture,center).rgb),
                      length(texture2D(texture,center + vec2(stepx,0)).rgb),
                      length(texture2D(texture,center + vec2(-stepx,-stepy)).rgb),
                      length(texture2D(texture,center + vec2(0,-stepy)).rgb),
                      length(texture2D(texture,center + vec2(stepx,-stepy)).rgb));
    vec2 result;
    result.x = convolve(kernelX, image);
    result.y = convolve(kernelY, image);
    
    float color = clamp(length(result), 0.0, 255.0);
    return vec4(color);
}

//Colors edges using the actual color for the fragment at this location
vec4 trueColorEdge(float stepx, float stepy, vec2 center, mat3 kernelX, mat3 kernelY) {
    vec4 edgeVal = edge(stepx, stepy, center, kernelX, kernelY);
    return edgeVal * texture2D(texture,center);
}

void main( void ){
    vec2 uv = gl_FragCoord.xy / sketchSize.xy;
    vec4 color = texture2D(texture, uv.xy);
#ifdef KAYYALI_NESW
    gl_FragColor = EDGE_FUNC(STEP/sketchSize[0], STEP/sketchSize[1],
                            uv,
                            kayyali_NESW, kayyali_NESW);
#endif
#ifdef KAYYALI_SENW
    gl_FragColor = EDGE_FUNC(STEP/sketchSize[0], STEP/sketchSize[1],
                            uv,
                            kayyali_SENW, kayyali_SENW);
#endif
#ifdef PREWITT
    gl_FragColor = EDGE_FUNC(STEP/sketchSize[0], STEP/sketchSize[1],
                            uv,
                            prewittKernelX, prewittKernelY);
#endif
#ifdef ROBERTSCROSS
    gl_FragColor = EDGE_FUNC(STEP/sketchSize[0], STEP/sketchSize[1],
                            uv,
                            robertsCrossKernelX, robertsCrossKernelY);
#endif
#ifdef SOBEL
    gl_FragColor = EDGE_FUNC(STEP/sketchSize[0], STEP/sketchSize[1],
                            uv,
                            sobelKernelX, sobelKernelY);
#endif
#ifdef SCHARR
    gl_FragColor = EDGE_FUNC(STEP/sketchSize[0], STEP/sketchSize[1],
                            uv,
                            scharrKernelX, scharrKernelY);
#endif
}

JRubyArt sketch doing video capture:-

load_library :video, :video_event
include_package 'processing.video'
attr_reader :cam, :my_shader

def setup
  sketch_title 'Edge Detect Capture'
  @my_shader = load_shader(data_path('edge_detect.glsl'))
  # @my_shader = load_shader('edge_detect.glsl') # require --nojruby flag
  # @my_shader = load_shader(File.absolute_path('data/edge_detect.glsl')) # full data path
  my_shader.set('sketchSize', width.to_f, height.to_f)
  start_capture(width, height)
end

def start_capture(w, h)
  @cam = Capture.new(self, w, h)
  cam.start
end

def draw
  image(cam, 0, 0)
  return if mouse_pressed?
  filter(my_shader)
end

def captureEvent(c)
  c.read
end

def settings
  size(640, 480, P2D)
end

filter

What a Great response !!!
—But I don’t understand anything, sorry, …
I’m just a Processing user but not a JRubyArt user.
(is this very difficult to understand?JRubyArt… for high levels?)
But what a great beauty of images they come out!
Regards

Is it possible to pass that to Processing language?

See Filters4Processing also ImageProcessing_Filter sketch using Thomas Diewald PixelFlow library for some more inspiration.

Yes but start with a good image and you can do great stuff with Gimp (or photoshop if you must)!
Here’s my Grandfather with a corn stoop:-

There’s more interesting things you can with processing like combining it with cfdg to produce images like this Che Che.

1 Like