Coder Social home page Coder Social logo

quchen / generative-art Goto Github PK

View Code? Open in Web Editor NEW
147.0 147.0 7.0 9.76 MB

I wanted to make a nicer sticker for Munihac, then things got out of hand.

License: BSD 3-Clause "New" or "Revised" License

Haskell 99.65% Nix 0.29% Makefile 0.06%
art generative-art haskell

generative-art's People

Contributors

fmthoma avatar quchen avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

generative-art's Issues

bezierSmoothen: outdated docs

The sentence

Outdated, wrong: "Simply using bezierSmoothen will yield a sharp bend at the line’s origin"

is wrong and outdated. Remove it.

Remove all auto-generated output

The reason I checked in so many pictures is that they are readily available online. However, thanks to Github Actions, they are now built and deployed to Github Pages automatically 🎉 (link)

We should thus think about removing all the auto-generted pictures from the repo and have CI/local builds handle everything.

  • Upsides
    • Saves space in the repo
    • No more »only tiny images« limit
  • Downsides
    • When pictures change unexpectedly, Git does not show a diff. (Workaround: keep a file with hashes of all generated files?)

Plotting changes

  • Make Plot opaque: don’t expose MonadWriter/MonadState externally
  • Allow local feedrates
  • Make previewing the BB an easy to access command rather than a setting?
  • Make mirroring the Y axis easily available?

Hide Delaunay internals

These just clutter up Haddock. If we need the internals we should modify the lib instead and expose what we need explicitly, or do a fork on a branch for some dirty hacking.

SVG path parser: chaining

Much like multiple coordinates after M in #11, the other SVG commands allow multiple arguments, such as L 1 2 3 4 or C 1 2 3 4 5 6 1 2 3 4 5 6 to define multiple segments of a curve without repeating the instruction.

Remove overlapping instances

Being polymorphic over Sequential yields ambiguities etc. The price of this is a bit high compared to just writing e.g. Plotting Vector and Plotting [a] by hand.

Implement smarter circle plotting again

The naive way of painting a circle is by always starting them e.g. on the very left. This requires some unnecessary pen hovering, and for some pens creates a visible »pen down« dot.

Instead, start the circle with minimal pen hovering, depending on the current pen position.

Standardize all functions and claims that change with coordinate handedness

tl;dr

Standardize all functions and documentation to standard math coordinates

Long description

Cairo has zero at the top-left, and the documentation often reflects that. For the most part, I simply winged it, and fiddled with the signs long enough so the picture is pretty. I never checked whether clockwise is actually clockwise for example, and even worse: now that we have both a Cairo and a penplotting backend, claims that change with coordinate handedness are wrong for one of the backends.

I think it’s therefore a good idea to check the functions that rotate things for whether their documentation is right, and fixing any sign errors (and un-fixing those sign errors so output of previous pictures does not change).

Shrink polygon by constant amount

Using scale, we can shrink polygons by a factor. For getting nice patterns out of e.g. a Delaunay triangulation, it would be cool to shrink all edges of the triangle by a number of pixels, instead of a factor of its distance from some point.

Doctest: consider `...` matching

Doctest output can be made nicer by using Doctest’s ... feature, so that instead of

>>> S.fromList (reverse [1..10])
S.fromList [1,2,3,4,5,6,7,8,9,10]

we can omit some noise and write

>>> S.fromList (reverse [1..10])
... [1,2,3,4,5,6,7,8,9,10]

Bézier subdivision: more robustness

Missing pieces

The Caseljeau article handles more corner cases than the current implementation in Geometry.Bezier, e.g. non-differentiable bends.

The missing parts are:

  • Estimation of Tangent Error
  • Processing of the Cusps (nondifferentiable case)
  • The Devil is in the Details
  • Collinear Case

References

  • Fork/dump of the antigrain website: link
  • PDF version of the subdivision of bezier curves post: link
  • Internet archive version of ^: link

Backup code

C++ code from Antigrain (source), in case the archive page goes offline:

Click to expand
//----------------------------------------------------------------------------
// Anti-Grain Geometry (AGG) - Version 2.5
// A high quality rendering engine for C++
// Copyright (C) 2002-2006 Maxim Shemanarev
// Contact: [email protected]
//          [email protected]
//          http://antigrain.com
// 
// AGG is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
// 
// AGG is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with AGG; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 
// MA 02110-1301, USA.
//----------------------------------------------------------------------------

#include <math.h>
#include "agg_curves.h"
#include "agg_math.h"

namespace agg
{

    //------------------------------------------------------------------------
    const double curve_distance_epsilon                  = 1e-30;
    const double curve_collinearity_epsilon              = 1e-30;
    const double curve_angle_tolerance_epsilon           = 0.01;
    enum curve_recursion_limit_e { curve_recursion_limit = 32 };



    //------------------------------------------------------------------------
    void curve3_inc::approximation_scale(double s) 
    { 
        m_scale = s;
    }

    //------------------------------------------------------------------------
    double curve3_inc::approximation_scale() const 
    { 
        return m_scale;
    }

    //------------------------------------------------------------------------
    void curve3_inc::init(double x1, double y1, 
                          double x2, double y2, 
                          double x3, double y3)
    {
        m_start_x = x1;
        m_start_y = y1;
        m_end_x   = x3;
        m_end_y   = y3;

        double dx1 = x2 - x1;
        double dy1 = y2 - y1;
        double dx2 = x3 - x2;
        double dy2 = y3 - y2;

        double len = sqrt(dx1 * dx1 + dy1 * dy1) + sqrt(dx2 * dx2 + dy2 * dy2); 

        m_num_steps = uround(len * 0.25 * m_scale);

        if(m_num_steps < 4)
        {
            m_num_steps = 4;   
        }

        double subdivide_step  = 1.0 / m_num_steps;
        double subdivide_step2 = subdivide_step * subdivide_step;

        double tmpx = (x1 - x2 * 2.0 + x3) * subdivide_step2;
        double tmpy = (y1 - y2 * 2.0 + y3) * subdivide_step2;

        m_saved_fx = m_fx = x1;
        m_saved_fy = m_fy = y1;
        
        m_saved_dfx = m_dfx = tmpx + (x2 - x1) * (2.0 * subdivide_step);
        m_saved_dfy = m_dfy = tmpy + (y2 - y1) * (2.0 * subdivide_step);

        m_ddfx = tmpx * 2.0;
        m_ddfy = tmpy * 2.0;

        m_step = m_num_steps;
    }

    //------------------------------------------------------------------------
    void curve3_inc::rewind(unsigned)
    {
        if(m_num_steps == 0)
        {
            m_step = -1;
            return;
        }
        m_step = m_num_steps;
        m_fx   = m_saved_fx;
        m_fy   = m_saved_fy;
        m_dfx  = m_saved_dfx;
        m_dfy  = m_saved_dfy;
    }

    //------------------------------------------------------------------------
    unsigned curve3_inc::vertex(double* x, double* y)
    {
        if(m_step < 0) return path_cmd_stop;
        if(m_step == m_num_steps)
        {
            *x = m_start_x;
            *y = m_start_y;
            --m_step;
            return path_cmd_move_to;
        }
        if(m_step == 0)
        {
            *x = m_end_x;
            *y = m_end_y;
            --m_step;
            return path_cmd_line_to;
        }
        m_fx  += m_dfx; 
        m_fy  += m_dfy;
        m_dfx += m_ddfx; 
        m_dfy += m_ddfy; 
        *x = m_fx;
        *y = m_fy;
        --m_step;
        return path_cmd_line_to;
    }

    //------------------------------------------------------------------------
    void curve3_div::init(double x1, double y1, 
                          double x2, double y2, 
                          double x3, double y3)
    {
        m_points.remove_all();
        m_distance_tolerance_square = 0.5 / m_approximation_scale;
        m_distance_tolerance_square *= m_distance_tolerance_square;
        bezier(x1, y1, x2, y2, x3, y3);
        m_count = 0;
    }

    //------------------------------------------------------------------------
    void curve3_div::recursive_bezier(double x1, double y1, 
                                      double x2, double y2, 
                                      double x3, double y3,
                                      unsigned level)
    {
        if(level > curve_recursion_limit) 
        {
            return;
        }

        // Calculate all the mid-points of the line segments
        //----------------------
        double x12   = (x1 + x2) / 2;                
        double y12   = (y1 + y2) / 2;
        double x23   = (x2 + x3) / 2;
        double y23   = (y2 + y3) / 2;
        double x123  = (x12 + x23) / 2;
        double y123  = (y12 + y23) / 2;

        double dx = x3-x1;
        double dy = y3-y1;
        double d = fabs(((x2 - x3) * dy - (y2 - y3) * dx));
        double da;

        if(d > curve_collinearity_epsilon)
        { 
            // Regular case
            //-----------------
            if(d * d <= m_distance_tolerance_square * (dx*dx + dy*dy))
            {
                // If the curvature doesn't exceed the distance_tolerance value
                // we tend to finish subdivisions.
                //----------------------
                if(m_angle_tolerance < curve_angle_tolerance_epsilon)
                {
                    m_points.add(point_d(x123, y123));
                    return;
                }

                // Angle & Cusp Condition
                //----------------------
                da = fabs(atan2(y3 - y2, x3 - x2) - atan2(y2 - y1, x2 - x1));
                if(da >= pi) da = 2*pi - da;

                if(da < m_angle_tolerance)
                {
                    // Finally we can stop the recursion
                    //----------------------
                    m_points.add(point_d(x123, y123));
                    return;                 
                }
            }
        }
        else
        {
            // Collinear case
            //------------------
            da = dx*dx + dy*dy;
            if(da == 0)
            {
                d = calc_sq_distance(x1, y1, x2, y2);
            }
            else
            {
                d = ((x2 - x1)*dx + (y2 - y1)*dy) / da;
                if(d > 0 && d < 1)
                {
                    // Simple collinear case, 1---2---3
                    // We can leave just two endpoints
                    return;
                }
                     if(d <= 0) d = calc_sq_distance(x2, y2, x1, y1);
                else if(d >= 1) d = calc_sq_distance(x2, y2, x3, y3);
                else            d = calc_sq_distance(x2, y2, x1 + d*dx, y1 + d*dy);
            }
            if(d < m_distance_tolerance_square)
            {
                m_points.add(point_d(x2, y2));
                return;
            }
        }

        // Continue subdivision
        //----------------------
        recursive_bezier(x1, y1, x12, y12, x123, y123, level + 1); 
        recursive_bezier(x123, y123, x23, y23, x3, y3, level + 1); 
    }

    //------------------------------------------------------------------------
    void curve3_div::bezier(double x1, double y1, 
                            double x2, double y2, 
                            double x3, double y3)
    {
        m_points.add(point_d(x1, y1));
        recursive_bezier(x1, y1, x2, y2, x3, y3, 0);
        m_points.add(point_d(x3, y3));
    }





    //------------------------------------------------------------------------
    void curve4_inc::approximation_scale(double s) 
    { 
        m_scale = s;
    }

    //------------------------------------------------------------------------
    double curve4_inc::approximation_scale() const 
    { 
        return m_scale;
    }

    //------------------------------------------------------------------------
    static double MSC60_fix_ICE(double v) { return v; }

    //------------------------------------------------------------------------
    void curve4_inc::init(double x1, double y1, 
                          double x2, double y2, 
                          double x3, double y3,
                          double x4, double y4)
    {
        m_start_x = x1;
        m_start_y = y1;
        m_end_x   = x4;
        m_end_y   = y4;

        double dx1 = x2 - x1;
        double dy1 = y2 - y1;
        double dx2 = x3 - x2;
        double dy2 = y3 - y2;
        double dx3 = x4 - x3;
        double dy3 = y4 - y3;

        double len = (sqrt(dx1 * dx1 + dy1 * dy1) + 
                      sqrt(dx2 * dx2 + dy2 * dy2) + 
                      sqrt(dx3 * dx3 + dy3 * dy3)) * 0.25 * m_scale;

#if defined(_MSC_VER) && _MSC_VER <= 1200
        m_num_steps = uround(MSC60_fix_ICE(len));
#else
        m_num_steps = uround(len);
#endif

        if(m_num_steps < 4)
        {
            m_num_steps = 4;   
        }

        double subdivide_step  = 1.0 / m_num_steps;
        double subdivide_step2 = subdivide_step * subdivide_step;
        double subdivide_step3 = subdivide_step * subdivide_step * subdivide_step;

        double pre1 = 3.0 * subdivide_step;
        double pre2 = 3.0 * subdivide_step2;
        double pre4 = 6.0 * subdivide_step2;
        double pre5 = 6.0 * subdivide_step3;
	
        double tmp1x = x1 - x2 * 2.0 + x3;
        double tmp1y = y1 - y2 * 2.0 + y3;

        double tmp2x = (x2 - x3) * 3.0 - x1 + x4;
        double tmp2y = (y2 - y3) * 3.0 - y1 + y4;

        m_saved_fx = m_fx = x1;
        m_saved_fy = m_fy = y1;

        m_saved_dfx = m_dfx = (x2 - x1) * pre1 + tmp1x * pre2 + tmp2x * subdivide_step3;
        m_saved_dfy = m_dfy = (y2 - y1) * pre1 + tmp1y * pre2 + tmp2y * subdivide_step3;

        m_saved_ddfx = m_ddfx = tmp1x * pre4 + tmp2x * pre5;
        m_saved_ddfy = m_ddfy = tmp1y * pre4 + tmp2y * pre5;

        m_dddfx = tmp2x * pre5;
        m_dddfy = tmp2y * pre5;

        m_step = m_num_steps;
    }

    //------------------------------------------------------------------------
    void curve4_inc::rewind(unsigned)
    {
        if(m_num_steps == 0)
        {
            m_step = -1;
            return;
        }
        m_step = m_num_steps;
        m_fx   = m_saved_fx;
        m_fy   = m_saved_fy;
        m_dfx  = m_saved_dfx;
        m_dfy  = m_saved_dfy;
        m_ddfx = m_saved_ddfx;
        m_ddfy = m_saved_ddfy;
    }

    //------------------------------------------------------------------------
    unsigned curve4_inc::vertex(double* x, double* y)
    {
        if(m_step < 0) return path_cmd_stop;
        if(m_step == m_num_steps)
        {
            *x = m_start_x;
            *y = m_start_y;
            --m_step;
            return path_cmd_move_to;
        }

        if(m_step == 0)
        {
            *x = m_end_x;
            *y = m_end_y;
            --m_step;
            return path_cmd_line_to;
        }

        m_fx   += m_dfx;
        m_fy   += m_dfy;
        m_dfx  += m_ddfx; 
        m_dfy  += m_ddfy; 
        m_ddfx += m_dddfx; 
        m_ddfy += m_dddfy; 

        *x = m_fx;
        *y = m_fy;
        --m_step;
        return path_cmd_line_to;
    }




    //------------------------------------------------------------------------
    void curve4_div::init(double x1, double y1, 
                          double x2, double y2, 
                          double x3, double y3,
                          double x4, double y4)
    {
        m_points.remove_all();
        m_distance_tolerance_square = 0.5 / m_approximation_scale;
        m_distance_tolerance_square *= m_distance_tolerance_square;
        bezier(x1, y1, x2, y2, x3, y3, x4, y4);
        m_count = 0;
    }

    //------------------------------------------------------------------------
    void curve4_div::recursive_bezier(double x1, double y1, 
                                      double x2, double y2, 
                                      double x3, double y3, 
                                      double x4, double y4,
                                      unsigned level)
    {
        if(level > curve_recursion_limit) 
        {
            return;
        }

        // Calculate all the mid-points of the line segments
        //----------------------
        double x12   = (x1 + x2) / 2;
        double y12   = (y1 + y2) / 2;
        double x23   = (x2 + x3) / 2;
        double y23   = (y2 + y3) / 2;
        double x34   = (x3 + x4) / 2;
        double y34   = (y3 + y4) / 2;
        double x123  = (x12 + x23) / 2;
        double y123  = (y12 + y23) / 2;
        double x234  = (x23 + x34) / 2;
        double y234  = (y23 + y34) / 2;
        double x1234 = (x123 + x234) / 2;
        double y1234 = (y123 + y234) / 2;


        // Try to approximate the full cubic curve by a single straight line
        //------------------
        double dx = x4-x1;
        double dy = y4-y1;

        double d2 = fabs(((x2 - x4) * dy - (y2 - y4) * dx));
        double d3 = fabs(((x3 - x4) * dy - (y3 - y4) * dx));
        double da1, da2, k;

        switch((int(d2 > curve_collinearity_epsilon) << 1) +
                int(d3 > curve_collinearity_epsilon))
        {
        case 0:
            // All collinear OR p1==p4
            //----------------------
            k = dx*dx + dy*dy;
            if(k == 0)
            {
                d2 = calc_sq_distance(x1, y1, x2, y2);
                d3 = calc_sq_distance(x4, y4, x3, y3);
            }
            else
            {
                k   = 1 / k;
                da1 = x2 - x1;
                da2 = y2 - y1;
                d2  = k * (da1*dx + da2*dy);
                da1 = x3 - x1;
                da2 = y3 - y1;
                d3  = k * (da1*dx + da2*dy);
                if(d2 > 0 && d2 < 1 && d3 > 0 && d3 < 1)
                {
                    // Simple collinear case, 1---2---3---4
                    // We can leave just two endpoints
                    return;
                }
                     if(d2 <= 0) d2 = calc_sq_distance(x2, y2, x1, y1);
                else if(d2 >= 1) d2 = calc_sq_distance(x2, y2, x4, y4);
                else             d2 = calc_sq_distance(x2, y2, x1 + d2*dx, y1 + d2*dy);

                     if(d3 <= 0) d3 = calc_sq_distance(x3, y3, x1, y1);
                else if(d3 >= 1) d3 = calc_sq_distance(x3, y3, x4, y4);
                else             d3 = calc_sq_distance(x3, y3, x1 + d3*dx, y1 + d3*dy);
            }
            if(d2 > d3)
            {
                if(d2 < m_distance_tolerance_square)
                {
                    m_points.add(point_d(x2, y2));
                    return;
                }
            }
            else
            {
                if(d3 < m_distance_tolerance_square)
                {
                    m_points.add(point_d(x3, y3));
                    return;
                }
            }
            break;

        case 1:
            // p1,p2,p4 are collinear, p3 is significant
            //----------------------
            if(d3 * d3 <= m_distance_tolerance_square * (dx*dx + dy*dy))
            {
                if(m_angle_tolerance < curve_angle_tolerance_epsilon)
                {
                    m_points.add(point_d(x23, y23));
                    return;
                }

                // Angle Condition
                //----------------------
                da1 = fabs(atan2(y4 - y3, x4 - x3) - atan2(y3 - y2, x3 - x2));
                if(da1 >= pi) da1 = 2*pi - da1;

                if(da1 < m_angle_tolerance)
                {
                    m_points.add(point_d(x2, y2));
                    m_points.add(point_d(x3, y3));
                    return;
                }

                if(m_cusp_limit != 0.0)
                {
                    if(da1 > m_cusp_limit)
                    {
                        m_points.add(point_d(x3, y3));
                        return;
                    }
                }
            }
            break;

        case 2:
            // p1,p3,p4 are collinear, p2 is significant
            //----------------------
            if(d2 * d2 <= m_distance_tolerance_square * (dx*dx + dy*dy))
            {
                if(m_angle_tolerance < curve_angle_tolerance_epsilon)
                {
                    m_points.add(point_d(x23, y23));
                    return;
                }

                // Angle Condition
                //----------------------
                da1 = fabs(atan2(y3 - y2, x3 - x2) - atan2(y2 - y1, x2 - x1));
                if(da1 >= pi) da1 = 2*pi - da1;

                if(da1 < m_angle_tolerance)
                {
                    m_points.add(point_d(x2, y2));
                    m_points.add(point_d(x3, y3));
                    return;
                }

                if(m_cusp_limit != 0.0)
                {
                    if(da1 > m_cusp_limit)
                    {
                        m_points.add(point_d(x2, y2));
                        return;
                    }
                }
            }
            break;

        case 3: 
            // Regular case
            //-----------------
            if((d2 + d3)*(d2 + d3) <= m_distance_tolerance_square * (dx*dx + dy*dy))
            {
                // If the curvature doesn't exceed the distance_tolerance value
                // we tend to finish subdivisions.
                //----------------------
                if(m_angle_tolerance < curve_angle_tolerance_epsilon)
                {
                    m_points.add(point_d(x23, y23));
                    return;
                }

                // Angle & Cusp Condition
                //----------------------
                k   = atan2(y3 - y2, x3 - x2);
                da1 = fabs(k - atan2(y2 - y1, x2 - x1));
                da2 = fabs(atan2(y4 - y3, x4 - x3) - k);
                if(da1 >= pi) da1 = 2*pi - da1;
                if(da2 >= pi) da2 = 2*pi - da2;

                if(da1 + da2 < m_angle_tolerance)
                {
                    // Finally we can stop the recursion
                    //----------------------
                    m_points.add(point_d(x23, y23));
                    return;
                }

                if(m_cusp_limit != 0.0)
                {
                    if(da1 > m_cusp_limit)
                    {
                        m_points.add(point_d(x2, y2));
                        return;
                    }

                    if(da2 > m_cusp_limit)
                    {
                        m_points.add(point_d(x3, y3));
                        return;
                    }
                }
            }
            break;
        }

        // Continue subdivision
        //----------------------
        recursive_bezier(x1, y1, x12, y12, x123, y123, x1234, y1234, level + 1); 
        recursive_bezier(x1234, y1234, x234, y234, x34, y34, x4, y4, level + 1); 
    }

    //------------------------------------------------------------------------
    void curve4_div::bezier(double x1, double y1, 
                            double x2, double y2, 
                            double x3, double y3, 
                            double x4, double y4)
    {
        m_points.add(point_d(x1, y1));
        recursive_bezier(x1, y1, x2, y2, x3, y3, x4, y4, 0);
        m_points.add(point_d(x4, y4));
    }

}

QC failure for pseudoAngle

In 40861e0:

      *** Failed! Falsified (after 2 tests):
      NonZeroVec2 (Vec2 (-1.0) 0.0)
      NonZeroVec2 (Vec2 0.653308615076723 0.9706462362409549)
      LT /= GT
      Use --quickcheck-replay=167254 to reproduce.
      Use -p '/pseudoAngle/' to rerun this test only.```

Haddock CI: include dependencies

Naively removing --no-haddock-deps rebuilds all packages (~7500 lines of log output for the Haddock step). Caching is crucial here.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.