查看: 14758|回复: 2

[胡扯] 论用RGSS画复变函数图像

[复制链接]
梦石
0
星屑
1944
在线时间
404 小时
注册时间
2015-8-30
回帖
322
发表于 2018-9-9 20:03:53 | 显示全部楼层 |阅读模式

加入我们,或者,欢迎回来。

您需要 登录 才可以下载或查看,没有账号?注册会员

×
本帖最后由 有丘直方 于 2018-9-9 20:40 编辑

这个东西貌似已经有人搞过了??[pre lang="ruby"]# Color
class Color
  
  # 根据HSL数据来生成颜色对象,第四个参数是不透明度
  def self.hsl(hue, saturation, lightness, alpha = 1.0)
    saturation = saturation.clamp(0.0, 1.0)
    lightness = lightness.clamp(0.0, 1.0)
    alpha = alpha.clamp(0.0, 1.0)
   
    times = (hue * 3 / Math::PI).modulo(6)
    amount1 = (1 - (2 * lightness - 1).abs) * saturation
    amount2 = amount1 * (1 - (times % 2 - 1).abs)
    correction = lightness - amount1 / 2
    translation = (times.to_i + 1) % 6
   
    args = [0, 0, 0]
    args[translation / 2] = amount1
    args[2 - translation % 3] = amount2
   
    new(*args.map {|origin| (origin + correction) * 255 }, alpha * 255)
  end
  
  # 在此颜色之上按正常方式叠加另一个颜色时呈现的颜色
  def blend(other)
    alpha1 = alpha / 255.0
    alpha2 = other.alpha / 255.0
    calc_blend = proc do |c1, c2|
      c1 /= 255.0
      c2 /= 255.0
      blend = c1 * alpha1 * (1 - alpha2) + c2 * alpha2
      blend /= alpha1 + alpha2 - alpha1 * alpha2
      blend *= 255
    end
    result = Color.new
    result.alpha = (alpha1 + alpha2 - alpha1 * alpha2) * 255
    result.red = calc_blend.call(red, other.red)
    result.green = calc_blend.call(green, other.green)
    result.blue = calc_blend.call(blue, other.blue)
    result
  end
  
  # blend的破坏型方法
  def blend!(other)
    set(blend(other))
    self
  end
  
end

# Comparable
module Comparable
  
  # 返回在min和max的范围内最接近self的对象
  def clamp(min, max)
    return min if self <=> min < 0
    return max if self <=> max > 0
    self
  end
  
end

# 关于x的以mean为期望,variance为方差的正态分布函数
# 不给出mean和variance参数时是标准正态分布函数
def Math.normal_distribution(x, mean = 0.0, variance = 1.0)
  exp(-(x - mean) ** 2 / (2 * variance)) / sqrt(2 * self::PI * variance)
end

# Complex
class Complex
  
  # 获取此复数对应的定义域着色
  # lightness_base: 用于计算亮度的函数中的底数,越小越亮,必须在0和1之间
  # saturation: 饱和度
  def to_color(lightness_base = 0.5, saturation = 1.0)
    raise unless lightness_base.between?(0, 1)
    exponent = abs
    hue = exponent == 0 ? 0.0 : arg
    lightness = 1 - lightness_base ** exponent
    Color.hsl(hue, saturation, lightness)
  end
  
  # 获取此复数对应的结构化的定义域着色
  # 这种着色方案有特殊效果,具体看参数说明
  # discontinuity_base: 当此复数的模是达到参数的整数次幂时,亮度骤然变为最低
  # saturation: 饱和度
  # min_lightness: 不考虑网格和射线时能达到的最低亮度
  # lightness_range: 不考虑网格和射线时能达到的最高和最低亮度之差
  # grid_hue: 网格的色相
  # rays_hue: 射线的色相
  # grid_lightness: 网格的亮度
  # rays_lightness: 射线的亮度
  # grid_density: 网格线的密度,复数的实部或虚部是参数的倒数的整数倍时出现网格
  # rays_density: 射线的密度,复数的俯角是参数的倒数的整数倍时出现射线
  # grid_radius: 网格线的宽度的一半,不包括扩散出去的部分
  # rays_radius: 射线的宽度的一半,不包括扩散出去的部分
  # grid_restraint: 网格线的扩散度,越大越模糊
  # rays_restraint: 射线的扩散度,越大越模糊
  # grid_opacity: 网格的不透明度,如果不想显示网格请设为0.0
  # rays_opacity: 射线的不透明度,如果不想显示射线请设为0.0
  def to_color_structured(discontinuity_base = 2.0, saturation = 1.0,
      min_lightness = 0.3, lightness_range = 0.4, grid_hue = 0.0,
      rays_hue = 0.0, grid_lightness = 0.0, rays_lightness = 1.0,
      grid_density = 1.0, rays_density = 6.0, grid_radius = 0.005,
      rays_radius = 0.01, grid_restraint = 60.0, rays_restraint = 50.0,
      grid_opacity = 0.2, rays_opacity = 0.8)
    antilog = abs
    hue = antilog == 0 ? 0.0 : arg
   
    lightness_phase = Math.log(antilog, discontinuity_base).modulo(1)
    lightness_phase = 0.0 if lightness_phase.nan?
    lightness = lightness_phase * lightness_range + min_lightness
   
    color = Color.hsl(hue, saturation, lightness)
    f = Math.sqrt(2 * Math::PI)
   
    dense_real = real * grid_density
    real_distance = (dense_real - dense_real.round).abs
    dense_imaginary = imaginary * grid_density
    imaginary_distance = (dense_imaginary - dense_imaginary.round).abs
    grid_distance = [[real_distance, imaginary_distance].min, grid_radius].max
    grid_excursion = (grid_distance - grid_radius) * grid_restraint
    grid_alpha = Math.normal_distribution(grid_excursion) * f * grid_opacity
    grid_color = Color.hsl(grid_hue, saturation, grid_lightness, grid_alpha)
    color.blend!(grid_color)
   
    rays_distances = Array.new(rays_density) do |i|
      slope = Math.tan(i / rays_density * Math::PI)
      (slope * real - imaginary).abs / Math.hypot(slope, -1)
    end
    rays_distance = [rays_distances.min, rays_radius].max
    rays_excursion = (rays_distance - rays_radius) * rays_restraint
    rays_alpha = Math.normal_distribution(rays_excursion) * f * rays_opacity
    rays_color = Color.hsl(rays_hue, saturation, rays_lightness, rays_alpha)
    color.blend!(rays_color)
  end
end

# Bitmap
class Bitmap
  # 绘制复变函数的函数图像
  # function: 需要绘制的函数,提供接受一个复数参数并返回复数的call方法
  # delta_x: 图中1像素的距离代表的大小
  # structured: 是否采用结构化的图像,见Complex#to_color_structured
  # ox: 自变量取0+0i时所在的位置的相对于位图的x坐标
  # oy: 自变量取0+0i时所在的位置的相对于位图的y坐标
  def draw_function(function, delta_x, structured = false,
    ox = Graphics.width / 2, oy = Graphics.height / 2)
    width.times do |x|
      height.times do |y|
        real = (x - ox) * delta_x
        imaginary = (oy - y) * delta_x
        complex = function.call(Complex(real, imaginary))
        color = structured ? complex.to_color_structured : complex.to_color
        set_pixel(x, y, color)
      end
    end
  end
end
[/pre]一个简单的用于玩耍的代码(依赖这玩意)(由于我用了RGD所以无视分辨率限制了)(虽然不用多线程能画得更快,但是那样就看不到图像慢慢绘制出来的过程了):[pre lang="ruby"]# 玩耍方法:在控制台输入你想要画的函数,然后等待即可
Graphics.resize_screen(1024, 768)
sprite = Sprite.new
sprite.bitmap = Bitmap.new(Graphics.width, Graphics.height)
Thread.start do
  loop do
    print("f(z) = ")
    string = $stdin.gets
    function = eval("->(z) { #{string} }")
    puts("Please wait...")
    time = Time.now
    sprite.bitmap.draw_function(function, 0.008, true) rescue puts($!.message)
    printf("Completed after %d seconds.\n", Time.now - time)
  end
end

rgss_stop
[/pre]配上CMath后玩耍体验更佳:[pre lang="ruby"]#==============================================================================
# * CMath
#------------------------------------------------------------------------------
# Trigonometric and transcendental functions for complex numbers.
# CMath is a library that provides trigonometric and transcendental functions
# for complex numbers. The functions in this module accept integers, floating-
# point numbers or complex numbers as arguments.
# Note that the selection of functions is similar, but not identical, to that
# in module math. The reason for having two modules is that some users aren't
# interested in complex numbers, and perhaps don't even know what they are. They
# would rather have Math.sqrt(-1) raise an exception than return a complex
# number.
# For more information you can see Complex class.
#==============================================================================

module CMath

  include Math

  alias exp! exp
  alias log! log
  alias log2! log2
  alias log10! log10
  alias sqrt! sqrt
  alias cbrt! cbrt

  alias sin! sin
  alias cos! cos
  alias tan! tan

  alias sinh! sinh
  alias cosh! cosh
  alias tanh! tanh

  alias asin! asin
  alias acos! acos
  alias atan! atan
  alias atan2! atan2

  alias asinh! asinh
  alias acosh! acosh
  alias atanh! atanh

  #--------------------------------------------------------------------------
  # * exp(z)
  #--------------------------------------------------------------------------
  # Math::E raised to the z power
  #--------------------------------------------------------------------------
  def exp(z)
    if z.real?
      exp!(z)
    else
      ere = exp!(z.real)
      Complex(ere * cos!(z.imag),
              ere * sin!(z.imag))
    end
  end

  #--------------------------------------------------------------------------
  # * log(z, b=::Math::E)
  #--------------------------------------------------------------------------
  # Returns the natural logarithm of Complex. If a second argument is given,
  # it will be the base of logarithm.
  #--------------------------------------------------------------------------
  def log(*args)
    z, b = args
    if z.real? and z >= 0 and (b.nil? or b >= 0)
      log!(*args)
    else
      a = Complex(log!(z.abs), z.arg)
      if b
        a /= log(b)
      end
      a
    end
  end

  #--------------------------------------------------------------------------
  # * log2(z)
  #--------------------------------------------------------------------------
  # Returns the base 2 logarithm of z
  #--------------------------------------------------------------------------
  def log2(z)
    if z.real? and z >= 0
      log2!(z)
    else
      log(z) / log!(2)
    end
  end

  #--------------------------------------------------------------------------
  # * log10(z)
  #--------------------------------------------------------------------------
  # Returns the base 10 logarithm of z
  #--------------------------------------------------------------------------
  def log10(z)
    if z.real? and z >= 0
      log10!(z)
    else
      log(z) / log!(10)
    end
  end

  #--------------------------------------------------------------------------
  # * sqrt(z)
  #--------------------------------------------------------------------------
  # Returns the non-negative square root of Complex.
  #--------------------------------------------------------------------------
  def sqrt(z)
    if z.real?
      if z < 0
        Complex(0, sqrt!(-z))
      else
        sqrt!(z)
      end
    else
      if z.imag < 0 ||
        (z.imag == 0 && z.imag.to_s[0] == '-')
        sqrt(z.conjugate).conjugate
      else
        r = z.abs
        x = z.real
        Complex(sqrt!((r + x) / 2), sqrt!((r - x) / 2))
      end
    end
  end

  #--------------------------------------------------------------------------
  # * cbrt(z)
  #--------------------------------------------------------------------------
  # Returns the principal value of the cube root of z
  #--------------------------------------------------------------------------
  def cbrt(z)
    if z.real?
      cbrt!(z)
    else
      Complex(z) ** (1.0/3)
    end
  end

  #--------------------------------------------------------------------------
  # * sin(z)
  #--------------------------------------------------------------------------
  # Returns the sine of z, where z is given in radians
  #--------------------------------------------------------------------------
  def sin(z)
    if z.real?
      sin!(z)
    else
      Complex(sin!(z.real) * cosh!(z.imag),
              cos!(z.real) * sinh!(z.imag))
    end
  end

  #--------------------------------------------------------------------------
  # * cos(z)
  #--------------------------------------------------------------------------
  # Returns the cosine of z, where z is given in radians
  #--------------------------------------------------------------------------
  def cos(z)
    if z.real?
      cos!(z)
    else
      Complex(cos!(z.real) * cosh!(z.imag),
              -sin!(z.real) * sinh!(z.imag))
    end
  end

  #--------------------------------------------------------------------------
  # * tan(z)
  #--------------------------------------------------------------------------
  # Returns the tangent of z, where z is given in radians
  #--------------------------------------------------------------------------
  def tan(z)
    if z.real?
      tan!(z)
    else
      sin(z) / cos(z)
    end
  end

  #--------------------------------------------------------------------------
  # * sinh(z)
  #--------------------------------------------------------------------------
  # Returns the hyperbolic sine of z, where z is given in radians
  #--------------------------------------------------------------------------
  def sinh(z)
    if z.real?
      sinh!(z)
    else
      Complex(sinh!(z.real) * cos!(z.imag),
              cosh!(z.real) * sin!(z.imag))
    end
  end

  #--------------------------------------------------------------------------
  # * cos(z)
  #--------------------------------------------------------------------------
  # Returns the hyperbolic cosine of z, where z is given in radians
  #--------------------------------------------------------------------------
  def cosh(z)
    if z.real?
      cosh!(z)
    else
      Complex(cosh!(z.real) * cos!(z.imag),
              sinh!(z.real) * sin!(z.imag))
    end
  end

  #--------------------------------------------------------------------------
  # * tanh(z)
  #--------------------------------------------------------------------------
  # Returns the hyperbolic tangent of z, where z is given in radians
  #--------------------------------------------------------------------------
  def tanh(z)
    if z.real?
      tanh!(z)
    else
      sinh(z) / cosh(z)
    end
  end

  #--------------------------------------------------------------------------
  # * asin(z)
  #--------------------------------------------------------------------------
  # Returns the arc sine of z
  #--------------------------------------------------------------------------
  def asin(z)
    if z.real? and z >= -1 and z <= 1
      asin!(z)
    else
      (-1.0).i * log(1.0.i * z + sqrt(1.0 - z * z))
    end
  end

  #--------------------------------------------------------------------------
  # * acos(z)
  #--------------------------------------------------------------------------
  # Returns the arc cosine of z
  #--------------------------------------------------------------------------
  def acos(z)
    if z.real? and z >= -1 and z <= 1
      acos!(z)
    else
      (-1.0).i * log(z + 1.0.i * sqrt(1.0 - z * z))
    end
  end

  #--------------------------------------------------------------------------
  # * atan(z)
  #--------------------------------------------------------------------------
  # Returns the arc tangent of z
  #--------------------------------------------------------------------------
  def atan(z)
    if z.real?
      atan!(z)
    else
      1.0.i * log((1.0.i + z) / (1.0.i - z)) / 2.0
    end
  end

  #--------------------------------------------------------------------------
  # * atan2(z)
  #--------------------------------------------------------------------------
  # returns the arc tangent of y divided by x using the signs of y and x to
  # determine the quadrant
  #--------------------------------------------------------------------------
  def atan2(y,x)
    if y.real? and x.real?
      atan2!(y,x)
    else
      (-1.0).i * log((x + 1.0.i * y) / sqrt(x * x + y * y))
    end
  end

  #--------------------------------------------------------------------------
  # * asinh(z)
  #--------------------------------------------------------------------------
  # returns the inverse hyperbolic sine of z
  #--------------------------------------------------------------------------
  def asinh(z)
    if z.real?
      asinh!(z)
    else
      log(z + sqrt(1.0 + z * z))
    end
  end

  #--------------------------------------------------------------------------
  # * acosh(z)
  #--------------------------------------------------------------------------
  # returns the inverse hyperbolic cosine of z
  #--------------------------------------------------------------------------
  def acosh(z)
    if z.real? and z >= 1
      acosh!(z)
    else
      log(z + sqrt(z * z - 1.0))
    end
  end

  #--------------------------------------------------------------------------
  # * atanh(z)
  #--------------------------------------------------------------------------
  # returns the inverse hyperbolic tangent of z
  #--------------------------------------------------------------------------
  def atanh(z)
    if z.real? and z >= -1 and z <= 1
      atanh!(z)
    else
      log((1.0 + z) / (1.0 - z)) / 2.0
    end
  end

  module_function :exp!
  module_function :exp
  module_function :log!
  module_function :log
  module_function :log2!
  module_function :log2
  module_function :log10!
  module_function :log10
  module_function :sqrt!
  module_function :sqrt
  module_function :cbrt!
  module_function :cbrt

  module_function :sin!
  module_function :sin
  module_function :cos!
  module_function :cos
  module_function :tan!
  module_function :tan

  module_function :sinh!
  module_function :sinh
  module_function :cosh!
  module_function :cosh
  module_function :tanh!
  module_function :tanh

  module_function :asin!
  module_function :asin
  module_function :acos!
  module_function :acos
  module_function :atan!
  module_function :atan
  module_function :atan2!
  module_function :atan2

  module_function :asinh!
  module_function :asinh
  module_function :acosh!
  module_function :acosh
  module_function :atanh!
  module_function :atanh

  module_function :frexp
  module_function :ldexp
  module_function :hypot
  module_function :erf
  module_function :erfc
  module_function :gamma
  module_function :lgamma

end
[/pre] QQ截图20180909200109.png
QQ截图20180909200045.png
顺便一提,虽然绘制速度慢到让人吐血,但是由于不支持后台运行,请将焦点保持在窗口上,慢慢看着它画完;我也不是很清楚为什么RGD不支持这个
再顺便一提,要是RGD有更高的Ruby版本就好了,我就不用写Complex(1, 2),直接写1+2i了

评分

参与人数 1赞 +1 收起 理由
David_Exmachina + 1

查看全部评分

小仙女一枚~

可以吃的头衔

梦石
0
星屑
1015
在线时间
424 小时
注册时间
2010-10-17
回帖
586
发表于 2018-9-10 03:43:57 | 显示全部楼层
尝试用Shader完成这个效果把(效率会让你惊呆

点评

主要耗时不在绘制,而是计算。1024*768次循环的复杂的没有经过优化的计算,能不慢吗  发表于 2018-9-10 19:13
……
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册会员

本版积分规则

Powered by Discuz! X5.0 © 2001-2026 Discuz! Team.

在本版发帖返回顶部