LoginSignup
2
2

More than 5 years have passed since last update.

MokkosuでHypotrochoidを作ってみた

Posted at

はじめに

SKPSKSさんの記事を読んで、私も関数型言語 Mokkosu で Hypotrochoid を作ってみました。

Hypotrochoid01.png

ソースコード

Hypotrochoid.mok
#=============================================================================
#! @file    Hypotrochoid.mok
#! @brief   Yet Another Hypotrochoid
#! @author  kielnow
#=============================================================================
#__define "CONSOLE_APPLICATION";

include "Print.mok";
include "List.mok";
include "Graphics.mok";

using System;
using System.Collections;

#-----------------------------------------------------------------------------
# AFX
#-----------------------------------------------------------------------------
fun map f = {
  [] -> [];
  x::xs -> f x :: map f xs;
};
fun concat = {
  [] -> [];
  x::xs -> x ++ concat xs;
};
let concat_map f lis = concat (map f lis);
let __for_bind lis f = concat_map f lis;

let double_to_float (d : Double) = call Convert::ToSingle(d);
let float_to_double (f : {Single}) = call Convert::ToDouble(f);

#-----------------------------------------------------------------------------
# Global constants
#-----------------------------------------------------------------------------
let (screen_width,screen_height) = (800,600);
let fps = 120;
let fps_inv = 1000 / fps;
let title = "Hypotrochoid";

let (origin_x, origin_y) as origin = (screen_width/2,screen_height/2);
let points_max = 8192;

#-----------------------------------------------------------------------------
# Global states
#-----------------------------------------------------------------------------
# outer circle
let radius_o = ref 200.0;
let centre_o = ref (int_to_double origin_x, int_to_double origin_y);

# inner circle
let radius_i = ref 127.0;
let centre_i =
  let ri = !radius_i in
  let ro = !radius_o in
  let (xo,yo) = !centre_o in
  ref (xo +.ro-.ri, yo);

# distance from the centre of the inner circle
let dist = ref 226.0;
let delta_dist = ref 16.0;

# angle
let theta = ref 0.0;
let delta_theta = ref (!delta_dist *. !radius_i /. (!dist *. !radius_o));

let points = newarr<PointF>(points_max);
let points_begin = ref 0;
let points_end = ref 1;

let points_loop = newarr<PointF>(5);

let point = ref (0.0,0.0);

let pause = ref false;
let show_circle = ref true;

#-----------------------------------------------------------------------------
# Utilities
#-----------------------------------------------------------------------------
let get_state () = 
  let ro = !radius_o in
  let ri = !radius_i in
  let d  = !dist in
  let (xo,yo) as co = !centre_o in
  let (xi,yi) as ci = !centre_i in
  ((ro,ri,d),co,ci);

let reset_delta_theta () =
  let d = ((!delta_dist *. !radius_i) /. (!dist *. !radius_o)) in
  do
    delta_theta := min 0.1 (max 0.00001 d);
  end;

let to_PointF (x,y) = new PointF(double_to_float x, double_to_float y);
let from_PointF (p : {PointF}) = (float_to_double p.get_X(), float_to_double p.get_Y());

#-----------------------------------------------------------------------------
# GraphicsEx
#-----------------------------------------------------------------------------
let mokkosu_brush = new_solid_brush 103 128 170;
let mokkosu_pen = new_pen 103 128 170 1;
let mokkosu_pen2 = new_pen 103 128 170 2;

let gradient_brush =
  let c1 = call Color::get_Cyan() in
  let c2 = call Color::get_Blue() in
  let p1 = new Point(0,0) in
  let p2 = new Point(screen_width,screen_height) in
  new Drawing2D.LinearGradientBrush(p1,p2,c1,c2);
let gradient_pen = new Pen(gradient_brush,1);
let gradient_pen2 = new Pen(gradient_brush,2);

let gx_clear gr brush = fill_rectangle gr brush 0 0 screen_width screen_height;

let gx_draw_circle gr pen (x,y) r =
  let (x_,y_) = (double_to_int x, double_to_int y) in
  let r_ = double_to_int r in
  let d = 2*r_ in draw_ellipse gr pen (x_-r_) (y_-r_) d d;

let gx_draw_line gr pen (x1,y1) (x2,y2) = 
  let (x1_,y1_) = (double_to_int x1, double_to_int y1) in
  let (x2_,y2_) = (double_to_int x2, double_to_int y2) in
  draw_line gr pen x1_ y1_ x2_ y2_;

let gx_draw_curve (gr : {Graphics}) (pen : {Pen}) (ps : {PointF[]}) = gr.DrawCurve(pen, ps);
let gx_draw_curve_ofs (gr : {Graphics}) (pen : {Pen}) (ps : {PointF[]}) (ofs : Int) (num : Int) = gr.DrawCurve(pen, ps, ofs, num);

#-----------------------------------------------------------------------------
# Hypotrochoid
#-----------------------------------------------------------------------------
let hypotrochoid (ox,oy) (ro,ri,d) t =
  let a = ro -. ri in
  let b = a /. ri in
  let x = a *. cos t +. d *. cos (b *. t) in
  let y = a *. sin t -. d *. sin (b *. t) in
  (ox+.x,oy+.y);

#-----------------------------------------------------------------------------
# Scene init
#-----------------------------------------------------------------------------
let tick_init () =
  let (((ro,ri,d) as ht), ((xo,yo) as co), ((xi,yi) as ci)) = get_state () in
  let p = hypotrochoid co ht !theta in
  do
    reset_delta_theta ();
    theta := 0.0;
    points_begin := 1;
    points_end   := 2;
    stelem<PointF>(points, 0, to_PointF p);
    stelem<PointF>(points, 1, to_PointF p);
    stelem<PointF>(points, 2, to_PointF p);
    point := p;
    pause := false;
    show_circle := true;
    switch "main";
  end;    

#-----------------------------------------------------------------------------
# Scene main
#-----------------------------------------------------------------------------
let tick_main () = if !pause -> redraw () else
  let (((ro,ri,d) as ht), co, _) = get_state () in
  let new_theta = !theta +. !delta_theta in
  let (b,e) = (!points_begin, !points_end) in
  let e1 = (e + 1) % points_max in
  let e2 = (e + 2) % points_max in
  let p = hypotrochoid co ht new_theta in
  do
    theta := new_theta;
    stelem<PointF>(points, e, to_PointF p);
    stelem<PointF>(points, e1, to_PointF p);
    points_end := e1;
    if e2 == b ->
      let b1 = (b+1) % points_max in
      do
        stelem<PointF>(points_loop, 0, ldelem<PointF>(points, points_max-3));
        stelem<PointF>(points_loop, 1, ldelem<PointF>(points, points_max-2));
        stelem<PointF>(points_loop, 2, ldelem<PointF>(points, points_max-1));
        stelem<PointF>(points_loop, 3, ldelem<PointF>(points, 0));
        stelem<PointF>(points_loop, 4, ldelem<PointF>(points, 1));
        points_begin := b1;
      end else ();
    point := p;
    redraw (); 
  end;

let draw_main (gr : {Graphics}) =
  let ((ro,ri,d), ((xo,yo) as co), _) = get_state () in
  let rd = ro-.ri in
  let (xi_,yi_) as ci_ = (xo +. rd *. cos !theta, yo +. rd *. sin !theta) in
  let p = !point in
  let pe = ldelem<PointF>(points, (!points_end + points_max-2) % points_max) in
  let (b,e) = (!points_begin, !points_end) in
  let n = if b < e -> e-b-1 else points_max-b-1 in
  let m = if b < e -> 0 else e-1 in
  let pen = gradient_pen in
  do
    gr.set_SmoothingMode(sget Drawing2D.SmoothingMode::AntiAlias);
    gx_clear gr white_brush;
    if !show_circle -> do
      gx_draw_circle gr mokkosu_pen co ro;
      gx_draw_circle gr mokkosu_pen ci_ ri;
      gx_draw_line gr mokkosu_pen ci_ p;
      gx_draw_circle gr mokkosu_pen p 8.0;
    end else ();
    if n > 0 -> gx_draw_curve_ofs gr pen points b n else ();
    if m > 0 -> do
      gx_draw_curve_ofs gr pen points_loop 2 1;
      gx_draw_curve_ofs gr pen points 0 m;
    end else ();
  end;

#-----------------------------------------------------------------------------
# Main
#-----------------------------------------------------------------------------
let main () = do
  # setup window
  set_size screen_width screen_height;
  set_title title;
  set_speed fps_inv;

  # init
  scene "init" {
    ~Tick -> tick_init ();
    _ -> (); 
  };

  # main
  scene "main" {
    ~Tick -> tick_main ();
    ~MouseDown(LeftButton,_,_) -> pause := not !pause;
    ~MouseDown(RightButton,_,_) -> show_circle := not !show_circle;
    ~Draw(gr) -> draw_main gr;
    _ -> ();
  };

  # startup
  show_window "init";
  end;

#-----------------------------------------------------------------------------
# Entry point
#-----------------------------------------------------------------------------
do main ();

解説

Mokkosu 1.3.1 から追加された array を使用しています。というか、これがないと .NET の DrawCurve 関数が使えませんでした。

感想

.NET の配列を Mokkosu から自然に扱えるようになっていい感じと思いました。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2