diff --git a/README.md b/README.md index e87a4e7..40d844b 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,10 @@ go run main.go flipbook.flip ## Language Design +5. SwirlEffect: This function applies a rotation transformation to each page of a flipbook, with the angle of rotation increasing for each subsequent page. The syntax is as follows: + +***swirl [Book ObjType variable] [Rotation Rate INT];*** + ### Rules: 1. There are two type of objects in this language: Book and Image. @@ -36,7 +40,7 @@ go run main.go flipbook.flip 3.2 Integers: They are any positive integer. -4. There are four types of Statements implemented right now: new, insert, keyframe, save. Every Statement must end with a semicolon. +4. There are five types of Statements implemented right now: new, insert, keyframe, save, swirl. Every Statement must end with a semicolon. 4.1 ***New***: It creates a new object Syntax - @@ -61,7 +65,16 @@ go run main.go flipbook.flip ***save [Book ObjType variable] outputNameString*** ## Example program -Lets animate apple falling over newton's head. +Lets animate apple falling over newton's head with a swirl effect. +``` +new book bookone = ((1600,1600), 25); +new image newton = ((100,100), "newton.png"); +insert newton bookone from page 1 to 25 at (600,1270); +new image melon = ((100,100), "melon.png"); +insert melon bookone from page 1 to 25 at (600,0); +keyframe melon bookone positionY (1,0) to (25, 1000); +swirl bookone 10; +save bookone "out.pdf"; ``` new book bookone = ((1600,1600), 25); new image newton = ((100,100), "newton.png"); @@ -90,5 +103,4 @@ KeyWords= { new, at, to, set, image, book, scale, insert, from, page, keyframe,, 1. Compelete the document: Due to the limitation of time, The documentation is not very resourceful. 2.Clean Up the Code: Due to the same reason, I couldnt get the chance to properly refactor code and comment it. 3. Add Expression pevaluation via Pratt's Parser technique -4. Add an analouge of Functions as Effects like swirl -5. Add more properties like opacity and more statements like 'set'. +4. Add more properties like opacity and more statements like 'set'. diff --git a/evaluator/evaluator.go b/evaluator/evaluator.go index 83b6d4c..e746b65 100644 --- a/evaluator/evaluator.go +++ b/evaluator/evaluator.go @@ -7,6 +7,16 @@ import ( "github.com/Krishanu230/Flipbook-Language/ast" "github.com/Krishanu230/Flipbook-Language/object" + "github.com/disintegration/imaging" +) + +func SwirlEffect(pages []object.PageProperty, rotationRate int) { + for i, page := range pages { + rotationAngle := i * rotationRate + for _, img := range page.ImagesProps { + img.Image = imaging.Rotate(img.Image, float64(rotationAngle), color.Transparent) + } + } "github.com/signintech/gopdf" ) @@ -14,7 +24,7 @@ var ( NULL = &object.Null{} ) -//eval a ast node. recursive nature +// eval a ast node. recursive nature func Eval(node ast.Node, env *object.Environment) object.Object { switch node := node.(type) { @@ -42,6 +52,21 @@ func Eval(node ast.Node, env *object.Environment) object.Object { return val } + case *ast.SwirlEffectStatement: + val := evalSwirlEffect(node, env) + if isError(val) { + return val + } + } + + func evalSwirlEffect(node *ast.SwirlEffectStatement, env *object.Environment) object.Object { + r := evalIdentifier(node.Book, env) + book, ok := r.(*object.Book) + if !ok { + return r + } + SwirlEffect(book.Pages, node.RotationRate.Value) + return &object.Null{} case *ast.SaveStatement: val := evalSave(node, env) if isError(val) { @@ -51,7 +76,7 @@ func Eval(node ast.Node, env *object.Environment) object.Object { return nil } -//eval a bunch of statements by passing them to Eval() +// eval a bunch of statements by passing them to Eval() func evalStatements(sts []ast.Statement, env *object.Environment) object.Object { var result object.Object @@ -62,7 +87,7 @@ func evalStatements(sts []ast.Statement, env *object.Environment) object.Object return result } -//resolve an identifier +// resolve an identifier func evalIdentifier(node *ast.Identifier, env *object.Environment) object.Object { val, ok := env.Get(node.Value) if ok { @@ -71,7 +96,7 @@ func evalIdentifier(node *ast.Identifier, env *object.Environment) object.Object return newError("identifier not found: " + node.Value) } -//evaluate New type of statements +// evaluate New type of statements func evalNew(inp *ast.NewStatement, env *object.Environment) object.Object { st := *inp if st.DType.Value == "image" { @@ -89,7 +114,7 @@ func evalNew(inp *ast.NewStatement, env *object.Environment) object.Object { return &object.Null{} } -//evaluate Insert statements +// evaluate Insert statements func evalInsert(inp *ast.InsertStatement, env *object.Environment) object.Object { //env.Print() r := evalIdentifier(inp.Image, env) @@ -125,7 +150,7 @@ func evalInsert(inp *ast.InsertStatement, env *object.Environment) object.Object return &object.Null{} } -//evaluate keyframe statements +// evaluate keyframe statements func evalKeyframe(inp *ast.KeyframeStatement, env *object.Environment) object.Object { r := evalIdentifier(inp.Image, env) img, ok := r.(*object.Image) @@ -177,11 +202,30 @@ func evalKeyframe(inp *ast.KeyframeStatement, env *object.Environment) object.Ob } func evalSave(inp *ast.SaveStatement, env *object.Environment) object.Object { + ix := 0 + iy := 0 r := evalIdentifier(inp.Book, env) book, ok := r.(*object.Book) if !ok { return r } + imgObj := evalIdentifier(inp.Image, env) + img, ok := imgObj.(*object.Image) + if !ok { + return imgObj + } + pdf = gopdf.GoPdf{} + pdf.Start(gopdf.Config{PageSize: gopdf.Rect{W: float64(book.DimX), H: float64(book.DimY)}}) + for pno, page := range book.Pages { + pdf.AddPage() + for _, img := range page.ImagesProps { + rotatedImg := img.Image + pdf.Image(rotatedImg, float64(ix), float64(iy), nil) + } + } + if !ok { + return r + } fname := inp.OutputName.Value pw := book.DimX ph := book.DimY @@ -192,7 +236,7 @@ func evalSave(inp *ast.SaveStatement, env *object.Environment) object.Object { for pno, page := range book.Pages { pdf.AddPage() for _, img := range page.ImagesProps { - imgpath := img.Image.Filename + imgpath := img.Filename //// TODO: implement scale property change by using a better library for pdf /*iw := int(img.Image.DimX * (0)) ih := int(img.Image.DimX * (0)) @@ -201,8 +245,8 @@ func evalSave(inp *ast.SaveStatement, env *object.Environment) object.Object { println("ERROR1") return newError("Image " + fname + " size larger than the page " + strconv.Itoa(pno)) }*/ - ix := img.PosX - iy := img.PosY + ix = img.PosX + iy = img.PosY if (ix > pw) || (iy > ph) { return newError("Image " + fname + " position beyond the page " + strconv.Itoa(pno)) } @@ -215,7 +259,7 @@ func evalSave(inp *ast.SaveStatement, env *object.Environment) object.Object { return &object.Null{} } -//Helper Functions +// Helper Functions func isError(obj object.Object) bool { if obj != nil { return obj.Type() == object.ERROR_OBJ diff --git a/evaluator/evaluator_test.go b/evaluator/evaluator_test.go new file mode 100644 index 0000000..74b5216 --- /dev/null +++ b/evaluator/evaluator_test.go @@ -0,0 +1,47 @@ +package evaluator + +import ( + "testing" + + "github.com/Krishanu230/Flipbook-Language/ast" + "github.com/Krishanu230/Flipbook-Language/object" +) + +func TestSwirlEffect(t *testing.T) { + pages := []object.PageProperty{ + {ImagesProps: []object.ImageProperty{{Image: object.Image{Rotation: 0}}}}, + {ImagesProps: []object.ImageProperty{{Image: object.Image{Rotation: 0}}}}, + } + SwirlEffect(pages, 10) + if pages[0].ImagesProps[0].Image.Rotation != 0 || pages[1].ImagesProps[0].Image.Rotation != 10 { + t.Errorf("SwirlEffect did not correctly update the rotation of the images") + } +} + +func TestEvalSwirlEffect(t *testing.T) { + env := object.NewEnvironment() + env.Set("bookone", &object.Book{Pages: []object.PageProperty{ + {ImagesProps: []object.ImageProperty{{Image: object.Image{Rotation: 0}}}}, + {ImagesProps: []object.ImageProperty{{Image: object.Image{Rotation: 0}}}}, + }}) + node := &ast.SwirlEffectStatement{Book: &ast.Identifier{Value: "bookone"}, RotationRate: &ast.IntegerLiteral{Value: 10}} + evalSwirlEffect(node, env) + book, _ := env.Get("bookone") + if book.(*object.Book).Pages[0].ImagesProps[0].Image.Rotation != 0 || book.(*object.Book).Pages[1].ImagesProps[0].Image.Rotation != 10 { + t.Errorf("evalSwirlEffect did not correctly update the rotation of the images") + } +} + +func TestEvalSave(t *testing.T) { + env := object.NewEnvironment() + env.Set("bookone", &object.Book{Pages: []object.PageProperty{ + {ImagesProps: []object.ImageProperty{{Image: object.Image{Rotation: 0}}}}, + {ImagesProps: []object.ImageProperty{{Image: object.Image{Rotation: 0}}}}, + }}) + node := &ast.SaveStatement{Book: &ast.Identifier{Value: "bookone"}, OutputName: &ast.StringLiteral{Value: "out.pdf"}} + evalSave(node, env) + book, _ := env.Get("bookone") + if book.(*object.Book).Pages[0].ImagesProps[0].Image.Rotation != 0 || book.(*object.Book).Pages[1].ImagesProps[0].Image.Rotation != 10 { + t.Errorf("evalSave did not correctly update the rotation of the images") + } +} diff --git a/lexer/lexer_test.go b/lexer/lexer_test.go index 1e9156b..49d27e1 100644 --- a/lexer/lexer_test.go +++ b/lexer/lexer_test.go @@ -41,6 +41,10 @@ func TestNextToken(t *testing.T) { {token.SCALE, "scale"}, {token.INT, "10"}, {token.SEMICOLON, ";"}, + {token.SWIRL, "swirl"}, + {token.IDN, "bookone"}, + {token.INT, "10"}, + {token.SEMICOLON, ";"}, } l := NewLexer(input)