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.
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.
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).
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.)
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))
That's it: with these three lines of code and the definition of w
above you get a graphic on the screen.
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.)
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
.
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
.
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.
Events
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.
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.
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.
So, for example, you just can write
Subscriber.to(world) {
case e: Event ⇒ println(e)
}
to print out all events occurring on the specified world
.
To get the triggering enabled, the withEvents
factory methods of World
or Screen
have to be used.
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 ⇒ ()
}
API
See Scaladoc.
Maven Coordinates
See The Central Repository. Choose the version you want (of course, the latest one is recommended) and look under "Dependency Information".
Licence
SimGraf - A Simple Scala Graphics Library
Copyright 2016-2017 Hans-Hermann Bode
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.
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 ofScreen
. -
The
ScreenDriver
factory methods have been removed. -
The type of the
event.stream
value changed fromEventStream
toScreenEventBus
(both are implementations ofEventBus
) 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.