soegaard / sketching

A Racket library for creative drawings and animations. Inspired by Processing.
110 stars 10 forks source link

Support for shapes (PShapes) #69

Closed Ecsodikas closed 2 years ago

Ecsodikas commented 2 years ago

Here is a implementation for a big chunk of #10, I just wanted to check if this is the right direction or if anything should be changed. I played around with it and everything seems to work. I did no automated systematic testing. I just created some shapes and played around with them.

The following things are implemented from the original Processing Pshape:

The following things are missing:

I tried to use as much as possible from the shape struct, so I had to augment the function calls with optional arguments and I don't know if it would be better to duplicate code or to add optional arguments to functions. I think duplicated code is worse than multiple optionals. But I am by far no experienced Racketeer and I will change it if it is bad this way.

A example on using the shape class:

#lang sketching

(define s1 (shape-create))

(define (setup)
  (frame-rate 144)
  (size 500 500)
  (send s1 begin-shape 'quad-strip)
  (send s1 vertex 0 0)
  (send s1 vertex 50 0)
  (send s1 vertex 0 50)
  (send s1 vertex 50 50)
  (send s1 end-shape)
  (send s1 set-fill 0 0 255 100)
  (send s1 set-stroke 255 0 0 255)
  (send s1 translate 500 500))

(define (draw)
  (background 255)
  (text-size 16)
  (fill 123 12 44 255)
  (text (format "~a" frame-rate) 100 100)
  (send s1 draw mouse-x mouse-y))
soegaard commented 2 years ago

Hi,

This looks like a great start and is definitely in the right direction. I won't have time to try it out until the weekend, but I have two notes.

First the handling of translate needs to be changed. The change will give you what you need to implement rotate and scale for free.

The way translations, rotations and scaling works in Sketching is via transformations. The idea is that a combinations of translations, rotations and scalings can be represented as a single transformation (as a matrix).

Right now you represent a translation as (cons dx dy). Rename the instance variable variable translation to transformation-matrix and give it the initial value:

`(new-matrix 1 0 0 1 0 0)`   ; the identity transformation

When Shape.translate is called, you set it to the new value like this:

(send transformation-matrix 
     multiply (translation-matrix dx dy) transformation-matrix)

where translation-matrix is imported from transform.rkt.

Similarly, in transform.rkt there are helpers rotation-matrix, scaling-matrix and shearing-matrix.

Now when the shape is drawn one must do this:

- get the old transformation from current-dc and store it
- apply the transformation of the shape
- draw as usual
- restore the old transformation

This also solves your problem here:

I tried to use as much as possible from the shape struct, so I had to augment the function calls with optional arguments and I don't know if it would be better to duplicate code or to add optional arguments to functions.

Using the temporary transformation above, there is no longer a need for the optional arguments.

Finally, rename the class from shape% to Shape. I know this follows Java and not Racket conventions, but that's what I have used elsewhere in Sketching. I am not sure it was a good idea or not - but it does make the object/class system look more Java-like.

Feel free to dm me on Discord, if you something needs clarifying.

Thanks for working on this.

soegaard commented 2 years ago

@eXodiquas

This looks great, but I think you forgot to add "exports-no-gui.rkt" to your last commit.

image

Ecsodikas commented 2 years ago

@soegaard Whoops, should be fixed now.

soegaard commented 2 years ago

I'll try to find some examples with Pshape and port them.