1# xm-enved.rb -- Translation of xm-enved.scm and enved.scm 2 3# Translator/Author: Michael Scholz <mi-scholz@users.sourceforge.net> 4# Created: 03/03/18 00:18:35 5# Changed: 20/09/19 00:41:47 6 7# Tested with Snd 20.x 8# Ruby 2.6 9# Motif 2.3.3 X11R6 10# 11# module Snd_enved 12# channel_enved(snd, chn) 13# set_channel_enved(new_env, snd, chn) 14# channel_envelope(snd, chn) 15# set_channel_envelope(new_env, snd, chn) 16# mouse_press_envelope(snd, chn, button, state, x, y) 17# mouse_drag_envelope(snd, chn, button, state, x, y) 18# mouse_release_envelope(snd, chn, button, state, x, y, axis) 19# create_initial_envelopes(snd) 20# enveloping_key_press(snd, chn, key, state) 21# start_enveloping 22# stop_enveloping 23# play_with_envs(snd = false) 24# 25# envelope?(obj) 26# make_enved(enved) 27# enved?(obj) 28# make_graph_enved(enved, snd, chn) 29# graph_enved?(obj) 30# make_xenved(name, parent, *rest) 31# xenved?(obj) 32# xenved_test(name) 33# 34# class Enved 35# initialize(enved) 36# inspect 37# to_s 38# envelope 39# envelope=(new_env) 40# reset 41# interp(x, base) 42# stretch(old_att, new_att, old_dec, new_dec) 43# stretch!(old_att, new_att, old_dec, new_dec) 44# scale(scale, offset) 45# scale!(scale, offset) 46# normalize(new_max) 47# normalize!(new_max) 48# reverse 49# reverse! 50# point(idx, *args) 51# min 52# max 53# length 54# first 55# last 56# first_x 57# last_x 58# first_y 59# last_y 60# in_range?(x) 61# each 62# each_with_index 63# map 64# map! 65# 66# class Graph_enved < Enved 67# initialize(enved, snd, chn) 68# click_time 69# click_time=(val) 70# before_enved_hook 71# after_enved_hook 72# inspect 73# to_s 74# reset 75# redraw 76# mouse_press_cb(x, y) 77# mouse_drag_cb(x, y) 78# mouse_release_cb 79# 80# class Xenved < Graph_enved 81# initialize(name, parent, enved, bounds, args, axis_label) 82# inspect 83# to_s 84# clear 85# envelope=(new_env) 86# axis_bounds 87# axis_bounds=(bounds) 88# point(idx, *args) 89# create 90# close 91 92require "env" 93require "hooks" 94require "extensions" 95 96if provided?(:snd_motif) 97 require "snd-xm" 98 include Snd_XM 99else 100 $with_motif = false 101end 102 103# 104# defined in snd-xm.rb: 105# 106# $with_motif 107# 108 109module Snd_enved 110 # returns Graph_enved object or nil 111 def channel_enved(snd = false, chn = false) 112 channel_property(:enved_envelope, snd, chn) 113 end 114 115 # sets Graph_enved object 116 def set_channel_enved(new_ge, snd = false, chn = false) 117 set_channel_property(:enved_envelope, new_ge, snd, chn) 118 end 119 120 add_help(:channel_envelope, 121 "channel_envelope(snd=false, chn=false) \ 122Returns the current enved envelope associated with SND's channel CHN.") 123 def channel_envelope(snd = false, chn = false) 124 if graph_enved?(ge = channel_enved(snd, chn)) 125 ge.envelope 126 elsif envelope?(en = channel_property(:channel_envelope, snd, chn)) 127 set_channel_enved(make_graph_enved(en, snd, chn), snd, chn) 128 en 129 else 130 nil 131 end 132 end 133 134 def set_channel_envelope(new_env, snd = false, chn = false) 135 set_channel_property(:channel_envelope, new_env, snd, chn) 136 if graph_enved?(ge = channel_enved(snd, chn)) 137 ge.envelope = new_env 138 else 139 ge = make_graph_enved(new_env, snd, chn) 140 set_channel_enved(ge, snd, chn) 141 end 142 ge 143 end 144 145 # left button: set/delete point 146 # middle button: reset to original env 147 def mouse_press_envelope(snd, chn, button, state, x, y) 148 case button 149 when 1 150 graph_enved?(ge = channel_enved(snd, chn)) and ge.mouse_press_cb(x, y) 151 when 2 152 graph_enved?(ge = channel_enved(snd, chn)) and ge.reset 153 end 154 end 155 156 def mouse_drag_envelope(snd, chn, button, state, x, y) 157 graph_enved?(ge = channel_enved(snd, chn)) and ge.mouse_drag_cb(x, y) 158 end 159 160 def mouse_release_envelope(snd, chn, button, state, x, y, axis) 161 if axis == Lisp_graph 162 graph_enved?(ge = channel_enved(snd, chn)) and ge.mouse_release_cb 163 true 164 else 165 false 166 end 167 end 168 169 def create_initial_envelopes(snd) 170 channels(snd).times do |chn| 171 set_dot_size(8, snd, chn) 172 set_channel_envelope([0.0, 1.0, 1.0, 1.0], snd, chn) 173 end 174 end 175 176 def enveloping_key_press(snd, chn, key, state) 177 # C-g returns to original env 178 # C-. applies current env to amplitude 179 if key == ?. and state == 4 180 env_channel((channel_envelope(snd, chn) or [0, 1, 1, 1]), 181 0, framples(snd, chn), snd, chn) 182 true 183 else 184 if key == ?g and state == 4 185 graph_enved?(ge = channel_enved(snd, chn)) and ge.reset 186 true 187 else 188 false 189 end 190 end 191 end 192 193 Hook_name = "graph-enved" 194 195 add_help(:start_enveloping, 196 "start_enveloping() \ 197Starts the enved processes, displaying an envelope editor in each channel.") 198 def start_enveloping 199 unless $after_open_hook.member?(Hook_name) 200 $after_open_hook.add_hook!(Hook_name) do |snd| 201 create_initial_envelopes(snd) 202 end 203 $mouse_press_hook.add_hook!(Hook_name) do |snd, chn, button, state, x, y| 204 mouse_press_envelope(snd, chn, button, state, x, y) 205 end 206 $mouse_drag_hook.add_hook!(Hook_name) do |snd, chn, button, state, x, y| 207 mouse_drag_envelope(snd, chn, button, state, x, y) 208 end 209 $mouse_click_hook.add_hook!(Hook_name) do |snd, chn, but, st, x, y, axis| 210 mouse_release_envelope(snd, chn, but, st, x, y, axis) 211 end 212 $key_press_hook.add_hook!(Hook_name) do |snd, chn, key, state| 213 enveloping_key_press(snd, chn, key, state) 214 end 215 true 216 else 217 false 218 end 219 end 220 221 add_help(:stop_enveloping, 222 "stop_enveloping() \ 223Turns off the enved channel-specific envelope editors.") 224 def stop_enveloping 225 $after_open_hook.remove_hook!(Hook_name) 226 $mouse_press_hook.remove_hook!(Hook_name) 227 $mouse_drag_hook.remove_hook!(Hook_name) 228 $mouse_click_hook.remove_hook!(Hook_name) 229 $key_press_hook.remove_hook!(Hook_name) 230 Snd.catch do 231 set_lisp_graph?(false, Snd.snd, Snd.chn) 232 end 233 nil 234 end 235 236 # some examples 237 238 add_help(:play_with_envs, 239 "play_with_envs(snd=false) \ 240Sets channel amps during playback from the associated enved envelopes.") 241 def play_with_envs(snd = false) 242 channel_envelope(snd, 0) or create_initial_envelopes(snd) 243 channels(snd).times do |chn| 244 player = make_player(snd, chn) 245 e = make_env(:envelope, channel_envelope(snd, chn), 246 :length, (framples(snd, chn).to_f / dac_size).floor) 247 add_player(player, 0, -1, -1, 248 lambda do |reason| 249 $play_hook.reset_hook! 250 end) 251 $play_hook.add_hook!(get_func_name) do |fr| 252 set_amp_control(env(e), player) 253 end 254 end 255 start_playing(channels(snd), srate(snd)) 256 end 257 258 def envelope?(obj) 259 array?(obj) and obj.length >= 4 and obj.length.even? 260 end 261 262 def make_enved(enved = [0, 0, 1, 1]) 263 assert_type(envelope?(enved), enved, 0, 264 "an envelope, at least 2 points [x0, y0, x1, y1, ...]") 265 Enved.new(enved) 266 end 267 268 def enved?(obj) 269 obj.instance_of?(Enved) 270 end 271 272 def make_graph_enved(enved = [0, 0, 1, 1], snd = Snd.snd, chn = Snd.chn) 273 assert_type(envelope?(enved), enved, 0, 274 "an envelope, at least 2 points [x0, y0, x1, y1, ...]") 275 (ge = Graph_enved.new(enved, snd, chn)).redraw 276 ge 277 end 278 279 def graph_enved?(obj) 280 obj.instance_of?(Graph_enved) 281 end 282 283 if $with_motif 284 def make_xenved(name, parent, *rest) 285 envelope, bounds, args, label = optkey(rest, 286 [:envelope, [0, 0, 1, 1]], 287 [:axis_bounds, [0, 1, 0, 1]], 288 [:args, []], 289 :axis_label) 290 unless string?(name) and (not name.empty?) 291 name = "xenved" 292 end 293 assert_type(widget?(parent), parent, 1, "a widget") 294 assert_type((array?(bounds) and bounds.length == 4), bounds, 3, 295 "an array of 4 elements [x0, x1, y0, y1]") 296 unless array?(label) and label.length == 4 297 label = bounds 298 end 299 Xenved.new(name, parent, envelope, bounds, args, label) 300 end 301 302 def xenved?(obj) 303 obj.instance_of?(Xenved) 304 end 305 306 Test_widget_type = RxmFormWidgetClass 307 Test_widget_args = [RXmNheight, 200] 308 Test_xenved_args = [RXmNleftAttachment, RXmATTACH_WIDGET, 309 RXmNtopAttachment, RXmATTACH_WIDGET, 310 RXmNbottomAttachment, RXmATTACH_WIDGET, 311 RXmNrightAttachment, RXmATTACH_WIDGET] 312 313 def xenved_test(name = "xenved") 314 make_xenved(name, 315 add_main_pane(name, Test_widget_type, Test_widget_args), 316 :envelope, [0, 0, 1, 1], 317 :axis_bounds, [0, 1, 0, 1], 318 :args, Test_xenved_args) 319 end 320 end 321end 322 323include Snd_enved 324 325class Enved 326 include Enumerable 327 include Info 328 329 def initialize(enved = [0, 0, 1, 1]) 330 (@envelope = enved).map! do |x| 331 x.to_f 332 end 333 @init = @envelope.dup 334 set_enved_help 335 end 336 attr_reader :envelope 337 338 def inspect 339 format("%s.new(%s)", self.class, @envelope) 340 end 341 342 def to_s 343 format("#<%s: envelope: %s>", self.class, @envelope.to_string) 344 end 345 346 def envelope=(enved) 347 assert_type(envelope?(enved), enved, 0, 348 "an envelope, at least 2 points [x0, y0, x1, y1, ...]") 349 @envelope = enved.map do |x| 350 x.to_f 351 end 352 end 353 354 def reset 355 @envelope = @init.dup 356 end 357 358 def interp(x, base = 0) 359 envelope_interp(x, @envelope, base) 360 end 361 362 def stretch(old_att = nil, new_att = nil, old_dec = nil, new_dec = nil) 363 stretch_envelope(@envelope, old_att, new_att, old_dec, new_dec) 364 end 365 366 def stretch!(old_att = nil, new_att = nil, old_dec = nil, new_dec = nil) 367 self.envelope = self.stretch(old_att, new_att, old_dec, new_dec) 368 end 369 370 def scale(scale = 1.0, offset = 0.0) 371 scale_envelope(@envelope, scale, offset) 372 end 373 374 def scale!(scale = 1.0, offset = 0.0) 375 self.envelope = self.scale(scale, offset) 376 end 377 378 def normalize(new_max = 1.0) 379 self.scale(new_max / self.max) 380 end 381 382 def normalize!(new_max = 1.0) 383 self.envelope = self.normalize(new_max) 384 end 385 386 def reverse 387 reverse_envelope(@envelope) 388 end 389 390 def reverse! 391 self.envelope = self.reverse 392 end 393 394 def point(idx, *args) 395 x, y = optkey(args, :x, :y) 396 if x 397 @envelope[idx * 2] = x 398 end 399 if y 400 @envelope[idx * 2 + 1] = y 401 end 402 @envelope[idx * 2, 2] 403 end 404 405 def min 406 min_envelope(@envelope) 407 end 408 409 def max 410 max_envelope(@envelope) 411 end 412 413 def length 414 @envelope.length / 2 415 end 416 417 def first 418 if @envelope.length > 1 419 @envelope[0, 2] 420 else 421 [0.0, 0.0] 422 end 423 end 424 425 def last 426 if @envelope.length > 3 427 @envelope[-2, 2] 428 else 429 [1.0, 0.0] 430 end 431 end 432 433 def first_x 434 @envelope[0] 435 end 436 437 def first_y 438 @envelope[1] 439 end 440 441 def last_x 442 @envelope[-2] 443 end 444 445 def last_y 446 @envelope[-1] 447 end 448 449 def in_range?(x) 450 x > @envelope[0] and x < @envelope[-2] 451 end 452 453 def each 454 0.step(@envelope.length - 1, 2) do |i| 455 yield(@envelope[i, 2]) 456 end 457 @envelope 458 end 459 460 def each_with_index 461 0.step(@envelope.length - 1, 2) do |i| 462 yield(@envelope[i, 2] + [i]) 463 end 464 @envelope 465 end 466 467 def map 468 res = make_array(@envelope.length) 469 0.step(@envelope.length - 1, 2) do |i| 470 res[i, 2] = yield(@envelope[i, 2]) 471 end 472 res 473 end 474 475 def map! 476 0.step(@envelope.length - 1, 2) do |i| 477 @envelope[i, 2] = yield(@envelope[i, 2]) 478 end 479 @envelope 480 end 481 482 private 483 def set_enved_help 484 self.description = "\ 485# make_enved(env) 486# 487# class Enved 488# initialize(env) 489# 490# getter and setter: 491# envelope=(new_env) 492# envelope 493# 494# methods: 495# interp(x, base) 496# stretch(oatt, natt, odec, ndec) stretch!(oatt, natt, odec, ndec) 497# scale(scale, offset) scale!(scale, offset) 498# normalize(new_max) normalize!(new_max) 499# reverse reverse! 500# max min 501# first (first [x, y]) last (last [x, y]) 502# first_x last_x 503# first_y last_y 504# map do |x, y| ... end map! do |x, y| ... end 505# each do |x, y| ... end each_with_index do |x, y, i| ... end 506# length 507# point(idx, *args) # point(idx) ==> [x, y] 508# # point(idx, :x, x_val, :y, y_val) 509# # sets x, y or both and returns new [x, y] 510# in_range?(x) (x > first_x and x < last_x) 511# help (alias info and description) 512" 513 add_help(:Enved, self.description) 514 end 515end 516 517class Graph_enved < Enved 518 def initialize(enved, snd = false, chn = false) 519 super(enved) 520 @graph_name = short_file_name(snd) 521 @snd = snd 522 @chn = chn 523 @before_enved_hook = Hook.new("@before_enved_hook", 4, "\ 524lambda do |pos, x, y, reason| ... end: called before changing a \ 525breakpoint in @envelope. This hook runs the global $enved_hook as \ 526first hook, subsequent procedures can directly manipulate @envelope. 527 528This instance hook is like the global $enved_hook; POS is @envelope's \ 529x-position, X and Y are the new points, and REASON is one of \ 530Enved_add_point, Enved_delete_point, Enved_move_point. If the last \ 531hook procedure in the hook list returns `false', the class changes the \ 532breakpoint, otherwise the hook procedures are responsible for \ 533manipulating @envelope itself. 534 535From dlocsig.rb: 536 537@velocity = make_xenved(\"velocity (v)\", frame, 538 :envelope, [0.0, 0.0, 1.0, 0.0], 539 :axis_bounds, [0.0, 1.0, 0.0, 1.0], 540 :axis_label, [-20.0, 20.0, 0.0, 2.0]) 541@velocity.before_enved_hook.reset_hook! # to prevent running $enved_hook 542@velocity.before_enved_hook.add_hook!(\"dlocsig-hook\") do |pos, x, y, reason| 543 if reason == Enved_move_point 544 if @velocity.in_range?(x) 545 old_x = @velocity.point(pos).first 546 @velocity.stretch!(old_x, x) 547 @velocity.point(pos, :y, y) 548 else 549 false 550 end 551 else 552 false 553 end 554end 555 556In contrast the same procedure on the global $enved_hook: 557 558$enved_hook.add_hook!(\"snd-init-hook\") do |env, pt, x, y, reason| 559 if reason == Enved_move_point 560 if x > 0.0 and x < env[-2] 561 old_x = env[2 * pt] 562 new_env = stretch_envelope(env, old_x, x) 563 new_env[pt * 2 + 1] = y 564 new_env 565 else 566 # env # first and last points are fixed 567 false # first and last points can be moved 568 end 569 else 570 false 571 end 572end") 573 hn = "initialize-xm-enved-hook" 574 @before_enved_hook.add_hook!(hn) do |pos, x, y, reason| 575 if $enved_hook.empty? 576 false 577 else 578 e = nil 579 $enved_hook.run_hook do |prc| 580 case e = prc.call(@envelope.dup, pos, x, y, reason) 581 when Array 582 self.envelope = e 583 when Enved, Graph_enved, Xenved 584 self.envelope = e.envelope 585 end 586 end 587 e.class != FalseClass 588 end 589 end 590 @after_enved_hook = Hook.new("@after_enved_hook", 2, "\ 591lambda do |pos, reason| ... end: called after redrawing new or changed \ 592breakpoints. POS is @envelope's x-position, and REASON is one of \ 593Enved_add_point, Enved_delete_point, Enved_move_point.") 594 init 595 set_enved_help 596 end 597 alias help description 598 attr_accessor :click_time 599 attr_reader :before_enved_hook 600 attr_reader :after_enved_hook 601 602 def inspect 603 format("%s.new(%s, %s, %s)", self.class, @envelope, @snd, @chn) 604 end 605 606 def to_s 607 format("#<%s: %s[%s:%s]: %s>", 608 self.class, @graph_name, @snd, @chn, @envelope.to_string) 609 end 610 611 def init 612 @mouse_up = 0.0 613 @mouse_down = 0.0 614 @click_time = 0.5 615 @mouse_pos = 0 616 @mouse_new = false 617 end 618 619 def envelope=(new_env) 620 super 621 self.redraw 622 @envelope 623 end 624 625 def reset 626 super 627 self.redraw 628 init 629 @envelope 630 end 631 632 def redraw 633 graph(@envelope, @graph_name, 0.0, 1.0, 0.0, 1.0, @snd, @chn) 634 update_lisp_graph(@snd, @chn) 635 end 636 637 Mouse_radius = 0.03 638 639 # To prevent unexpected point movements if position is near first or 640 # last point. 641 Secure_distance = 0.001 642 643 def mouse_press_cb(x, y) 644 x = [0.0, [x, 1.0].min].max 645 y = [0.0, [y, 1.0].min].max 646 pos = false 647 self.each_with_index do |x1, y1, i| 648 if (x1 - x).abs < Mouse_radius and (y1 - y).abs < Mouse_radius 649 pos = i 650 break 651 end 652 end 653 @mouse_new = (not pos) 654 @mouse_down = Time.now.to_f 655 if pos 656 @mouse_pos = pos 657 else 658 x = [Secure_distance, [x, 1.0 - Secure_distance].min].max 659 if run_before_enved_hook(x, y, Enved_add_point) 660 add_envelope_point(x, y) 661 end 662 self.redraw 663 @after_enved_hook.call(@mouse_pos / 2, Enved_add_point) 664 end 665 end 666 667 def mouse_drag_cb(x, y) 668 lx = if @mouse_pos.zero? 669 @envelope[0] 670 elsif @mouse_pos >= (@envelope.length - 2) 671 @envelope[-2] 672 else 673 [@envelope[@mouse_pos - 2] + Secure_distance, 674 [x, @envelope[@mouse_pos + 2] - Secure_distance].min].max 675 end 676 #ly = [0.0, [y, 1.0].min].max 677 ly = y 678 if run_before_enved_hook(lx, ly, Enved_move_point) 679 @envelope[@mouse_pos, 2] = [lx, ly] 680 end 681 self.redraw 682 @after_enved_hook.call(@mouse_pos / 2, Enved_move_point) 683 end 684 685 def mouse_release_cb 686 @mouse_up = Time.now.to_f 687 if (not @mouse_new) and (@mouse_up - @mouse_down) <= @click_time and 688 @mouse_pos.nonzero? and @mouse_pos < (@envelope.length - 2) 689 if run_before_enved_hook(@envelope[@mouse_pos], @envelope[@mouse_pos + 1], 690 Enved_delete_point) 691 @envelope.slice!(@mouse_pos, 2) 692 end 693 self.redraw 694 @after_enved_hook.call(@mouse_pos / 2, Enved_delete_point) 695 end 696 @mouse_new = false 697 end 698 699 protected 700 # If the last hook procedure returns false, change the envelope, 701 # otherwise the hook procedure is responsible. 702 def run_before_enved_hook(x, y, reason) 703 if @before_enved_hook.empty? 704 true 705 else 706 e = nil 707 @before_enved_hook.run_hook do |prc| 708 e = prc.call(@mouse_pos / 2, x, y, reason) 709 end 710 e.class == FalseClass 711 end 712 end 713 714 def add_envelope_point(x, y) 715 idx = @mouse_pos 716 test_env = @envelope.to_pairs 717 if cur_pair = test_env.assoc(x) 718 idx = test_env.index(cur_pair) * 2 719 @envelope[idx + 1] = y 720 else 721 cur_pair = test_env.detect do |pair| 722 x < pair[0] 723 end 724 if cur_pair 725 idx = test_env.index(cur_pair) * 2 726 @envelope.insert(idx, x, y) 727 end 728 end 729 @mouse_pos = idx 730 end 731 732 private 733 def set_enved_help 734 super 735 self.description += "\ 736# 737# make_graph_enved(enved, snd, chn) 738# 739# class Graph_enved < Enved (see enved.scm) 740# initialize(enved, snd, chn) 741# before_enved_hook lambda do |pos, x, y, reason| ... end 742# after_enved_hook lambda do |pos, reason| ... end 743# 744# getter and setter: 745# click_time=(val) 746# click_time 747# 748# interactive methods: 749# init 750# reset 751# redraw 752# mouse_press_cb(x, y) 753# mouse_drag_cb(x, y) 754# mouse_release_cb 755# help (alias info and description) 756 757# more examples in xm-enved.rb, module Snd_enved 758 759ge = make_graph_enved([0, 0, 1, 1], 0, 0) 760ge.envelope # ==> [0.0, 0.0, 1.0, 1.0] 761ge.click_time # ==> 0.2 762ge.envelope = [0, 1, 1, 1] 763ge.help # this help 764" 765 add_help(:Graph_enved, self.description) 766 end 767end 768 769if $with_motif 770 class Xenved < Graph_enved 771 def initialize(name, parent, enved, bounds, args, axis_label) 772 super(enved) 773 @name = name 774 @parent = parent 775 @x0, @x1, @y0, @y1 = bounds.map do |x| 776 x.to_f 777 end 778 @args = args 779 unless @args.member?(RXmNforeground) 780 @args += [RXmNforeground, data_color] 781 end 782 unless @args.member?(RXmNbackground) 783 @args += [RXmNbackground, graph_color] 784 end 785 @lx0, @lx1, @ly0, @ly1 = if envelope?(axis_label) 786 axis_label.map do |x| 787 x.to_f 788 end 789 else 790 [0.0, 1.0, -1.0, 1.0] 791 end 792 @gc = snd_gcs[0] 793 @drawer = @dpy = @window = nil 794 @px0 = @px1 = @py0 = @py1 = nil 795 @dragging = false 796 set_enved_help 797 create 798 end 799 alias help description 800 801 def inspect 802 format("%s.new(%p, %s, %s, %s, %s, %s)", 803 self.class, 804 @name, 805 @parent, 806 @envelope, 807 [@x0, @x1, @y0, @y1], 808 @args, 809 [@lx0, @lx1, @ly0, @ly1]) 810 end 811 812 def to_s 813 format("#<%s: name: %p, envelope: %s>", 814 self.class, @name, @envelope.to_string) 815 end 816 817 def axis_bounds 818 [@x0, @x1, @y0, @y1] 819 end 820 821 def axis_bounds=(bounds) 822 assert_type((array?(bounds) and bounds.length == 4), bounds, 0, 823 "an array of 4 elements [x0, x1, y0, y1]") 824 @x0, @x1, @y0, @y1 = bounds.map do |x| 825 x.to_f 826 end 827 self.envelope = @init 828 end 829 830 def point(idx, *args) 831 if args.length > 0 832 super 833 redraw 834 end 835 @envelope[idx * 2, 2] 836 end 837 838 def create 839 if widget?(@drawer) 840 show_widget(@drawer) 841 else 842 create_enved 843 end 844 end 845 alias open create 846 847 def close 848 hide_widget(@drawer) 849 end 850 851 protected 852 def redraw 853 if is_managed?(@drawer) and @px0 and @py0 > @py1 854 RXClearWindow(@dpy, @window) 855 # Motif's DRAW-AXES takes 6 optional arguments. 856 # '( x0 y0 x1 y1 ) = draw-axes(wid gc label 857 # x0=0.0 x1=1.0 y0=-1.0 y1=1.0 858 # style=x-axis-in-seconds 859 # axes=show-all-axes) 860 # arity #( 3 6 #f ) 861 draw_axes(@drawer, @gc, @name, @lx0, @lx1, @ly0, @ly1) 862 lx = ly = nil 863 @envelope.each_pair do |x, y| 864 cx = grfx(x) 865 cy = grfy(y) 866 RXFillArc(@dpy, @window, @gc, 867 cx - Mouse_r, cy - Mouse_r, Mouse_d, Mouse_d, 0, 360 * 64) 868 if lx 869 RXDrawLine(@dpy, @window, @gc, lx, ly, cx, cy) 870 end 871 lx, ly = cx, cy 872 end 873 end 874 end 875 876 private 877 def set_enved_help 878 super 879 self.description += "\ 880# 881# make_xenved(name, parent, *rest) 882# name String 883# parent Widget 884# *rest 885# :envelope, [0, 0, 1, 1] x0, y0, x1, y1, ... 886# :axis_bounds, [0, 1, 0, 1] x0, x1, y0, y1 887# :args, [] Motif properties 888# :axis_label, nil if axes labels should have 889# other values than axis_bounds, 890# (see dlocsig.rb) 891# 892# class Xenved < Graph_enved (see xm-enved.scm) 893# initialize(name, parent, env, axis_bounds, args, axis_label) 894# 895# getter and setter: 896# axis_bounds=(new_bounds) 897# axis_bounds 898# 899# interactive methods: 900# create (alias open) 901# close 902# reset 903# help (alias info and description) 904 905# more examples in effects.rb 906 907xe = xenved_test 908xe.envelope # ==> [0.0, 0.0, 1.0, 1.0] 909xe.click_time # ==> 0.5 910xe.envelope = [0, 1, 1, 1] 911# some clicks later 912xe.envelope # ==> [0.0, 0.0, 913 # 0.190735694822888, 0.562264150943396, 914 # 0.632152588555858, 0.932075471698113, 915 # 0.848773841961853, 0.316981132075472, 916 # 1.0, 1.0] 917xe.help # this help 918" 919 add_help(:Xenved, self.description) 920 end 921 922 Mouse_d = 10 923 Mouse_r = 5 924 925 def create_enved 926 @drawer = RXtCreateManagedWidget(@name, RxmDrawingAreaWidgetClass, 927 @parent, @args) 928 @dpy = RXtDisplay(@drawer) 929 @window = RXtWindow(@drawer) 930 RXtAddCallback(@drawer, RXmNresizeCallback, 931 lambda do |w, c, i| 932 draw_axes_cb 933 end) 934 RXtAddCallback(@drawer, RXmNexposeCallback, 935 lambda do |w, c, i| 936 draw_axes_cb 937 end) 938 RXtAddEventHandler(@drawer, RButtonPressMask, false, 939 lambda do |w, c, e, f| 940 mouse_press_cb(ungrfx(Rx(e)), ungrfy(Ry(e))) 941 end) 942 RXtAddEventHandler(@drawer, RButtonReleaseMask, false, 943 lambda do |w, c, e, f| 944 mouse_release_cb 945 end) 946 RXtAddEventHandler(@drawer, RButtonMotionMask, false, 947 lambda do |w, c, e, f| 948 mouse_drag_cb(ungrfx(Rx(e)), ungrfy(Ry(e))) 949 end) 950 RXtAddEventHandler(@drawer, REnterWindowMask, false, 951 lambda do |w, cursor, e, f| 952 RXDefineCursor(@dpy, @window, cursor) 953 end, RXCreateFontCursor(@dpy, RXC_crosshair)) 954 RXtAddEventHandler(@drawer, RLeaveWindowMask, false, 955 lambda do |w, c, e, f| 956 RXUndefineCursor(@dpy, @window) 957 end) 958 end 959 960 # Motif's DRAW_AXES takes 3 required and 6 optional arguments. 961 # draw_axes(wid, gc, label, 962 # x0=0.0, x1=1.0, y0=-1.0, y1=1.0, 963 # style=X_axis_in_seconds, 964 # axes=Show_all_axes) 965 def draw_axes_cb 966 @px0, @py0, @px1, @py1 = draw_axes(@drawer, @gc, @name, 967 @lx0, @lx1, @ly0, @ly1, 968 X_axis_in_seconds, 969 Show_all_axes) 970 redraw 971 end 972 973 def ungrfx(x) 974 if @px0 == @px1 975 @x0 976 else 977 [@x1, 978 [@x0, @x0 + ((@x1 - @x0) * ((x - @px0) / (@px1.to_f - @px0)))].max].min 979 end 980 end 981 982 def ungrfy(y) 983 if @py0 == @py1 984 @y1 985 else 986 [@y1, 987 [@y0, @y0 + ((@y1 - @y0) * ((@py0 - y) / (@py0.to_f - @py1)))].max].min 988 end 989 end 990 991 def grfx(x) 992 if @px0 == @px1 993 @px0 994 else 995 [@px1, 996 [@px0, 997 (@px0 + 998 ((@px1 - @px0) * ((x - @x0) / (@x1.to_f - @x0)))).round].max].min 999 end 1000 end 1001 1002 def grfy(y) 1003 if @py0 == @py1 1004 @py0 1005 else 1006 [@py0, 1007 [@py1, 1008 (@py1 + 1009 ((@py0 - @py1) * ((y - @y1) / (@y0.to_f - @y1)))).round].max].min 1010 end 1011 end 1012 end 1013end 1014 1015# xm-enved.rb ends here 1016