Module: GTK::Geometry

Included in:
AttrRect, MousePoint
Defined in:
dragon/geometry.rb

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.angle_between_lines(line_one, line_two, replace_infinity: nil) ⇒ Object


180
181
182
183
184
# File 'dragon/geometry.rb', line 180

def self.angle_between_lines line_one, line_two, replace_infinity: nil
  m_line_one = line_slope line_one, replace_infinity: replace_infinity
  m_line_two = line_slope line_two, replace_infinity: replace_infinity
  Math.atan((m_line_one - m_line_two) / (1 + m_line_two * m_line_one)).to_degrees
end

.angle_from(start_point, end_point) ⇒ Object


320
321
322
323
324
325
326
# File 'dragon/geometry.rb', line 320

def self.angle_from start_point, end_point
  d_y = end_point.y - start_point.y
  d_x = end_point.x - start_point.x
  Math::PI.+(Math.atan2(d_y, d_x)).to_degrees
rescue Exception => e
  raise e, ":angle_from failed for start_point: #{start_point} end_point: #{end_point}."
end

.angle_to(start_point, end_point) ⇒ Object


329
330
331
332
333
# File 'dragon/geometry.rb', line 329

def self.angle_to start_point, end_point
  angle_from end_point, start_point
rescue Exception => e
  raise e, ":angle_to failed for start_point: #{start_point} end_point: #{end_point}."
end

.center_inside_rect(rect, other_rect) ⇒ Object


79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'dragon/geometry.rb', line 79

def self.center_inside_rect rect, other_rect
  offset_x = (other_rect.w - rect.w).half
  offset_y = (other_rect.h - rect.h).half
  new_rect = rect.shift_rect(0, 0)
  new_rect.x = other_rect.x + offset_x
  new_rect.y = other_rect.y + offset_y
  new_rect
rescue Exception => e
  raise e, <<-S
* ERROR:
center_inside_rect for self #{self} and other_rect #{other_rect}. Failed with exception #{e}.
S
end

.center_inside_rect_x(rect, other_rect) ⇒ Object


97
98
99
100
101
102
103
104
105
106
107
108
# File 'dragon/geometry.rb', line 97

def self.center_inside_rect_x rect, other_rect
  offset_x   = (other_rect.w - rect.w).half
  new_rect   = rect.shift_rect(0, 0)
  new_rect.x = other_rect.x + offset_x
  new_rect.y = other_rect.y
  new_rect
rescue Exception => e
  raise e, <<-S
* ERROR:
center_inside_rect_x for self #{self} and other_rect #{other_rect}. Failed with exception #{e}.
S
end

.center_inside_rect_y(rect, other_rect) ⇒ Object


114
115
116
117
118
119
120
121
122
123
124
125
# File 'dragon/geometry.rb', line 114

def self.center_inside_rect_y rect, other_rect
  offset_y = (other_rect.h - rect.h).half
  new_rect = rect.shift_rect(0, 0)
  new_rect.x = other_rect.x
  new_rect.y = other_rect.y + offset_y
  new_rect
rescue Exception => e
  raise e, <<-S
* ERROR:
center_inside_rect_y for self #{self} and other_rect #{other_rect}. Failed with exception #{e}.
S
end

.contract_intersect_rect?Boolean

Returns:

  • (Boolean)

262
263
264
# File 'dragon/geometry.rb', line 262

def self.contract_intersect_rect?
  [:left, :right, :top, :bottom]
end

.cubic_bezier(t, a, b, c, d) ⇒ Object

Returns f(t) for a cubic Bezier curve.


9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# File 'dragon/geometry.rb', line 9

def self.cubic_bezier t, a, b, c, d
  s  = 1 - t
  s0 = 1
  s1 = s
  s2 = s * s
  s3 = s * s * s

  t0 = 1
  t1 = t
  t2 = t * t
  t3 = t * t * t

  1 * s3 * t0 * a +
  3 * s2 * t1 * b +
  3 * s1 * t2 * c +
  1 * s0 * t3 * d
end

.distance(point_one, point_two) ⇒ Object


313
314
315
316
317
# File 'dragon/geometry.rb', line 313

def self.distance point_one, point_two
  Math.sqrt((point_two.x - point_one.x)**2 + (point_two.y - point_one.y)**2)
rescue Exception => e
  raise e, ":distance failed for point_one: #{point_one} point_two #{point_two}."
end

.inside_rect?(inner_rect, outer_rect, tolerance = 0.0) ⇒ Boolean

Returns:

  • (Boolean)

343
344
345
346
347
348
349
350
# File 'dragon/geometry.rb', line 343

def self.inside_rect? inner_rect, outer_rect, tolerance = 0.0
  inner_rect.x     + tolerance >= outer_rect.x     - tolerance &&
  inner_rect.right - tolerance <= outer_rect.right + tolerance &&
  inner_rect.y     + tolerance >= outer_rect.y     - tolerance &&
  inner_rect.top   - tolerance <= outer_rect.top   + tolerance
rescue Exception => e
  raise e, ":inside_rect? failed for inner_rect: #{inner_rect} outer_rect: #{outer_rect}."
end

.intersect_rect?(rect_one, rect_two, tolerance = 0.1) ⇒ Boolean

Returns:

  • (Boolean)

267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
# File 'dragon/geometry.rb', line 267

def self.intersect_rect? rect_one, rect_two, tolerance = 0.1
  return false if rect_one.right - tolerance < rect_two.left + tolerance
  return false if rect_one.left + tolerance > rect_two.right - tolerance
  return false if rect_one.top - tolerance < rect_two.bottom + tolerance
  return false if rect_one.bottom + tolerance > rect_two.top - tolerance
  return true
rescue Exception => e
  context_help_rect_one = (rect_one.__help_contract_implementation contract_intersect_rect?)[:not_implemented_methods]
  context_help_rect_two = (rect_two.__help_contract_implementation contract_intersect_rect?)[:not_implemented_methods]
  context_help = ""
  if context_help_rect_one && context_help_rect_one.length > 0
    context_help += <<-S
rect_one needs to implement the following methods: #{context_help_rect_one}

You may want to try include the ~AttrRect~ module which will give you these methods.
S
  end

  if context_help_rect_two && context_help_rect_two.length > 0
    context_help += <<-S
* FAILURE REASON:
rect_two needs to implement the following methods: #{context_help_rect_two}
NOTE: You may want to try include the ~GTK::Geometry~ module which will give you these methods.
S
  end

  raise e, <<-S
* ERROR:
:intersect_rect? failed for
- rect_one: #{rect_one}
- rect_two: #{rect_two}
#{context_help}
S
end

.intersects_rect?(*args) ⇒ Boolean

Returns:

  • (Boolean)

160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'dragon/geometry.rb', line 160

def self.intersects_rect? *args
  raise <<-S
intersects_rect? (with an \"s\") has been deprecated.
Use intersect_rect? instead (remove the \"s\").

* NOTE:
Ruby's naming convention is to *never* include the \"s\" for
interrogative method names (methods that end with a ?). It
doesn't sound grammatically correct, but that has been the
rule for a long time (and why intersects_rect? has been deprecated).

S
end

.line_intersect(line_one, line_two) ⇒ Object


252
253
254
255
256
257
258
259
260
# File 'dragon/geometry.rb', line 252

def self.line_intersect line_one, line_two
  m1 = line_slope(line_one)
  m2 = line_slope(line_two)
  b1 = line_y_intercept(line_one)
  b2 = line_y_intercept(line_two)
  x = (b1 - b2) / (m2 - m1)
  y = (-b2.fdiv(m2) + b1.fdiv(m1)).fdiv(1.fdiv(m1) - 1.fdiv(m2))
  [x, y]
end

.line_rect(line) ⇒ Object


232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
# File 'dragon/geometry.rb', line 232

def self.line_rect line
  if line.x > line.x2
    x  = line.x2
    y  = line.y2
    x2 = line.x
    y2 = line.y
  else
    x  = line.x
    y  = line.y
    x2 = line.x2
    y2 = line.y2
  end

  w = x2 - x
  h = y2 - y

  { x: x, y: y, w: w, h: h }
end

.line_rise_run(line) ⇒ Object


193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
# File 'dragon/geometry.rb', line 193

def self.line_rise_run line
  rise = (line.y2 - line.y).to_f
  run  = (line.x2 - line.x).to_f
  if rise.abs > run.abs && rise != 0
    rise = rise.fdiv rise.abs
    run = run.fdiv rise.abs
  elsif run.abs > rise.abs && run != 0
    rise = rise.fdiv run.abs
    run = run.fdiv run.abs
  else
    rise = rise / rise.abs if rise != 0
    run = run / run.abs if run != 0
  end
  return { x: run , y: rise }
end

.line_slope(line, replace_infinity: nil) ⇒ Object


187
188
189
190
191
# File 'dragon/geometry.rb', line 187

def self.line_slope line, replace_infinity: nil
  return replace_infinity if line.x2 == line.x
  (line.y2 - line.y).fdiv(line.x2 - line.x)
                    .replace_infinity(replace_infinity)
end

.line_y_intercept(line) ⇒ Object


175
176
177
# File 'dragon/geometry.rb', line 175

def self.line_y_intercept line
  line.y - line_slope(line) * line.x
end

.point_inside_circle?(point, circle_center_point, radius) ⇒ Boolean

Returns:

  • (Boolean)

336
337
338
339
340
# File 'dragon/geometry.rb', line 336

def self.point_inside_circle? point, circle_center_point, radius
  (point.x - circle_center_point.x) ** 2 + (point.y - circle_center_point.y) ** 2 < radius ** 2
rescue Exception => e
  raise e, ":point_inside_circle? failed for point: #{point} circle_center_point: #{circle_center_point} radius: #{radius}"
end

.ray_test(point, line) ⇒ Object


210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
# File 'dragon/geometry.rb', line 210

def self.ray_test point, line
  slope = (line.y2 - line.y).fdiv(line.x2 - line.x)

  if line.x > line.x2
    point_two, point_one = [point_one, point_two]
  end

  r = ((line.x2 - line.x) * (point.y - line.y) -
       (point.x -  line.x) * (line.y2 - line.y))

  if r == 0
    return :on
  elsif r < 0
    return :right if slope >= 0
    return :left
  elsif r > 0
    return :left if slope >= 0
    return :right
  end
end

.scale_rect(rect, percentage, *anchors) ⇒ Object


388
389
390
391
392
393
394
395
396
397
398
399
# File 'dragon/geometry.rb', line 388

def self.scale_rect rect, percentage, *anchors
  anchor_x, anchor_y = *anchors.flatten
  anchor_x ||= 0
  anchor_y ||= anchor_x
  Geometry.scale_rect_extended rect,
                               percentage_x: percentage,
                               percentage_y: percentage,
                               anchor_x: anchor_x,
                               anchor_y: anchor_y
rescue Exception => e
  raise e, ":scale_rect failed for rect: #{rect} percentage: #{percentage} anchors [#{anchor_x} (x), #{anchor_y} (y)]."
end

.scale_rect_extended(rect, percentage_x: percentage_x, percentage_y: percentage_y, anchor_x: anchor_x, anchor_y: anchor_y) ⇒ Object


353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
# File 'dragon/geometry.rb', line 353

def self.scale_rect_extended rect,
                             percentage_x: percentage_x,
                             percentage_y: percentage_y,
                             anchor_x: anchor_x,
                             anchor_y: anchor_y
  anchor_x ||= 0.0
  anchor_y ||= 0.0
  percentage_x ||= 1.0
  percentage_y ||= 1.0
  new_w = rect.w * percentage_x
  new_h = rect.h * percentage_y
  new_x = rect.x + (rect.w - new_w) * anchor_x
  new_y = rect.y + (rect.h - new_h) * anchor_y
  if rect.is_a? Array
    return [
      new_x,
      new_y,
      new_w,
      new_h,
      *rect[4..-1]
    ]
  elsif rect.is_a? Hash
    return rect.merge(x: new_x, y: new_y, w: new_w, h: new_h)
  else
    rect.x = new_x
    rect.y = new_y
    rect.w = new_w
    rect.h = new_h
    return rect
  end
rescue Exception => e
  raise e, ":scale_rect_extended failed for rect: #{rect} percentage_x: #{percentage_x} percentage_y: #{percentage_y} anchors_x: #{anchor_x} anchor_y: #{anchor_y}."
end

.shift_line(line, x, y) ⇒ Object


147
148
149
150
151
152
153
154
155
156
157
158
# File 'dragon/geometry.rb', line 147

def self.shift_line line, x, y
  if line.is_a?(Array) || line.is_a?(Hash)
    new_line = line.dup
    new_line.x  += x
    new_line.x2 += x
    new_line.y  += y
    new_line.y2 += y
    new_line
  else
    raise "shift_line for #{line} is not supported."
  end
end

.to_square(size, x, y, anchor_x = 0.5, anchor_y = nil) ⇒ Object


303
304
305
306
307
308
309
310
# File 'dragon/geometry.rb', line 303

def self.to_square size, x, y, anchor_x = 0.5, anchor_y = nil
  anchor_y ||= anchor_x
  x = x.shift_left(size * anchor_x)
  y = y.shift_down(size * anchor_y)
  [x, y, size, size]
rescue Exception => e
  raise e, ":to_square failed for size: #{size} x: #{x} y: #{y} anchor_x: #{anchor_x} anchor_y: #{anchor_y}."
end

Instance Method Details

#anchor_rect(anchor_x, anchor_y) ⇒ Object

Returns a primitive that is anchored/repositioned based off its rectangle.


134
135
136
137
138
139
140
# File 'dragon/geometry.rb', line 134

def anchor_rect anchor_x, anchor_y
  current_w = self.w
  current_h = self.h
  delta_x = -1 * (anchor_x * current_w)
  delta_y = -1 * (anchor_y * current_h)
  self.shift_rect(delta_x, delta_y)
end

#angle_from(other_point) ⇒ Object

Returns the angle to one primitive from another primitive.


69
70
71
# File 'dragon/geometry.rb', line 69

def angle_from other_point
  Geometry.angle_from self, other_point
end

#angle_given_point(other_point) ⇒ Object


142
143
144
# File 'dragon/geometry.rb', line 142

def angle_given_point other_point
  raise ":angle_given_point has been deprecated use :angle_from instead."
end

#angle_to(other_point) ⇒ Object

Returns the angle from one primitive to another primitive.


63
64
65
# File 'dragon/geometry.rb', line 63

def angle_to other_point
  Geometry.angle_to self, other_point
end

#center_inside_rect(other_rect) ⇒ Object


93
94
95
# File 'dragon/geometry.rb', line 93

def center_inside_rect other_rect
  Geometry.center_inside_rect self, other_rect
end

#center_inside_rect_x(other_rect) ⇒ Object


110
111
112
# File 'dragon/geometry.rb', line 110

def center_inside_rect_x other_rect
  Geometry.center_inside_rect_x self, other_rect
end

#center_inside_rect_y(other_rect) ⇒ Object


127
128
129
# File 'dragon/geometry.rb', line 127

def center_inside_rect_y other_rect
  Geometry.center_inside_rect_y self, other_rect
end

#inside_rect?(outer, tolerance = 0.0) ⇒ Boolean

Returns true if a primitive's rectangle is entirely inside another primitive's rectangle.

Returns:

  • (Boolean)

29
30
31
# File 'dragon/geometry.rb', line 29

def inside_rect? outer, tolerance = 0.0
  Geometry.inside_rect? self, outer, tolerance
end

#intersect_rect?(other, tolerance = 0.1) ⇒ Boolean

Returns true if a primitive's rectangle overlaps another primitive's rectangle.

Returns:

  • (Boolean)

35
36
37
# File 'dragon/geometry.rb', line 35

def intersect_rect? other, tolerance = 0.1
  Geometry.intersect_rect? self, other, tolerance
end

#intersects_rect?(*args) ⇒ Boolean

Returns:

  • (Boolean)

39
40
41
# File 'dragon/geometry.rb', line 39

def intersects_rect? *args
  Geometry.intersects_rect?(*args)
end

#point_inside_circle?(circle_center_point, radius) ⇒ Boolean

Returns true if a primitive is within a circle specified by the circle's center and radius.

Returns:

  • (Boolean)

75
76
77
# File 'dragon/geometry.rb', line 75

def point_inside_circle? circle_center_point, radius
  Geometry.point_inside_circle? self, circle_center_point, radius
end

#scale_rect(percentage, *anchors) ⇒ Object

Scales a primitive rect by a percentage.


57
58
59
# File 'dragon/geometry.rb', line 57

def scale_rect percentage, *anchors
  Geometry.scale_rect self, percentage, *anchors
end

#scale_rect_extended(percentage_x: percentage_x, percentage_y: percentage_y, anchor_x: anchor_x, anchor_y: anchor_y) ⇒ Object


43
44
45
46
47
48
49
50
51
52
53
# File 'dragon/geometry.rb', line 43

def scale_rect_extended percentage_x: percentage_x,
                        percentage_y: percentage_y,
                        anchor_x: anchor_x,
                        anchor_y: anchor_y

  Geometry.scale_rect_extended self,
                               percentage_x: percentage_x,
                               percentage_y: percentage_y,
                               anchor_x: anchor_x,
                               anchor_y: anchor_y
end