Joy is a tiny creative coding library in Python.

Related tags

IDE joy
Overview

Joy

Joy is a tiny creative coding library in Python.

Installation

The easiest way to install it is download joy.py and place it in your directory. The library has no dependencies.

It can be downloaded from:

https://github.com/fossunited/joy/raw/main/joy.py

Coordinate System

Joy uses a canvas with (0, 0) as the center of the canvas.

By default, the size of the canvas is (300, 300).

Using Joy

The Joy library integrates well with Jupyter environment and it is recommended to explore Joy in a Jupyter lab.

The first thing you need to do is import the module.

from joy import *

Once the functionality in the module is imported, you can start playing with it.

Basic Shapes

Joy supports the basic shapes circle, ellipse, rectangle and line.

Let's start with a drawing a circle:

c = circle()
show(c)

svg

By default circle will have center at (0, 0) and radius as 100. But you can specify different values.

c = circle(x=50, y=50, r=50)
show(c)

svg

The other basic types that are supported are ellipse, rectangle, and line:

s1 = circle()
s2 = ellipse()
s3 = rectangle()
s4 = line()
show(s1, s2, s3, s4)

svg

Combining Shapes

Joy supports + operator to join shapes.

def donut(x, y, r):
    c1 = circle(x=x, y=y, r=r)
    c2 = circle(x=x, y=y, r=r/2)
    return c1+c2

d = donut(0, 0, 100)
show(d)

svg

Transformations

Joy supports translate, rotate and scale transformations. Transformations are applied using | operator.

shape = circle(r=50) | translate(x=100, y=0)
show(shape)

svg

Transformations can be chained too.

r1 = rectangle(w=200, h=200)
r2 = r1 | rotate(angle=45) | scale(1/SQRT2)
show(r1, r2)

svg

Higer-Order Transformations

Joy supports higher-order transformation repeat.

The repeat transformation applies a transformation multiple times and combines all the resulting shapes.

For example, draw 10 circles:

c = circle(x=-100, y=0, r=50)
shape = c | Repeat(10, Translate(x=10, y=0)
show(shape)

svg

Combined with rotation, it can create amusing patterns.

shape = line() | repeat(18, rotate(angle=10))
show(shape)

svg

We could do the same with a square:

shape = rectangle(w=200, h=200) | repeat(18, rotate(angle=10))
show(shape)

svg

or a rectangle:

shape = rectangle(w=200, h=100) | repeat(18, rotate(angle=10))
show(shape)

svg

We can combine multiple transformations and repeat.

shape = rectangle(w=300, h=300) | repeat(72, rotate(360/72) | scale(0.92))
show(shape)

svg

You can try the same with a circle too:

c = circle(x=100, y=0, radius=50)
shape = c | repeat(36*4, rotate(10) | scale(0.97))
show(shape)

svg

For more information, please checkout the tutorial.

Tutorial

See tutorial.ipynb.

Acknowledgements

Special thanks to Amit Kapoor (@amitkaps). This library woundn't have been possible without his inputs.

The long discussions between @anandology and @amitkaps on functional programming and computational artistry (for almost over an year) and the initial experiments were some of the seeds that gave life to this library.

License

This repository has been released under the MIT License.

Comments
  • repeat - new higher-order transformation

    repeat - new higher-order transformation

    I'm proposing a new higher-order transformation repeat, what would apply a transformation repeatedly and combine all the resulting shapes.

    For example:

    repeat(rect(), n=4, transform=lambda s: scale(rotate(s, angle=45), sx=1/SQRT2))
    

    would produce:

    Screenshot 2021-06-07 at 10 04 52 AM

    New Transform API

    To make it easier to use the transforms, I propose a new API for transforms.

    >>> t = rotate(45) | scale(1/SQRT2)
    repeat(rect(), n=4, transform=t)
    

    All the transform functions work in two ways.

    When a shape argument is suplied, it returns a new shape with the transformation applied. For example rotate(rect(), 45) would rotate the shape by 45 degrees.

    When shape argument is not supplied, it returns a transformation object. rotate(45) return the transformation object that rotates given shape by 45 degres.

    t = rotate(45)
    t.apply(rect()) # apply the transformation
    t(rect()) # another way to apply the transformation
    

    A new transformation can be created by combining multiple transformations and the | is used for combining them.

    >>> t = rotate(45) | scale(1/SQRT2)
    >>> repeat(rect(), n=4, transform=t)
    

    Even repeat can be called without a shape and that creates a new transformation.

    With this we can do create many transformations with ease.

    Some examples:

    Draw four circles in a row:

    repeat(circle(r=50), n=4, transform=translate(x=100))
    

    Draw a 4x4 grid of circles.

    grid = repeat(n=4, transform=translate(y=100)) | repeat(n=4, transform=translate(x=100))
    grid(circle(r=50))
    

    In fact, the cycle function will become a special case of repeat.

    opened by anandology 12
  • Proposal: Make point first class citizens of Joy

    Proposal: Make point first class citizens of Joy

    Right now we specify just the x and y coordinates to the functions like x1, y1 or cx, cy etc. How about passing points instead of individual coordinates?

    The new API would become:

    circle(center=(100, 100), radius=50)
    line(start=(10, 10), end=(50, 50))
    rect(topleft=(50, 50), width=50, height=100)
    rect(center=(0, 0), width=50, height=100)
    

    As mentioned in #1, these shapes will have points available as attributes.

    >>> c = circle(center=(100, 100), radius=50)
    >>> print(c.center, c.right)
    

    And we could use the same for transformations.

    z = line(start=(0, 0), end=(100, 0))
    rotate(z, angle=45, anchor=z.end)
    
    opened by anandology 9
  • Bug in rendering repeat

    Bug in rendering repeat

    The following code is producing wrong output.

    a = ellipse() 
    b = a | repeat(2, rotate(90))
    a1 = a | scale(x=0.5) | translate(x=75)
    b1 = b | scale(x=0.5) | translate(x=-75)
    show(a1+b1)
    

    This generates the following output, which is incorrect.

    bug-1

    If you use another ellipse() to create b instead of reusing a, it gives the correct output.

    a = ellipse() 
    b = ellipse() | repeat(2, rotate(90))
    a1 = a | scale(x=0.5) | translate(x=75)
    b1 = b | scale(x=0.5) | translate(x=-75)
    show(a1+b1)
    

    Correct output:

    bug-2

    Notes

    Looks like there is some issue with the way references are handled.

    opened by anandology 4
  • Add type hints to the project

    Add type hints to the project

    This adds static type hints to the project. These hints don't affect anything at runtime, but they allow type checkers, analysis tools and IDEs to provide type checks while writing code, catching bugs early.

    See PEP 484 for more information.

    Rationale

    There's a few really nice benefits to having these type hints:

    Type inference and autocompletion:

    • Without types:

      image image

    • With types:

      image image

    Better bug tracking:

    Adding types led me to find a few bugs and edge cases in the codebase. Such as:

    • render_tag and **kwargs:

      Currently,

      render_tag('svg', **{'close': True})
      

      behaves the same way as

      render_tag('svg', close=True)
      

      Ideally close should never be accessed from the kwargs, as kwargs are supposed to purely be svg attributes.

      To fix this, we can change the type signature of render_svg to make close a positional-only argument:

      def render_tag(tag: str, close: bool = False, /, **attrs: Attr) -> str:
      #                                             ^ this is the addition
      

      However this change will require the user to be using Python 3.8 or above. Do let me know what you think about that.

    • A similar issue existed in Shape.__init__, where the children and transform properties could be inserted through kwargs.

    Scope for improvement

    This patch has a few areas that can be improved, specifically regarding generics.

    Right now, circle().clone() is correctly identified as Circle type, but doing transformations such as circle() | scale(2), it loses its specificity and becomes Shape.

    A bit more type information can be added to the subclasses of Transformation, which can tell what the new type of the shape will be after transformation. I'd be happy to add that in as well.


    I mainly did this patch for myself, to help better understand the project. If this seems useful, you could incorporate this into the main project as well. Any thoughts / suggestions would be appreciated :)

    opened by tusharsadhwani 3
  • Joy is not very beginner friendly

    Joy is not very beginner friendly

    I've been trying to teach the joy of programming course to some students. While they appreciate that the new library is more expressive, most of them feel that the new API is hard for beginners than it's previous version.

    The Issues

    Case Variations

    Beginners easily make mistakes with cases. Having to type some in TitleCase is confusing. Also, couple of students are leraning this on mobile phone and that is even more difficult.

    Also, the primitive shapes are in TitleCase and the user defined shapes will usually be in lower case. That is inconsistent.

    Using Point

    Forcing to use Point from the beginning is confusing for beginners. It adds an additional layer of nesting in calling these functions.

    c = Circle(center=Point(x=100, y=100), radius=50)
    
    c = Circle(x=100, y=100, radius=50)
    

    Also, using Point makes it hard for them to write functions that require drawing shapes with different center.

    def three_circles(center, radius):
        c1 = Circle(center=center, radius=radius)
        c2 = Circle(center=Point(x=center.x-2*radius, y=center.y), radius=radius)
        c3 = Circle(center=Point(x=center.x+2*radius, y=center.y), radius=radius)
        return c1 + c2 + c3
    
    def three_circles(x, y, r):
        c1 = Circle(x=x, y=y, radius=r)
        c2 = Circle(x=x-2*r, y=y, radius=r)
        c3 = Circle(x=x+2*r, y=y, radius=r)
        return c1 + c2 + c3
    
    # or even
    
    def three_circles(x, y, r):
         x1 = x
         x2 = x - 2 * r
         x3 = x + 2* r
        c1 = Circle(x=x1, y=y, radius=r)
        c2 = Circle(x=x2, y=y, radius=r)
        c3 = Circle(x=x3, y=y, radius=r)
        return c1 + c2 + c3
    

    I agree that the current version works very well with transformations, but they are introduced much later in the course.

    Long Argument Names

    The long arguments names are inconvenient. They are easy to understand in the beginning, but repeated use is painful.

    s1 = Circle(x=10, y=20, radius=30)
    s2 = Rectangle(x=10, y=20, width=30, height=40)
    ---
    s1 = Circle(x=10, y=20, r=30)
    s2 = Rectangle(x=10, y=20, w=30, h=40)
    

    Proposed API

    • all functions are lowercase
    • short argument names
    • x/y instead of point
    • point will still be there and used for polygon etc and used in the internal representations
    c = circle()
    c = circle(r=100)
    c = circle(x=10, y=20, r=100)
    c = circle(10, 20, 100)
    
    r = rectangle()
    r = rectangle(w=100, h=200)
    r = rectangle(x=20, y=30, w=100, h=200)
    r = rectangle(20, 30, 100, 200)
    
    z = line()
    z = line(x1=10, y1=20, x2=100, y2=120)
    
    s = polygon([
        point(0, 0),
        point(100, 0),
        point(0, 100)
    ])
    
    line() | repeat(4, rotate(10))
    rectangle() | repeat(36, rotate(10) | scale(0.9))
    
    circle() | translate(x=10, y=20)
    circle() | translate(10, 20)
    circle() | translate(x=10)
    circle() | translate(y=20)
    
    circle() | scale(1.5)   # scale both x and y
    circle() | scale(x=1.5) # scale only x
    circle() | Scale(y=1.2) # scale only y
    

    @amitkaps what do you think?

    opened by anandology 3
  • Add Square shape

    Add Square shape

    Add Square shape.

    Just like we have Ellipse and Circle. We could have Rectangle and Square.

    The API

    s = Square()
    s = Square(size=200)
    s = Square(center=Point(x=100, y=100), size=200)
    

    Attributes

    The square object will have the following attributes:

    • center
    • size
    • width
    • height

    The value of height and width attributes would be same as the size.

    opened by anandology 3
  • Made 3 new functions which allow for more complicated shapes and more

    Made 3 new functions which allow for more complicated shapes and more

    Reformated the file according to the Syntax that is better accepted. added a function to draw more complicated shapes. Path() added a function to create conic sections. CSection() added a function to create semi-circles. SemiCircle()

    Also added suitable examples and explained how each of them works within the classes for the respective functions.

    opened by mohamedarish 2
  • API for Scale and Transform

    API for Scale and Transform

    With the current Scale API, it is cumbersome to specify uniform scaling.

    Scale(sx=1.5, sy=1.5)
    Scale(sx=1.5) # sy will be same value as sx (if we follow svg style)
    

    I don't think that is a good idea. Here is alternative suggesion. Add ScaleX, ScaleY in addition to Scale and make all of them take just one argument.

    Scale(1.5) # uniformly scale both x and y by 1.5
    ScaleX(1.5) # scale x by 1.5 and y will be unchanged
    ScaleY(1.5) # scale y by 1.5 and x will be unchanged
    
    ScaleX(2) | ScaleY(1.5) # scale x and y independently
    

    When it comes to Transform, we hardly ever do uniform translation on both the axes.

    Transform(x=100, y=50) # Transform both x and y 
    Transform(x=100)  # implies y=0
    Transform(y=50)  # implies x=0
    

    The other option is to have:

    Transform(100, 50)
    TransformX(100)
    TransformY(100)
    

    I'm not really happy with either of the options for Transform.

    opened by anandology 1
  • The transformations are being applied in the wrong order

    The transformations are being applied in the wrong order

    This is in the new-api branch.

    Example to demonstrate the issue

    Screenshot 2021-06-10 at 9 28 01 AM

    Both of the following lines of code should produce the same result as circle is invarient to rotation.

    Circle()  |  Scale(sx=0.5, sy=1)
    Circle() | Rotate(90) |  Scale(sx=0.5, sy=1)
    
    opened by anandology 1
  • Add Polygon shape

    Add Polygon shape

    The API

    shape = Polygon(points = [ Point(0, -10), Point(10, 10), Point(20, -10), Point(30, 10) ])

    Reference

    https://developer.mozilla.org/en-US/docs/Web/SVG/Element/polygon

    opened by anandology 1
  • Add Ellipse Shape

    Add Ellipse Shape

    The API

    I think it is better to specify width and height of the ellipse, just like the rectangle. Using rx an ry as in SVG doesn't look that elegant.

    e = Ellipse()
    e = Ellipse(width=100, height=200)
    e = Ellipse(center=Point(10, 20), width=100, height=200)
    

    Going by the same argument, we should be using the diameter for the circle, not radius. @amitkaps what do you think?

    Reference

    https://developer.mozilla.org/en-US/docs/Web/SVG/Element/ellipse

    opened by anandology 1
  • Consider making all shape arguments keyword-only?

    Consider making all shape arguments keyword-only?

    Essentially for three reasons:

    • circle(x=100, y=100) is easier to explain than circle(100, 100) for first time learners, as it's clearer that x and y values are being assigned
    • Having code like circle(100, 50, 20) can confuse beginners, as they could misremember the order of args
    • circle(50) and circle(r=50) look very similar but result in completely different results
    opened by tusharsadhwani 0
  • Added text node.

    Added text node.

    In the first commit the flip transformation of the y-axis is exchanged by a sign change of all given y coordinates. This allows upright text.

    Then we can add a basic text node relatively straightforward. Of course I am looking forward for any feedback!

    opened by asteppke 1
  • Relative positioning inspired by TikZ

    Relative positioning inspired by TikZ

    First of all thank you for this fresh take on graphics in python. This is one of the few approaches that are directed at the user creating actual drawings with a succinct syntax!

    What comes to mind is that often a relative placement of objects would be handy. In the spirit of TikZ (Overleaf tutorial, TikZ on Wikipedia) to draw a diagram such as this one:

    image

    \begin{tikzpicture}[
    roundnode/.style={circle, draw=green!60, fill=green!5, very thick, minimum size=7mm},
    squarednode/.style={rectangle, draw=red!60, fill=red!5, very thick, minimum size=5mm},
    ]
    %Nodes
    \node[squarednode]      (maintopic)                              {2};
    \node[roundnode]        (uppercircle)       [above=of maintopic] {1};
    \node[squarednode]      (rightsquare)       [right=of maintopic] {3};
    \node[roundnode]        (lowercircle)       [below=of maintopic] {4};
    
    %Lines
    \draw[->] (uppercircle.south) -- (maintopic.north);
    \draw[->] (maintopic.east) -- (rightsquare.west);
    \draw[->] (rightsquare.south) .. controls +(down:7mm) and +(right:7mm) .. (lowercircle.east);
    \end{tikzpicture}
    

    So for something comparable in joy maybe something along these lines would allow this:

    maintopic = text(2)
    uppercircle = text(1) | above_of(maintopic)
    rightsquare = text(3) | right_of(maintopic)
    lowercircle = text(4) | below_of(maintopic)
    

    And corresponding lines:

    line(arrow='->') | from(uppercircle.south) | to(maintopic.north)
    # or alternative syntax ideas:
    line(from=uppercircle.south, to=maintopic.north)
    

    Would that be in the spirit of joy?

    opened by asteppke 0
  • Docs: getting-started.md and course link

    Docs: getting-started.md and course link

    Hello,

    PR is quite incomplete and I need have some questions and inputs on modification I have added course link in index.md and installation instructions in getting-started.md

    a) As show function uses IPython.display, how to get output in vscode ? I tried a decorator something like this but couldn't capture output from show function

    def open_in_browser(func):
        svg = func
    
        with open('output.html') as file:
    
            file.write('\n'.join(['<div style="border: 5px solid #ddd; position: relative;' \
                                  'z-index: 0; width: 300px; height: 300px;">',
                                  svg,
                                 '</div>'
                                ]))
    
        webbrowser.open(f'file://{__file__}/output.html') # this line is incomplete
        
     @open_in_browser
     show(circle())
    

    b) Need little inputs on documenting Reference.

    opened by ravish0007 0
Owner
FOSS United Foundation
Non-profit foundation that aims at promoting and strengthening the Free and Open Source Software community and ecosystem in India and elsewhere.
FOSS United Foundation
Python Indent - Correct python indentation in Visual Studio Code.

Python Indent Correct python indentation in Visual Studio Code. See the extension on the VSCode Marketplace and its source code on GitHub. Please cons

Kevin Rose 57 Dec 15, 2022
Spyder - The Scientific Python Development Environment

Spyder is a powerful scientific environment written in Python, for Python, and designed by and for scientists, engineers and data analysts. It offers a unique combination of the advanced editing, analysis, debugging, and profiling functionality of a comprehensive development tool with the data exploration, interactive execution, deep inspection, and beautiful visualization capabilities of a scientific package.

Spyder IDE 7.3k Jan 8, 2023
Gaphor is a UML and SysML modeling application written in Python.

Gaphor is a UML and SysML modeling application written in Python. It is designed to be easy to use, while still being powerful. Gaphor implements a fully-compliant UML 2 data model, so it is much more than a picture drawing tool. You can use Gaphor to quickly visualize different aspects of a system as well as create complete, highly complex models.

Gaphor 1.3k Jan 7, 2023
notebookJS: seamless JavaScript integration in Python Notebooks

notebookJS enables the execution of custom JavaScript code in Python Notebooks (Jupyter Notebook and Google Colab). This Python library can be useful for implementing and reusing interactive Data Visualizations in the Notebook environment.

jorgehpo 146 Dec 7, 2022
Mu - A Simple Python Code Editor

A small, simple editor for beginner Python programmers. Written in Python and Qt5.

Mu 1.2k Jan 3, 2023
A GitHub Action hosted Python IDE!

What is this ? This is an IDE running on GitHub Actions which can help in..... Running small snippets. Running codes whenever PC is not available and

Jainam Oswal 21 Nov 9, 2022
Python 3 patcher for Sublime Text v4107-4114 Windows x64

sublime-text-4-patcher Python 3 patcher for Sublime Text v4107-4114 Windows x64 Credits for signatures and patching logic goes to https://github.com/l

null 187 Dec 27, 2022
Python IDE or notebook to generate a basic Kepler.gl data visualization

geospatial-data-analysis [readme] Use this code in your Python IDE or notebook to generate a basic Kepler.gl data visualization, without pre-configura

null 2 Sep 5, 2022
A Python code editor that looks like GNU Emacs.

?? WARNING ?? : Under development... Testing is not recommended! Welcome to Snake Editor! Hi! This is our repository, we are here to present our new p

Marcio Dantas 5 May 20, 2022
This is code for IDLE python/ Magic8Ball Code

Magic8Ball this is code for IDLE python/ Magic8Ball Code this code is for beginers i hope you can learn code form this don't ever be a script kiddie a

null 1 Nov 5, 2021
cottonformation is a Python tool providing best development experience and highest productivity

Welcome to cottonformation Documentation Full Documentatioin Here cottonformation is a Python tool providing best development experience and highest p

Sanhe 6 Jul 8, 2022
An amazing simple Python IDE for developers!

PyHub An amazing simple Python IDE for developers! Get ready to compile and run your code in the most simplest and easiest IDE of the ancient world! T

Aniket Bhattacharjee 2 Dec 31, 2022
PSP (Python Starter Package) is meant for those who want to start coding in python but are new to the coding scene.

Python Starter Package PSP (Python Starter Package) is meant for those who want to start coding in python, but are new to the coding scene. We include

Giter/ 1 Nov 20, 2021
Transformer Huffman coding - Complete Huffman coding through transformer

Transformer_Huffman_coding Complete Huffman coding through transformer 2022/2/19

null 3 May 19, 2022
A simple python oriented telegram bot to give out creative font style's

Font-Bot A simple python oriented telegram bot to give out creative font style's REQUIREMENTS tgcrypto pyrogram==1.2.9 Installation Fork this reposito

BL4CK H47 4 Jan 30, 2022
The backend part of the simple password manager project made for the creative challenge.

SimplePasswordManagerBackend The backend part of the simple password manager project. Your task will be to showcase your creativity on our channel by

The Coding Jungle 5 Dec 28, 2021
Learn how modern web applications and microservice architecture work as you complete a creative assignment

Micro-service Создание микросервиса Цель работы Познакомиться с механизмом работы современных веб-приложений и микросервисной архитектуры в процессе в

Григорий Верховский 1 Dec 19, 2021
RuDOLPH: One Hyper-Modal Transformer can be creative as DALL-E and smart as CLIP

[Paper] [Хабр] [Model Card] [Colab] [Kaggle] RuDOLPH ?? ?? ☃️ One Hyper-Modal Tr

Sber AI 230 Dec 31, 2022
Easily display all of your creative avatars to keep them consistent across websites.

PyAvatar Easily display all of your creative avatars to keep them consistent across websites. Key Features • Download • How To Use • Support • Contrib

William 2 Oct 2, 2022
Technical Indicators implemented in Python only using Numpy-Pandas as Magic - Very Very Fast! Very tiny! Stock Market Financial Technical Analysis Python library . Quant Trading automation or cryptocoin exchange

MyTT Technical Indicators implemented in Python only using Numpy-Pandas as Magic - Very Very Fast! to Stock Market Financial Technical Analysis Python

dev 34 Dec 27, 2022