README.md 7.22 KB
Newer Older
h2b's avatar
h2b committed
1 2 3 4
# SimGraf - A Simple Scala Graphics Library

This is a Scala library of graphics utilities. It is focused on simple drawing of shapes and functions without the need of instructing the graphics system by a dozen or so settings before a simple picture is shown. It is not meant to program graphical user interfaces with buttons, menus and so on.

h2b's avatar
h2b committed
5 6 7 8 9 10 11 12
## The World

A `World` provides graphics in world coordinates. For instance,

    val w = World(p1, p2)(p0, w, h, title)
    
defines a world that has a coordinate system with a `Point` `p1` in the lower-left corner and the upper-right corner at `p2`. All operations ensure clipping to that area, so that it is safe to use coordinates outside of it.

h2b's avatar
h2b committed
13
The second parameter group defines the location on the screen: `p0` denotes the upper left `Pixel`, `w` and `h` the width and the height of the window in pixels and `title` gives a window title (that will not reduce the drawing area).
h2b's avatar
h2b committed
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34

A `Point` defines a location in the world coordinate system using two doubles, while a `Pixel` denotes a location on the screen by means of two integers. 

Note that the y axis of the world coordinate system is directed from bottom to top while at the screen level it is vice versa.

So, for example

    val w = World(Point(-100,-100), Point(100,100))(Pixel(0,0), 200, 200, "Circle")

defines a world with x and y axis both ranging from -100 to +100 shown in a window of size 200x200 pixels at the upper left corner of the screen titled "Circle". 

Once you have a world, you can execute several methods on it: `plot` a point or `clear` the world to a specified color, use `moveTo` or `drawTo` for plotter-like operations and -- at the highest abstraction -- `draw` or `fill` shapes of type `Drawable` or `Fillable`, respectively. (For details see the [API](#api).)

Each world maintains an `activeColor` which can be set and is used for most drawings and fillings until it is changed (except for those that use their own color).

To fill a circle of color `Magenta` and radius 75.0 around the origin of our world `w` on a white background, we write:

    w.clear(Color.White)
    w.activeColor = Color.Magenta
    w.fill(Circle(Point(0,0), 75.0))

h2b's avatar
h2b committed
35
That's it: with these three lines of code and the definition of `w` above you get a graphic on the screen.
h2b's avatar
h2b committed
36 37 38 39 40 41 42 43 44 45 46 47 48

## The Shapes

Shapes are used for the `World`'s `draw` and `fill` methods. A shape can extend the `Shape.Drawable` or `Shape.Fillable` trait or both, which makes it applicable to the according method.

The package comes with a variety of predefined shapes ranging from simple figures like `Line`, `Rectangle` or `Circle` to higher-order ones like `Function` or even `Grid` which draws a simple coordinate system into the world. (For details see the [API](#api).)

Of course, you can define your own shapes. Just implement the `Shape.Drawable` or `Shape.Fillable` trait(s).

## The Screen

A `Screen` provides direct pixel graphics. It is the back end of `World`.

h2b's avatar
h2b committed
49
It can be used on its own if no world coordinate system is needed and bare screen-pixel coordinates shall be applied instead. Though, there are no fancy general shape-oriented `draw` and `fill` operations as `World` has to offer, but only some primitives like `setPixel`, `drawLine`, `drawSquare`, `fillSquare`, `moveTo` or `drawTo`.
h2b's avatar
h2b committed
50

h2b's avatar
h2b committed
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
## Layout

Since version 1.1.0, a layout package is available that collects some tools for positioning elements on the screen.

It contains a `Cell` (a rectangular area) and a `GridLayout` of cells organized into rows and columns as this:

    ----------------------------
    | cell | cell | cell | ... |
    ----------------------------
    | cell | cell | cell | ... |
    ----------------------------
    | cell | cell | cell | ... |
    ----------------------------
    | ...  | ...  | ...  | ... |
    ----------------------------

Various factory methods are provided that construct grid layouts from a sample cell, within a cell or on the whole screen.
h2b's avatar
h2b committed
68

h2b's avatar
h2b committed
69 70
## Events

h2b's avatar
h2b committed
71
Since version 1.2.0 an event package is available that constitutes a high-level abstraction of input (e.g., keyboard or mouse) events using an actor event bus.
h2b's avatar
h2b committed
72 73 74

Such events, if triggered by the drivers, are published to the global event stream of that package and can be retrieved by subscribing to its `stream` object.

75
Also, there is a `Subscriber` actor that can be used to handle events. Since version 1.3.0, it's companion object defines `to` methods for screens and worlds with a `PartialFunction[Event, Unit]` as a parameter.
h2b's avatar
h2b committed
76 77 78

So, for example, you just can write 

79
	  Subscriber.to(world) {
h2b's avatar
h2b committed
80 81
	    case e: Event ⇒ println(e)
	  }
h2b's avatar
h2b committed
82

h2b's avatar
h2b committed
83
to print out all events occurring on the specified `world`.
h2b's avatar
h2b committed
84

h2b's avatar
h2b committed
85 86
To get the triggering enabled, the `withEvents` factory methods of `World` or `Screen` have to be used.

87 88 89 90 91 92 93 94 95 96 97
Note that the program doesn't terminate by itself on closing all screens if the event package is used. This is due to background processes still listening to the event stream. For explicit termination call the `terminate` method on the package object's `system` value; this can be done, e. g., by subscribing to all screens or worlds and using a termination event like this: 

	  Subscriber.to(world1, world2) {
	    case KeyEvent(k) if k=='q' ⇒
	      world1.screen.close()
	      world2.screen.close()
	      println("terminating...")
	      system.terminate()
	    case _: Event ⇒ ()
	  }

h2b's avatar
h2b committed
98 99 100 101
## API

See [Scaladoc](http://hans-hermann-bode.de/sites/default/files/sites/simgraf/scaladocs/de/h2b/scala/lib/simgraf/index.html).

h2b's avatar
h2b committed
102 103
## Maven Coordinates

h2b's avatar
h2b committed
104
See [The Central Repository](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22de.h2b.scala.lib%22%20AND%20%22simgraf%22). Choose the version you want (of course, the latest one is recommended) and look under "Dependency Information".
h2b's avatar
h2b committed
105 106 107 108 109

## Licence

SimGraf - A Simple Scala Graphics Library

h2b's avatar
h2b committed
110
Copyright 2016-2018 Hans-Hermann Bode
h2b's avatar
h2b committed
111 112 113 114 115 116 117 118 119 120 121 122

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

<http://www.apache.org/licenses/LICENSE-2.0>

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
h2b's avatar
h2b committed
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140

## Major Changes

### Version 1.3.0

As of version 1.3.0, events are handled by a specialised `ScreenEventBus` instead of the standard Akka `EventStream`. This only has a slight impact on the API so that the major version of this project hasn't been promoted.

Anyway, for reference here are the substantial changes.

* The `AwtEventDriver` now can only be mixed into a class that is a subtype of `Screen`.

* The `ScreenDriver` factory methods have been removed.

* The type of the `event.stream` value changed from `EventStream` to `ScreenEventBus` (both are implementations of `EventBus`) in order to handle events classified by screen.

* The global `event.publisher` actor now only publishes events that are not associated with a screen rather than publishing all events. The drivers use dedicated publishers for each screen, respectively.

* Subscriber factory methods are provided for events of a specified screen or world. The `ref` method still is there, but receives no events from this package anymore.