Class: Ashton::SignedDistanceField
- Inherits:
-
Object
- Object
- Ashton::SignedDistanceField
- Defined in:
- lib/ashton/signed_distance_field.rb
Constant Summary collapse
- ZERO_DISTANCE =
color channel containing 0 => -128, 128 => 0, 129 => 1, 255 => 128
128
Instance Attribute Summary collapse
-
#height ⇒ Object
readonly
Returns the value of attribute height.
-
#width ⇒ Object
readonly
Returns the value of attribute width.
Instance Method Summary collapse
-
#draw(x, y, z, options = {}) ⇒ Object
Draw the field, usually for debugging purposes.
-
#initialize(width, height, max_distance, options = {}, &block) ⇒ SignedDistanceField
constructor
Creates a Signed Distance Field based on a given image.
-
#line_of_sight?(x1, y1, x2, y2) ⇒ Boolean
Does the point x1, x2 have line of sight to x2, y2 (that is, no solid in the way).
-
#line_of_sight_blocked_at(x1, y1, x2, y2) ⇒ Object
Returns blocking position, else nil if line of sight isn’t blocked.
-
#position_clear?(x, y, radius) ⇒ Boolean
Is the position clear for a given radius around it.
-
#render_field ⇒ Object
Update the SDF should the image have changed.
-
#sample_distance(x, y) ⇒ Object
If positive, distance, in pixels, to the nearest opaque pixel.
-
#sample_gradient(x, y) ⇒ Float
Gets the gradient of the field at a given point.
-
#sample_normal(x, y) ⇒ Float
Get the normal at a given point.
-
#to_a ⇒ Array<Array<Integer>>
Convert into a nested array of sample values.
Constructor Details
#initialize(width, height, max_distance, options = {}, &block) ⇒ SignedDistanceField
Creates a Signed Distance Field based on a given image. When drawing into the SDF, drawing should ONLY have alpha of 0 (clear) or 255 (solid)
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
# File 'lib/ashton/signed_distance_field.rb', line 15 def initialize(width, height, max_distance, = {}, &block) = { scale: 1, step_size: 1, }.merge! @width, @height = width, height @scale = [:scale].to_f @shader = Shader.new fragment: :signed_distance_field, uniforms: { max_distance: max_distance.ceil, step_size: [:step_size].floor, # One pixel. texture_size: [width, height].map(&:to_f), } @field = Texture.new (width / @scale).ceil, (height / @scale).ceil @mask = Texture.new @field.width, @field.height if block_given? render_field(&block) else @field.clear color: Gosu::Color.rgb(*([ZERO_DISTANCE + max_distance] * 3)) end end |
Instance Attribute Details
#height ⇒ Object (readonly)
Returns the value of attribute height.
5 6 7 |
# File 'lib/ashton/signed_distance_field.rb', line 5 def height @height end |
#width ⇒ Object (readonly)
Returns the value of attribute width.
5 6 7 |
# File 'lib/ashton/signed_distance_field.rb', line 5 def width @width end |
Instance Method Details
#draw(x, y, z, options = {}) ⇒ Object
Draw the field, usually for debugging purposes.
129 130 131 132 133 134 135 136 137 138 139 |
# File 'lib/ashton/signed_distance_field.rb', line 129 def draw(x, y, z, = {}) = { mode: :add, }.merge! $window.scale @scale do @field.draw x, y, z, end nil end |
#line_of_sight?(x1, y1, x2, y2) ⇒ Boolean
Does the point x1, x2 have line of sight to x2, y2 (that is, no solid in the way).
78 79 80 |
# File 'lib/ashton/signed_distance_field.rb', line 78 def line_of_sight?(x1, y1, x2, y2) !line_of_sight_blocked_at(x1, y1, x2, y2) end |
#line_of_sight_blocked_at(x1, y1, x2, y2) ⇒ Object
Returns blocking position, else nil if line of sight isn’t blocked.
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
# File 'lib/ashton/signed_distance_field.rb', line 83 def line_of_sight_blocked_at(x1, y1, x2, y2) distance_to_travel = Gosu::distance x1, y1, x2, y2 distance_x, distance_y = x2 - x1, y2 - y1 distance_travelled = 0 x, y = x1, y1 loop do distance = sample_distance x, y # Blocked? return [x, y] if distance <= 0 distance_travelled += distance # Got to destination in the clear. return nil if distance_travelled >= distance_to_travel lerp = distance_travelled.fdiv distance_to_travel x = x1 + distance_x * lerp y = y1 + distance_y * lerp end end |
#position_clear?(x, y, radius) ⇒ Boolean
Is the position clear for a given radius around it.
41 42 43 |
# File 'lib/ashton/signed_distance_field.rb', line 41 def position_clear?(x, y, radius) sample_distance(x, y) >= radius end |
#render_field ⇒ Object
Update the SDF should the image have changed. Draw the mask in the passed block.
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 |
# File 'lib/ashton/signed_distance_field.rb', line 108 def render_field raise ArgumentError, "Block required" unless block_given? @mask.render do @mask.clear $window.scale 1.0 / @scale do yield self end end @shader.enable do @field.render do @mask.draw 0, 0, 0 end end nil end |
#sample_distance(x, y) ⇒ Object
If positive, distance, in pixels, to the nearest opaque pixel. If negative, distance in pixels to the nearest transparent pixel.
47 48 49 50 51 52 |
# File 'lib/ashton/signed_distance_field.rb', line 47 def sample_distance(x, y) x = [[x, width - 1].min, 0].max y = [[y, height - 1].min, 0].max # Could be checking any of red/blue/green. @field.red((x / @scale).round, (y / @scale).round) - ZERO_DISTANCE end |
#sample_gradient(x, y) ⇒ Float
Gets the gradient of the field at a given point.
56 57 58 59 60 61 62 63 |
# File 'lib/ashton/signed_distance_field.rb', line 56 def sample_gradient(x, y) d0 = sample_distance x, y - 1 d1 = sample_distance x - 1, y d2 = sample_distance x + 1, y d3 = sample_distance x, y + 1 [(d2 - d1) / @scale, (d3 - d0) / @scale] end |
#sample_normal(x, y) ⇒ Float
Get the normal at a given point.
67 68 69 70 71 72 73 74 75 |
# File 'lib/ashton/signed_distance_field.rb', line 67 def sample_normal(x, y) gradient_x, gradient_y = sample_gradient x, y length = Gosu::distance 0, 0, gradient_x, gradient_y if length == 0 [0, 0] # This could be NaN in edge cases. else [gradient_x / length, gradient_y / length] end end |
#to_a ⇒ Array<Array<Integer>>
Convert into a nested array of sample values.
143 144 145 146 147 148 149 |
# File 'lib/ashton/signed_distance_field.rb', line 143 def to_a width.times.map do |x| height.times.map do |y| sample_distance x, y end end end |