Giter Site home page Giter Site logo

drawpad-assignment's Introduction

Cool Stuff

I’ve recorded a video to make it simpler for you to understand what this project is doing :)

./tpl/youtube-thumbnail.png

Rationale

The project will implement a limited subset of Postscript language sufficient to render simple (two-dimensional) geometrical shapes

List of operators to implement:

  • closepath
  • lineto
  • moveto
  • newpath
  • rlineto
  • rmoveto
  • fill
  • setlinewidth
  • stroke
  • showpage
  • setrgbcolor not implementing setgray in an effort to reduce the workload

If time allows, also implement basic control structures, words and loops.

The reason for choosing Postscript over other candidates, which I shall discuss shortly was that it is the oldest industry standards, has canonical text representation suitable for human reader, is easy to implement, can be implemented iteratively, can be compiled to op-codes to reduce the file size, can be trivially validated for basic syntax consistency, is backened by a standard.

Below is the table of all other formats I’ve considered.

<8><8><8><8><8><8>
LanguageHuman-readableIndustry supportEasy to implementExpressivenessStandard
SVGYesPoorModerate (difficult to extract a small subset)Finite automataW3C
SWFNoSingle vendorDifficultTuring-completeOfficial white-paper
PDFNoGoodDifficultUnknownISO
LaTeXYesAverageDifficultTuring-completeNo
PSYesGoodEasyTuring-completeOfficial white-paper
XAML^1YesSingle vendorUnknownFinite automataUnknown
FXG^2YesSingle vendorEasy (exists)Finite automataNo
  1. I’ve not done much research on XAML graphic definition language simply because I don’t like Microsoft products, all I know is that it is not a standard in any way and no tool outside Microsoft’s own tool-chain is using it.
  2. FXG is implemented in Flex compiler (the MXMLC), however its use is bounded by many additional requirement, some may impact human productivity, others will impact performance. This was largely dismissed on the grounds of poor implementation.

It was very tempting to simply use SWF format, perhaps drawing on the work previously done by hxswfml project. I would prefer this solution in the real world setting, however, the implementation seems too cumbersome for the test assignment.

PDF would be another good possibility, also, there is an existing library, however, my experience with this library is that it may be buggy. Another argument against PDF is that it is not human-readable. The standard version of the format doesn’t support scripting (only Adobe version supports embedded JavaScript). The scripting in PDF is usually thought of as a security hazard.

Project structure

Postscript module

  • Context This module captures the graphical environment of the Postscript command being executed.
  • Interpreter This module reads and executes the text of the Postscript program.
  • ErrorMessages This is a class with a bunch of error messages used when throwing errors from various Postscript parts.
  • IOpcode An interface implemented by all Postscript op-codes.
  • IAsyncInputStream An interface for input character streams. This module provides two implementations, StringAsyncStream is an in-memory stream created from a string and UrlAsyncStream, an implementation based on URLStream.
  • PostScriptError An error thrown from various parts of Postscript module.
  • PS The loader, the highest level of interaction a user can have with Postscript module.
  • Reader The lexer used by Interpreter
  • StringAsyncStream A character input stream created from a string.
  • UrlAsyncStream An input character stream based on URLStream.

The Postscript module will provide means of loading and parsing of the Postscript source code. It will receive input source code of a program paired with the drawing surface and emit the interpreted output into the surface.

Tools

  • Keymap A container class for storing key-bindings, performs the same function as its namesake in Emacs.

The Tools module will capture and interpret user’s input into the executable commands.

Stage

  • Canvas
  • Stash

The Stage module will display the results of the user’s commands.

Bus

  • History This module will store the history of user interacting with the program. It will be responsible for reverting and replaying the commands.
  • Command This module records interactive commands performed by users.
  • EventServer This module mediates the input events to the connected clients. This server implements IServer.
  • IClient An interface a client must implement in order to be able to interact with IServer.
  • IServer An interface the server is required to implement.
  • Server A concrete but generic implementation of IServer.

The Bus module will connect the parts and provide the interface to the outside world (load data, handle errors, persist data etc.)

Effects

The Effects module will provide a library of visual or sound effects performed when user’s action is carried out. Seems like there won’t be any in this version.

Debugging

  • Console

Because it’s not possible currently to debug Flash written for players 11.3 and later, this console will translate the logging output into JavaScript console (you will need to run the program in the browser to see it).

Technical description

As of time of this writing I don’t have enough experience to try to use Stage3D or similar modern rendering techniques, partially because of time limit and because of the development setting (Stage3D is buggy on Linux) this is out of scope for now.

  • Target Flash Player version: 11.8
  • SDK used for compilation: 4.11 (had to build from upstream developers version, there’s not Linux distribution any more).
  • Project uses Apache Ant 1.8.4, but most likely will work with earlier versions too.

Project schema (UML)

@startuml

package tld.wvxvw.drawpad.bus <<Rect>> {
  interface ICommand {
    +ICommand execute()
    +ICommand udo()
  }

  interface IClient {
    +IServer server
    +void handle(String response, Array data)
  }
  
  interface IServer {
    +void add(IClient client)
    +void request(IClient client, String request, Array data)
  }
  
  class Command {
    +ICommand execute()
    +ICommand udo()
  }
  
  class Server {
    +void add(IClient client)
    +void echo(String command, Array args)
    +void fail(IClient client, String command, Array args)
    +void disconnect(IClient client)
    +void request(IClient client, String request, Array data)
    #void onRequest(IClient client, String request, Array data)
    +Vector.<String> listServices()
    +Array serviceArguments(String name, Boolean mandatoryOnly)
    +IAsyncInputStream callRpcService(IClient client, String name, Array args)
    +void loadConfig(Init config)
  }
  
  class EventServer {
    +void help(Event event)
    +void complete(IClient client)
    +void place(Shape shape)
    +void moveLeft(Event event)
    +void moveUp(Event event)
    +void moveDown(Event event)
    +void move(Event event)
    +void rotateLeft(Event event)
    +void rotateRight(Event event)
    +void select(Event event)
    +void drop(Event event)
  }
  
  class History {
    +Boolean inhibit
    +void push(ICommand command)
    +void undo()
  }
}

package tld.wvxvw.drawpad.config <<Rect>> {
  class Init {
    +Object keybindings()
    +Object server()
  }
}

package tld.wvxvw.drawpad.stage <<Rect>> {
  class Canvas {
    +void pick(Shape shape, int x, int y)
    +void drop(int x, int y)
    +void rotate(int angle)
    #Vector.<Function> rotateCommand(int angle)
    #Vector.<Function> pickCommand(Shape shape, int x, int y)
    #Vector.<Function> dropCommand(int x, int y)
    #Vector.<Function> selectCommand(int x, int y)
  }
  
  class GraphicClient {
    +IServer server
    #DisplayObjectContainer renderer
    #Vector.<DisplayObject> childern
    #Vector.<String> commands
    #DisplayObject selection
    #History history
    #IServer eventServer
    #Boolean ourSelection
    +void yank(DisplayObject child)
    +void select(int x, int y)
    +void unselect(int x, int y)
    +void move(int x, int y)
    #doInteractiveCommand(Vector.<Function> action)
    #Vector.<Function> placeCommand(DisplayObject child)
    #Vector.<Function> yankCommand(DisplayObject child)
    #Vector.<Function> selectCommand(int x, int y)
    #Vector.<Function> unselectCommand(int x, int y)
    #Vector.<Function> moveCommand(int x, int y)
  }
  
  class Stash {
    #Vector.<Function> yankCommand(DisplayObject child)
    #Vector.<Function> moveCommand(int x, int y)
    #Vector.<Function> placeCommand(DisplayObject child)
    #Vector.<Function> selectCommand(int x, int y)
    #Vector.<Function> unselectCommand(int x, int y)
  }
}

package tld.wvxvw.drawpad.tools <<Rect>> {
  class Keymap {
    +void defineKey(String keycode, Function handler)
    +void definePrefixKey(String key)
    +void dispatch(Event event)
  }
}

package tld.wvxvw.functions <<Rect>> {
  class Futils {
    +{static} Function bind(Function func, Object scope)
  }
}

package tld.wvxvw.postscript <<Rect>> {
  class PostScriptError {
  }
  
  class Context {
    +Boolean isString
    +Boolean isComment
    +void flush()
    +void reset()
    +Context offspring()
  }
  
  class ErrorMessage {
    +String ARGUMENT_COUNT_MISMATCH
    +String UNKNOWN_OPCODE
  }
  
  interface IAsyncInputStream {
    +Boolean isAtEnd
    +Boolean isAtStart
    +void readChar(Function callback)
    +void readLine(Function callback)
    +void readToken(Function callback, RegExp delimiter)
  }
  
  class Interpreter {
    +Shape shape
    +void interpret(IAsyncInputStream stream, Context context)
  }
  
  interface IOpcode {
    +void bind(Context context, Object argument)
    +void invoke(Context context)
    +Boolean needMoreArguments()
  }
  
  class PS {
    +EventDispatcher load(IAsyncInputStream stream, Shape shape)
  }
  
  class Reader {
    +void read(String token)
  }
  
  interface IEventDispatcher {
  }
  
  class StringAsyncStream {
    +Boolean isAtEnd
    +Boolean isAtStart
    +void readChar(Function callback)
    +void readLine(Function callback)
    +void readToken(Function callback, RegExp delimiter)
  }
  
  class UrlAsyncStream {
    +Boolean isAtEnd
    +Boolean isAtStart
    +void readChar(Function callback)
    +void readLine(Function callback)
    +void readToken(Function callback, RegExp delimiter)
  }
}

Application <|-- Sprite
Command <|-- ICommand
EventServer <|-- Server
Server <|--- IServer
Init <|-- ByteArray
Canvas <|-- GraphicClient
Stash <|-- GraphicClient
Interpreter <|-- EventDispatcher
StringAsyncStream <|-- EventDispatcher
StringAsyncStream <|-- IAsyncInputStream
UrlAsyncStream <|-- EventDispatcher
UrlAsyncStream <|-- IAsyncInputStream
IAsyncInputStream <|-- IEventDispatcher
PostScriptError <|-- Error
Reader <|-- EventDispatcher

Application o-- Canvas
Application o-- Stash
Application o-- Init
Application o-- PS

PS o-- Interpreter

Interpreter o-- Reader
Interpreter o-- IAsyncInputStream

Reader o-- Context
@enduml

Building

The instructions are given for RHEL-like distro, based on my experience with Fedora Cora 18, ymmv.

# yum install ant git java-1.7.0-openjdk

This was the easy part… Now, checkout Flex SDK:

$ cd ~
$ mkdir ./flex
$ cd ./flex
$ git clone https://git-wip-us.apache.org/repos/asf/flex-sdk.git sdk

I will refer to ~/flex/flex-sdk directory as $FLEX_HOME, if you cloned the SDK elsewhere, adjust your settings accordingly. Read carefully the Flex SDK readme and prepare to build it.

It should be only necessary that you build the compiler, this can be can be done like so:

$ cd $FLEX_SDK
$ ant modules

You will only need playerglobal.swc and related part of the setup as outlined in the readme. You don’t need to build or download neither TLF, nor AIR SDK. You will, however, need to download or build Batic Java library (used in compiler to transcode fonts). You don’t need Blaze DS.

Now you should be set to build the project. I will refer to project root directory as $basedir:

$ cd $basedir
$ vi ./build.xml

Change the value of $FLEX_HOME variable to where you installed Flex SDK. Also change the value of playerglobal variable to where you downloaded playerglobal.swc

$ ant

You should be all set up now.

Testing

There’s a minimal test suite, to test run the tests:

$ ant test

Running

The project comes with a minimalist server written in Python. Hopefully, no additional setup will be required. To start the server:

$ cd $basedir/server
$ ./services.py &

Now the project should be available at http://localhost:8080/ See:

$ ./services.py --help

for details on running the server.

drawpad-assignment's People

Contributors

wvxvw avatar

Watchers

James Cloos avatar  avatar

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.