免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 4219 | 回复: 4

Perl数据可视化 - 美元汇率3D图表 [复制链接]

论坛徽章:
12
子鼠
日期:2014-10-11 16:46:482016科比退役纪念章
日期:2018-03-16 10:24:0515-16赛季CBA联赛之山东
日期:2017-11-10 14:32:142016科比退役纪念章
日期:2017-09-02 15:42:4715-16赛季CBA联赛之佛山
日期:2017-08-28 17:11:5515-16赛季CBA联赛之浙江
日期:2017-08-24 16:55:1715-16赛季CBA联赛之青岛
日期:2017-08-17 19:55:2415-16赛季CBA联赛之天津
日期:2017-06-29 10:34:4315-16赛季CBA联赛之四川
日期:2017-05-16 16:38:55黑曼巴
日期:2016-07-19 15:03:112015亚冠之萨济拖拉机
日期:2015-05-22 11:38:5315-16赛季CBA联赛之北京
日期:2019-08-13 17:30:53
发表于 2017-10-24 19:26 |显示全部楼层
本帖最后由 523066680 于 2017-10-24 20:39 编辑





只为花俏,实际使用还是单纯的线条更通透

  1. =info
  2.     外汇牌价-数据可视化
  3.     Auth: 523066680
  4.     Date: 2017-10
  5.     https://github.com/vicyang/Exchange-Rates
  6. =cut

  7. use utf8;
  8. use Encode;
  9. use autodie;
  10. use Storable;
  11. use feature 'state';
  12. use Font::FreeType;
  13. use Time::HiRes qw/sleep/;
  14. use Time::Local;
  15. use File::Slurp;
  16. use Data::Dumper;
  17. use List::Util qw/sum min max/;

  18. use IO::Handle;
  19. use OpenGL qw/ :all /;
  20. use OpenGL::Config;
  21. use Math::Geometry::Delaunay;

  22. STDOUT->autoflush(1);

  23. BEGIN
  24. {
  25.     our $WinID;
  26.     our $HEIGHT = 500;
  27.     our $WIDTH  = 700;
  28.     our ($rx, $ry, $rz, $zoom) = (0.0, 0.0, 0.0, 1.0);
  29.     our ($mx, $my, $mz) = (0.0, 0.0, 0.0);

  30.     our $DB_File = "../Data/2017.perldb.bin";
  31.     our $hash = retrieve( $DB_File );
  32.     our [url=home.php?mod=space&uid=8087803]@days[/url] = (sort keys %$hash);
  33.     our $begin = 0;                  #展示数据的起始索引
  34.     sub col { 2 };

  35.     our %month;
  36.     our ($MIN, $MAX) = (1000.0, 0.0);
  37.     my $m;
  38.     for my $d (@days)
  39.     {
  40.         $m = substr($d, 0, 7);  #年+月作为 key
  41.         if ( not exists $month{$m} ) {
  42.             $month{$m} = { 'min' => 1000.0, 'max' => 0.0, 'delta' => undef, 'ply' => 1.0 };
  43.         }
  44.         for my $t ( keys %{$hash->{$d}} )
  45.         {
  46.             if ($hash->{$d}{$t}[col] < $MIN) { $MIN = $hash->{$d}{$t}[col] }
  47.             if ($hash->{$d}{$t}[col] > $MAX) { $MAX = $hash->{$d}{$t}[col] }
  48.             if ($hash->{$d}{$t}[col] < $month{$m}->{min}) { $month{$m}->{min} = $hash->{$d}{$t}[col] }
  49.             if ($hash->{$d}{$t}[col] > $month{$m}->{max}) { $month{$m}->{max} = $hash->{$d}{$t}[col] }
  50.         }
  51.     }

  52.     for my $m ( keys %month )
  53.     {
  54.         $month{$m}->{delta} = $month{$m}->{max} - $month{$m}->{min};
  55.         $month{$m}->{ply}   = 300.0 / $month{$m}->{delta}
  56.             if ($month{$m}->{delta} != 0);
  57.     }

  58.     $DELTA = $MAX - $MIN;
  59.     $PLY = 300.0/$DELTA;

  60.     printf("Done.\n");
  61.     printf("min: %.3f, max: %.3f\n", $MIN, $MAX );

  62.     our $tobj;
  63.     our ($font, $size) = ("C:/windows/fonts/msyh.ttf", 16);
  64.     our $dpi = 100;
  65.     our $face = Font::FreeType->new->face($font);
  66.     $face->set_char_size($size, $size, $dpi, $dpi);
  67. }

  68. INIT
  69. {
  70.     our %TEXT;
  71.     print "Loading contours ... ";
  72.     my $code;
  73.     my $char;
  74.     foreach $code (0x00..0x7F)
  75.     {
  76.         $char = chr( $code );
  77.         $TEXT{ $char } = get_contour( $char );
  78.     }

  79.     foreach $char (split //, "本当天年月日最小大高低平均值落差,:。")
  80.     {
  81.         $TEXT{ $char } = get_contour( $char );
  82.     }
  83.     print "Done\n";

  84.     #创建颜色插值表
  85.     our $table_size = 320;
  86.     our @color_idx;
  87.     for (0 .. $table_size) {
  88.         push @color_idx, { 'R' => 0.0, 'G' => 0.0, 'B' => 0.0 };
  89.     }

  90.     # fill_color( 20, 60, 1.0, 0.3, 0.3);
  91.     # fill_color(100,100, 1.0, 0.6, 0.0);
  92.     # fill_color(200,100, 0.2, 0.8, 0.2);
  93.     # fill_color(300,300, 0.2, 0.6, 1.0);

  94.     fill_color( 20,200, 1.0, 0.6, 0.2);
  95.     fill_color(150,200, 0.3, 1.0, 0.3);
  96.     fill_color(280,300, 0.2, 0.5, 1.0);

  97.     sub fill_color
  98.     {
  99.         my %insert;
  100.         @{insert}{'offset', 'length', 'R', 'G', 'B'} = @_;
  101.         my $site;
  102.         my $ref;
  103.         my $tc;

  104.         for my $i (  -$insert{length} .. $insert{length} )
  105.         {
  106.             $site = $i + $insert{offset};
  107.             next if ($site < 0 or $site > $table_size);
  108.             $ref = $color_idx[$site];
  109.             for my $c ('R', 'G', 'B')
  110.             {
  111.                 $tc = $insert{$c} - abs( $insert{$c} / $insert{length} * $i),  #等量划分 * step
  112.                 $ref->{$c} = $ref->{$c} > $tc ? $ref->{$c} : $tc  ;
  113.             }
  114.         }
  115.     }

  116.     sub triangulation
  117.     {
  118.         my $points = shift;
  119.         my $tri = new Math::Geometry::Delaunay();
  120.         $tri->addPoints( $points );
  121.         $tri->doEdges(1);
  122.         $tri->doVoronoi(1);
  123.         $tri->triangulate();
  124.         return $tri->elements();
  125.     }
  126. }

  127. =struct
  128.     $hash = {
  129.         'day1' => { 'time1' => [@data], 'time2' => [@data] },
  130.         'day2' => { ... }, ...
  131.      }
  132. =cut

  133. &main();

  134. sub display
  135. {
  136.     state @times;
  137.     state $i = 0;
  138.     state ($min, $max, $delta, $ply);

  139.     our ($hash, @days, $begin, $MIN, @color_idx);
  140.     my $day;
  141.     my $hour, $time, $last;
  142.     glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

  143.     glColor4f(0.8, 0.8, 0.8, 0.5);

  144.     glPushMatrix();
  145.     glScalef( $zoom, $zoom, $zoom );
  146.     glRotatef($rx, 1.0, 0.0, 0.0);
  147.     glRotatef($ry, 0.0, 1.0, 0.0);
  148.     glRotatef($rz, 0.0, 0.0, 1.0);
  149.     glTranslatef($mx, $my, $mz);

  150.     my $m = substr($days[$begin], 0, 7);
  151.     $MIN = $month{$m}->{min};
  152.     $MAX = $month{$m}->{max};
  153.     $PLY = $month{$m}->{ply};
  154.     $DELTA = $month{$m}->{delta};

  155.     my $bright = 1.0;
  156.     my $color;
  157.     my @points = ();

  158.     for my $di ( reverse $begin .. $begin+10 )
  159.     {
  160.         next if ( $di < 0 or $di > $#days );
  161.         $day = $days[$di];
  162.         #时间清零,避免受到上一次影响
  163.         [url=home.php?mod=space&uid=1775185]@Times[/url] = ();
  164.         @rates = ();
  165.         #时间排序
  166.         @times = sort keys %{ $hash->{$day} };
  167.         @rates = map { $hash->{$day}{$_}[col] } @times;

  168.         my $t1, $x1, $y1, $last_x;
  169.         $bright = $di == $begin ? 2.0 : 0.9*(1.0-($di-$begin)/10.0);
  170.         glBegin(GL_LINE_STRIP);
  171.         for my $ti ( 0 .. $#times )
  172.         {
  173.             $t1 = $times[$ti];
  174.             $t1 =~ /^0?(\d+):0?(\d+)/;
  175.             $x1 = ($1 * 60.0 + $2)/3.0;
  176.             $y1 = ($hash->{$day}{$t1}[col]-$MIN)*$PLY;

  177.             $color = $color_idx[int($y1)];
  178.             glColor4f( $color->{R}*$bright, $color->{G}*$bright, $color->{B}*$bright, 1.0 );
  179.             glVertex3f($x1, $y1, -($di-$begin)*30.0 );
  180.             push @points, [ $x1, -($di-$begin)*30.0, $y1 ];
  181.         }
  182.         glEnd();
  183.     }

  184.     glEnable(GL_LIGHTING);
  185.     glColor4f(1.0, 1.0, 1.0, 1.0);
  186.     my $tri;
  187.     my @tpa, @tpb, @norm;
  188.     $tri = triangulation( \@points );
  189.     glBegin(GL_TRIANGLES);
  190.     for my $a ( @$tri )
  191.     {
  192.         for my $i ( 0 .. 2 )
  193.         {
  194.             $tpa[$i] = $a->[1][$i] - $a->[0][$i] ;
  195.             $tpb[$i] = $a->[2][$i] - $a->[0][$i] ;
  196.         }
  197.         normcrossprod( \@tpa, \@tpb, \@norm );
  198.         glNormal3f( @norm );
  199.         for my $b ( @$a ) {
  200.             $color = $color_idx[int($b->[2])];
  201.             glColor4f( $color->{R}, $color->{G}, $color->{B}, 0.5 );
  202.             glVertex3f( @$b[0,2,1] );
  203.         }
  204.     }
  205.     glEnd();
  206.     glDisable(GL_LIGHTING);

  207.     glutStrokeHeight(GLUT_STROKE_MONO_ROMAN);

  208.     #横轴
  209.     glLineWidth(1.0);
  210.     glColor3f(1.0, 1.0, 1.0);
  211.     for (  my $mins = 0.0; $mins <= 1440.0; $mins+=40.0 )
  212.     {
  213.         $time = sprintf "%02d:%02d", int($mins/60), $mins % 60;
  214.         glPushMatrix();
  215.             glTranslatef($mins/3.0, -80.0, 0.0);
  216.             #glRotatef(90.0, 1.0, 0.0, 0.0);
  217.             #glRotatef(180.0, 0.0, 1.0, 0.0);
  218.             glRotatef(90.0, 0.0, 0.0, 1.0);
  219.             glScalef(0.1, 0.08, 0.1);
  220.             glutStrokeString(GLUT_STROKE_MONO_ROMAN, $time );
  221.         glPopMatrix();
  222.     }

  223.     #竖轴
  224.     for ( my $y = 0.0; $y<=300.0; $y+=15.0 )
  225.     {
  226.         glColor4f( @{$color_idx[int($y)]}{'R','G','B'}, 1.0 );
  227.         glPushMatrix();
  228.             glTranslatef(-80.0, $y, 0.0);
  229.             glScalef(0.1, 0.1, 0.1);
  230.             glutStrokeString(GLUT_STROKE_MONO_ROMAN,
  231.                 sprintf "%.3f", ( $DELTA *$y/300.0 + $MIN)/100.0 );
  232.         glPopMatrix();
  233.     }

  234.     #当天的数据特征
  235.     my $min = 1000.0, $max = 0.0;
  236.     for my $t ( keys %{$hash->{$days[$begin]}} )
  237.     {
  238.         if ($hash->{$days[$begin]}{$t}[col] < $min) { $min = $hash->{$days[$begin]}{$t}[col] }
  239.         if ($hash->{$days[$begin]}{$t}[col] > $max) { $max = $hash->{$days[$begin]}{$t}[col] }
  240.     }
  241.     my $delta = $max - $min;
  242.     my ($yy, $mm, $dd) = split(/\D/, $days[$begin] );
  243.     glColor3f(1.0, 1.0, 1.0);
  244.     glPushMatrix();
  245.     glTranslatef(-80.0, 320.0, 0.0);
  246.     draw_string(
  247.         sprintf("%s年%s月%s日 最高:%.3f 最低:%.3f 落差: %.3f\n",
  248.             $yy, $mm, $dd, $max/100.0, $min/100.0, $delta/100.0)
  249.     );
  250.     glPopMatrix();

  251.     # glPushMatrix();
  252.     # glTranslatef(50.0, 350.0, 0.0);
  253.     # draw_string(
  254.     #     sprintf("本月最高:%.3f 最低:%.3f 落差: %.3f\n",
  255.     #         $month{$m}->{max}/100.0,
  256.     #         $month{$m}->{min}/100.0, $month{$m}->{delta}/100.0)
  257.     # );
  258.     # glPopMatrix();

  259.     glPopMatrix();
  260.     glutSwapBuffers();
  261. }

  262. sub idle
  263. {
  264.     sleep 0.05;
  265.     glutPostRedisplay();
  266. }

  267. sub init
  268. {
  269.     glClearColor(0.0, 0.0, 0.0, 1.0);
  270.     #glClearColor(0.1, 0.2, 0.3, 1.0);
  271.     glPointSize(1.0);
  272.     glLineWidth(1.0);
  273.     glEnable(GL_BLEND);
  274.     glEnable(GL_DEPTH_TEST);
  275.     glEnable(GL_POINT_SMOOTH);
  276.     glEnable(GL_LINE_SMOOTH);
  277.     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  278.     #glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

  279.     glEnable(GL_LIGHTING);

  280.     my $ambient  = OpenGL::Array->new( 4, GL_FLOAT);
  281.     my $specular = OpenGL::Array->new( 4, GL_FLOAT);
  282.     my $diffuse  = OpenGL::Array->new( 4, GL_FLOAT);
  283.     my $shininess = OpenGL::Array->new( 1, GL_FLOAT);

  284.     my $light_position = OpenGL::Array->new( 4, GL_FLOAT);
  285.     my $light_specular = OpenGL::Array->new( 4, GL_FLOAT);
  286.     my $light_diffuse  = OpenGL::Array->new( 4, GL_FLOAT);

  287.     $ambient->assign(0,  ( 0.5, 0.5, 0.5, 1.0 ) );
  288.     $specular->assign(0, ( 0.5, 0.5, 0.5, 1.0 ) );
  289.     $diffuse->assign(0,  ( 1.0, 1.0, 1.0, 1.0 ) );
  290.     $shininess->assign(0,  100.0 );

  291.     $light_diffuse->assign(0, ( 1.0, 1.0, 1.0, 1.0 ) );
  292.     $light_specular->assign(0, ( 0.2, 0.2, 0.2, 1.0 ) );
  293.     $light_position->assign(0, ( 0.0, 1.0, 1.0, 1.0 ) );

  294.     glMaterialfv_c(GL_FRONT_AND_BACK, GL_AMBIENT, $ambient->ptr );
  295.     glMaterialfv_c(GL_FRONT_AND_BACK, GL_SPECULAR, $specular->ptr );
  296.     glMaterialfv_c(GL_FRONT_AND_BACK, GL_DIFFUSE, $diffuse->ptr );
  297.     glMaterialfv_c(GL_FRONT_AND_BACK, GL_SHININESS, $shininess->ptr );

  298.     glLightfv_c(GL_LIGHT0, GL_POSITION, $light_position->ptr);
  299.     glLightfv_c(GL_LIGHT0, GL_DIFFUSE, $light_diffuse->ptr);
  300.     glLightfv_c(GL_LIGHT0, GL_SPECULAR, $light_specular->ptr);

  301.     glEnable(GL_LIGHT0);

  302.     glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
  303.     glEnable(GL_COLOR_MATERIAL);

  304.     # my $light_position2 = OpenGL::Array->new( 4, GL_FLOAT);
  305.     # my $light_specular2 = OpenGL::Array->new( 4, GL_FLOAT);
  306.     # my $light_diffuse2  = OpenGL::Array->new( 4, GL_FLOAT);

  307.     # $light_diffuse2->assign(0, ( 1.0, 1.0, 1.0, 1.0 ) );
  308.     # $light_specular2->assign(0, ( 0.2, 0.2, 0.2, 1.0 ) );
  309.     # $light_position2->assign(0, ( 0.0, 0.0, 1.0, 0.0 ) );

  310.     # glLightfv_c(GL_LIGHT1, GL_POSITION, $light_position2->ptr);
  311.     # glLightfv_c(GL_LIGHT1, GL_DIFFUSE, $light_diffuse2->ptr);
  312.     # glLightfv_c(GL_LIGHT1, GL_SPECULAR, $light_specular2->ptr);
  313.     # glEnable(GL_LIGHT1);

  314.     $tobj = gluNewTess();
  315.     gluTessCallback($tobj, GLU_TESS_BEGIN,     'DEFAULT');
  316.     gluTessCallback($tobj, GLU_TESS_END,       'DEFAULT');
  317.     gluTessCallback($tobj, GLU_TESS_VERTEX,    'DEFAULT');
  318.     gluTessCallback($tobj, GLU_TESS_COMBINE,   'DEFAULT');
  319.     gluTessCallback($tobj, GLU_TESS_ERROR,     'DEFAULT');
  320.     gluTessCallback($tobj, GLU_TESS_EDGE_FLAG, 'DEFAULT');   
  321. }

  322. sub reshape
  323. {
  324.     my ($w, $h) = (shift, shift);
  325.     #Same with screen size
  326.     state $hz_half = $WIDTH/2.0;
  327.     state $vt_half = $HEIGHT/2.0;
  328.     state $fa = 1000.0;

  329.     glViewport(0, 0, $w, $h);
  330.     glMatrixMode(GL_PROJECTION);
  331.     glLoadIdentity();
  332.     #glOrtho(-100.0, $WIDTH-100.0, -100.0, $HEIGHT-100.0, 0.0, $fa*2.0);
  333.     #glFrustum(-100.0, $WIDTH-100.0, -100.0, $HEIGHT-100.0, 800.0, $fa*5.0);
  334.     gluPerspective( 45.0, 1.0, 1.0, $fa*2.0 );
  335.     glMatrixMode(GL_MODELVIEW);
  336.     glLoadIdentity();
  337.     #gluLookAt(0.0,0.0,$fa, 0.0,0.0,0.0, 0.0,1.0, $fa);
  338.     gluLookAt(200.0,200.0,$fa, 200.0,200.0,0.0, 0.0,1.0, $fa);
  339. }

  340. sub hitkey
  341. {
  342.     our $WinID;
  343.     my $k = lc(chr(shift));
  344.     if ( $k eq 'q') { quit() }

  345.     if ( $k eq '-') { $begin-=1 if $begin > 0 }
  346.     if ( $k eq '=') { $begin+=1 if $begin < $#days }

  347.     if ( $k eq '4') { $mx-=10.0 }
  348.     if ( $k eq '6') { $mx+=10.0 }
  349.     if ( $k eq '8') { $my+=10.0 }
  350.     if ( $k eq '2') { $my-=10.0 }
  351.     if ( $k eq '5') { $mz+=10.0 }
  352.     if ( $k eq '0') { $mz-=10.0 }

  353.     if ( $k eq 'w') { $rx+=10.0 }
  354.     if ( $k eq 's') { $rx-=10.0 }
  355.     if ( $k eq 'a') { $ry-=10.0 }
  356.     if ( $k eq 'd') { $ry+=10.0 }
  357.     if ( $k eq 'j') { $rz+=10.0 }
  358.     if ( $k eq 'k') { $rz-=10.0 }
  359.     if ( $k eq '[') { $zoom -= $zoom*0.1 }
  360.     if ( $k eq ']') { $zoom += $zoom*0.1 }
  361. }

  362. sub quit
  363. {
  364.     glutDestroyWindow( $WinID );
  365.     exit 0;
  366. }

  367. sub main
  368. {
  369.     glutInit();
  370.     glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE |GLUT_DEPTH | GLUT_MULTISAMPLE );
  371.     glutInitWindowSize($WIDTH, $HEIGHT);
  372.     glutInitWindowPosition(100, 100);
  373.     our $WinID = glutCreateWindow("Display");
  374.     &init();
  375.     glutDisplayFunc(\&display);
  376.     glutReshapeFunc(\&reshape);
  377.     glutKeyboardFunc(\&hitkey);
  378.     glutIdleFunc(\&idle);
  379.     glutMainLoop();
  380. }

  381. sub time_to_date
  382. {
  383.     my ($sec, $min, $hour, $day, $mon, $year) = localtime( shift );
  384.     $mon += 1;
  385.     $year += 1900;
  386.     return sprintf "%d-%02d-%02d", $year,$mon,$day;
  387. }


  388. DRAW_STRING:
  389. {
  390.     sub draw_character
  391.     {
  392.         our @TEXT;
  393.         my $char = shift;
  394.         my $cts;
  395.         gluTessBeginPolygon($tobj);
  396.         for $cts ( @{$TEXT{$char}->{outline}} )
  397.         {
  398.             gluTessBeginContour($tobj);
  399.             grep { gluTessVertex_p($tobj, @$_, 0.0 ) } @$cts;
  400.             gluTessEndContour($tobj);
  401.         }
  402.         gluTessEndPolygon($tobj);
  403.     }

  404.     sub draw_string
  405.     {
  406.         my $s = shift;
  407.         for my $c ( split //, $s )
  408.         {
  409.             draw_character($c);
  410.             glTranslatef($TEXT{$c}->{right}, 0.0, 0.0);
  411.         }
  412.     }

  413.     sub pointOnLine
  414.     {
  415.         my ($x1, $y1, $x2, $y2, $t) = @_;
  416.         return (
  417.             ($x2-$x1)*$t + $x1,
  418.             ($y2-$y1)*$t + $y1
  419.         );
  420.     }

  421.     sub pointOnQuadBezier
  422.     {
  423.         my ($x1, $y1, $x2, $y2, $x3, $y3, $t) = @_;
  424.         return
  425.             pointOnLine(
  426.                    pointOnLine( $x1, $y1, $x2, $y2, $t ),
  427.                    pointOnLine( $x2, $y2, $x3, $y3, $t ),
  428.                    $t
  429.             );
  430.     }

  431.     sub get_contour
  432.     {
  433.         our $glyph;
  434.         my $char = shift;
  435.         #previous x, y
  436.         my $px, $py, $parts, $step;
  437.         my @contour = ();
  438.         my $ncts    = -1;
  439.         
  440.         $parts = 5;
  441.         $glyph = $face->glyph_from_char($char) || return undef;

  442.         $glyph->outline_decompose(
  443.             move_to  =>
  444.                 sub
  445.                 {
  446.                     ($px, $py) = @_;
  447.                     $ncts++;
  448.                     push @{$contour[$ncts]}, [$px, $py];
  449.                 },
  450.             line_to  =>
  451.                 sub
  452.                 {
  453.                     ($px, $py) = @_;
  454.                     push @{$contour[$ncts]}, [$px, $py];
  455.                 },
  456.             conic_to =>
  457.                 sub
  458.                 {
  459.                     for ($step = 0.0; $step <= $parts; $step+=1.0)
  460.                     {
  461.                         push @{$contour[$ncts]},
  462.                             [ pointOnQuadBezier( $px, $py, @_[2,3,0,1], $step/$parts ) ];
  463.                     }
  464.                     ($px, $py) = @_;
  465.                 },
  466.             cubic_to => sub { warn "cubic\n"; }
  467.         );

  468.         return {
  469.             outline => [@contour],
  470.             right   => $glyph->horizontal_advance(),
  471.         };
  472.     }
  473. }

  474. LIGHT:
  475. {
  476.     sub normalize
  477.     {
  478.         my $v = shift;
  479.         my $d = sqrt($v->[0]*$v->[0] + $v->[1]*$v->[1] + $v->[2]*$v->[2]);
  480.         if ($d == 0.0)
  481.         {
  482.             printf("length zero!\n");
  483.             return;
  484.         }
  485.         $v->[0] /= $d;
  486.         $v->[1] /= $d;
  487.         $v->[2] /= $d;
  488.     }

  489.     sub normcrossprod
  490.     {
  491.         my ( $v1, $v2, $out ) = @_;

  492.         $out->[0] = $v1->[1] * $v2->[2] - $v1->[2] * $v2->[1];
  493.         $out->[1] = $v1->[2] * $v2->[0] - $v1->[0] * $v2->[2];
  494.         $out->[2] = $v1->[0] * $v2->[1] - $v1->[1] * $v2->[0];

  495.         normalize( $out );
  496.     }
  497. }
复制代码


评分

参与人数 1信誉积分 +10 收起 理由
rubyish + 10 赞一个! BTF ~~

查看全部评分

论坛徽章:
89
水瓶座
日期:2014-04-01 08:53:31天蝎座
日期:2014-04-01 08:53:53天秤座
日期:2014-04-01 08:54:02射手座
日期:2014-04-01 08:54:15子鼠
日期:2014-04-01 08:55:35辰龙
日期:2014-04-01 08:56:36未羊
日期:2014-04-01 08:56:27戌狗
日期:2014-04-01 08:56:13亥猪
日期:2014-04-01 08:56:02亥猪
日期:2014-04-08 08:38:58程序设计版块每日发帖之星
日期:2016-01-05 06:20:00程序设计版块每日发帖之星
日期:2016-01-07 06:20:00
发表于 2017-10-24 20:21 |显示全部楼层
太有才了,我一般都用JavaScript来搞类似的东西,而且也没搞过3D这么牛逼的。

论坛徽章:
7
戌狗
日期:2013-12-15 20:43:38技术图书徽章
日期:2014-03-05 01:33:12技术图书徽章
日期:2014-03-15 20:31:17未羊
日期:2014-03-25 23:48:20丑牛
日期:2014-04-07 22:37:44巳蛇
日期:2014-04-11 21:58:0915-16赛季CBA联赛之青岛
日期:2016-03-17 20:36:13
发表于 2017-10-25 00:02 |显示全部楼层
只为花俏,实际使用还是单纯的线条更通透


youyisi ~~

论坛徽章:
42
19周年集字徽章-周
日期:2019-10-14 14:35:31平安夜徽章
日期:2015-12-26 00:06:30数据库技术版块每日发帖之星
日期:2015-12-01 06:20:002015亚冠之首尔
日期:2015-11-04 22:25:43IT运维版块每日发帖之星
日期:2015-08-17 06:20:00寅虎
日期:2014-06-04 16:25:27狮子座
日期:2014-05-12 11:00:00辰龙
日期:2013-12-20 17:07:19射手座
日期:2013-10-24 21:01:23CU十二周年纪念徽章
日期:2013-10-24 15:41:34IT运维版块每日发帖之星
日期:2016-01-27 06:20:0015-16赛季CBA联赛之新疆
日期:2016-06-07 14:10:01
发表于 2017-10-30 09:10 |显示全部楼层
赞一个。

论坛徽章:
12
子鼠
日期:2014-10-11 16:46:482016科比退役纪念章
日期:2018-03-16 10:24:0515-16赛季CBA联赛之山东
日期:2017-11-10 14:32:142016科比退役纪念章
日期:2017-09-02 15:42:4715-16赛季CBA联赛之佛山
日期:2017-08-28 17:11:5515-16赛季CBA联赛之浙江
日期:2017-08-24 16:55:1715-16赛季CBA联赛之青岛
日期:2017-08-17 19:55:2415-16赛季CBA联赛之天津
日期:2017-06-29 10:34:4315-16赛季CBA联赛之四川
日期:2017-05-16 16:38:55黑曼巴
日期:2016-07-19 15:03:112015亚冠之萨济拖拉机
日期:2015-05-22 11:38:5315-16赛季CBA联赛之北京
日期:2019-08-13 17:30:53
发表于 2017-10-30 16:30 |显示全部楼层
Github: https://github.com/vicyang/Exchange-Rates

批量下载汇率数据的脚本位于 Data 目录
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP