Applying styles
Each MathObject has an associated Stylable object that stores its drawing parameters.
Basic styles
Colors
Each object has 2 colors: the draw color (changed with .drawColor), used for drawing the contour; and the fill color (changed with .fillColor), used to fill the object. Each color is stored in a JMColor object, with the components red, green, blue, and alpha. The .thickness method sets the thickness of the stroke used to draw the object.
def r = Shape.regularPolygon(5)
.fillColor(JMColor.parse("cadetblue"))
.drawColor(JMColor.parse("#041137"))
.thickness(15)
scene.add(r.center())
scene.advanceFrame()
When added to the scene, it will show something like this:

Here we can see the method .parse to define a color. All SVG color names are supported, as well as hexadecimal format #RRGGBBAA (8 hexadecimal digits), #RRGGBB (6 hexadecimal digits) and #RGB (3 hexadecimal digits). Additionally, both drawColor and fillColor accept a string directly, so that drawColor(string) is equivalent to drawColor(JMColor.parse(string)), and the same applies to fillColor.
def col1 = JMColor.rgba(1,1,1,.5)//White color with 50% opacity
def col2 = JMColor.parse("#CDFA14")//Red: CD, Green: FA, Blue: 14 (hexadecimal), opacity 100%
def col3 = JMColor.parse("#CDFA14A6")//Red: CD, Green: FA, Blue: 14 (hexadecimal), opacity A6
def col4 = JMColor.parse("snow")//Color SNOW from the SVG palette
The LatexMathObject also has the method .color(JMColor col), which changes both draw and fill colors at once. There is also an overloaded version that allows changing colors for specific glyphs (we will see this in the Mathematical Formulas chapter).
The methods .fillAlpha(double f) and .drawAlpha(double d) directly set the opacity of the fill and draw colors, respectively. These methods, like most, can be chained:
def sq=Shape.square().fillColor("cadetblue").fillAlpha(.5)
Gradients
All methods that accept colors also accept gradients (in fact, any class that extends PaintStyle).
Linear gradients can be defined in a similar way to the JavaFX syntax:
//A linear gradient from point (-1,0) to (1,0)
def gradientBG = JMLinearGradient.make(Vec.to(-1, 0), Vec.to(1, 0))
gradientBG.setRelativeToShape(false)
.add(0d, "orange")//at t=0 (point (-1,0)), orange color
.add(1d, "violet")//at t=1 (point (1,0)), violet color
config.setBackgroundColor(gradientBG)
def circle = Shape.circle()
//A radial gradient from relative point (.25,.75) and relative radius .75
def gradientCircle = JMRadialGradient.make(Vec.to(.25, .75), .5)
gradientCircle.setRelativeToShape(true)
.add(0d, "white")//At center of (.25,.75), white color
.add(1d, "steelblue")//With distance of the center >.5, steelblue color
circle.fillColor(gradientCircle)
scene.add(circle)
scene.advanceFrame()

You can apply gradients both to fill and draw colors:
Rect view = scene.getMathView()
//A linear gradient from the bottom of the screen to the top
def functionGradient = JMLinearGradient.make(view.getLower(), view.getUpper())
functionGradient.setRelativeToShape(false)
.add(0d, "blue")
.add(1d, "red")
//A radial gradient from the center, with radius 2
def axesGradient = JMRadialGradient.make(Vec.origin(), 2)
axesGradient.setRelativeToShape(false)
.add(0d, "black")
.add(1d, "violet")
def axes = Axes.make();
axes.generatePrimaryXTicks(-2, 2, .5)
.generatePrimaryYTicks(-2, 2, .5)
.drawColor(axesGradient)
def fg = FunctionGraph.make(x -> Math.sin(x*2), -2, 2)
fg.drawColor(functionGradient).thickness(10)
scene.add(axes, fg)
scene.advanceFrame()

DashStyle
The dashStyle method sets the dash used to draw the outline, chosen from the enum DashStyle. Currently, there are 4 different styles, SOLID, DASHED, DOTTED and DASHDOTTED. The following code creates 4 pentagons with these dash styles.
def r1 = Shape.regularPolygon(5).thickness(10);
def r2 = r1.copy().stack()
.withDestinyAnchor(AnchorType.RIGHT)
.withGaps(.1)
.toObject(r1)
def r3 = r1.copy().stack()
.withDestinyAnchor(AnchorType.RIGHT)
.withGaps(.1)
.toObject(r2)
def r4 = r1.copy().stack()
.withDestinyAnchor(AnchorType.RIGHT)
.withGaps(.1)
.toObject(r3)
r1.dashStyle(DashStyle.SOLID)
r2.dashStyle(DashStyle.DASHED)
r3.dashStyle(DashStyle.DOTTED)
r4.dashStyle(DashStyle.DASHDOTTED)
scene.add(
LatexMathObject.make("{\\tt SOLID}").stack().toObject(r1),
LatexMathObject.make("{\\tt DASHED}").stack().toObject(r2),
LatexMathObject.make("{\\tt DOTTED}").stack().toObject(r3),
LatexMathObject.make("{\\tt DASHDOTTED}").stack().toObject(r4),
r1, r2, r3, r4
)
camera.centerAtAllObjects()
scene.advanceFrame()

Saving styles
A particular combination of drawing parameters can be saved as a named style. The config object stores the saved styles and provides methods to manage them. To apply a style to an object, use the method .style(styleName).
def triangle = Shape.regularPolygon(3)
.thickness(8)
.dashStyle(DashStyle.DASHED)
.fillColor("steelblue")
//Creates style named myStyle
config.createStyleFrom(triangle, "myStyle")
def circle = Shape.circle().scale(.5)
.stack()
.withDestinyAnchor(AnchorType.LEFT)
.toObject(triangle)
//Apply style to circle
circle.style("myStyle")
scene.add(triangle, circle)
scene.advanceFrame()

Although using named styles is an efficient way to organize the appearance of an animation, you don't need to create them just to copy drawing attributes from one object to another. With the .getMP() method you can access directly the Stylable object that stores the drawing parameters. To copy the style from object A to object B, use the .copyFrom method like this:
B.getMP().copyFrom(A.getMP())
Configuring the scene
The Scene class has an instance of JMathAnimConfig, named config, that allows you to customize global aspects of the animation. Most of these methods should be called only in the setupSketch() part of the animation. Invoking config methods in runSketch() could lead to unpredictable behavior.
//Methods to adjust output
config.setMediaW(800)//Adjusts width output to 800px
config.setMediaH(600)//Adjusts height output to 600px
config.setFPS(25)//Adjusts frames per second of video output to 25 fps
config.setCreateMovie(true)//Generates a mp4 file with the animation
config.setOutputDir("media")//Specifies output directory at <PROJECT_DIR>\media (this is the default value)
config.setOutputFileName("animation")//Sets video filename as animation_WWW.mp4 where WWW is the width output (by default, the output file name is the name of the scene class)
config.setShowPreviewWindow(true)//Show the preview window (by default: true)
config.setBackgroundColor(JMColor.parse("WHITE)"))//Sets background color to white
config.setBackGroundImage("background.png")//Sets the background image, located at RESOURCES_DIR\images. If null, no image background is applied
config.setDrawShadow(true)//Apply shadow effect to the scene, using javafx shadow effect
config.setShadowParameters(10,15,15,.5f)//Sets shadow parameters (kernel 10, offsets 15 and 15, shadow alpha .5f)
config.setResourcesDir("resources")//Specifies resources directory at path <script_dir>\resources
config.setLoggingLevel(LogLevel.DEBUG)//Sets the logging level to DEBUG, showing more messages to the console
config.setLoggingLevel(4)//Sets the logging level using numbers. 0=No log, 1=Errors, 2=Warnings, 3=Info, 4=Debug
The configuration files
Loading config files
All settings and style definitions can be stored in XML files and loaded with the ConfigLoader class. This class provides the static method ConfigLoader.parseFile("file.xml"), which can also be called from the config object as config.parseFile("file.xml").
Where does JMathAnim look for the files? There are 3 location types that you can specify:
-
If the file name starts with
#it refers to an internal config file included in the library jar. -
If the file name starts with
!it refers to an absolute path. -
Otherwise, it will look into the
<default resources path>/config/folder.
By default, the resources path is located at the /resources folder. So if you want to add resources locally to your project, you should create this folder. You can change the default resources folder with the method config.setResourcesDir(newDir).
This way, if you want to store all your resources in a system-wide location, you can place them in a folder (say /home/bob/myJMathAnimResources) and point JMathAnim to it with the method config.setResourcesDir("/home/bob/myJMathAnimResources") at the beginning of the setupSketch() method (note that the ! modifier is not needed here).
A typical resources folder follows this structure:
resources/
├── config/
│ ├── preview.xml
│ ├── production.xml
│ ├── myStyles.xml
│ └── ...
├── images/
│ ├── background.png
│ ├── logo.svg
│ └── ...
└── ...
The config.parseFile will look into the config folder, and image-related objects like SVGMathObject or JMImage will look into the images folder.
A few examples:
- the
config.parseFile("file.xml")command will try to loadfile.xmllocated at<your current root project>/resources/configfolder - the
config.parseFile("#file.xml")command will try to loadfile.xmlinternally stored at the jar library. - the
config.parseFile("!/home/user/myResources/file.xml")command will try to loadfile.xmlfrom the location/home/user/myResources/file.xml.
Note: The "!" modifier can also be used when specifying a file path in the config files, like background images, for example.
If the program cannot find the file, the logger will report an error but the execution won't be stopped.
Here is an example of a basic config file that I use for previewing, called preview.xml. The <video> tag controls aspects related to movie output:
<JMathAnimConfig>
<video>
<size width="1066" height="600" fps="30"/>
<createMovie>false</createMovie>
<showPreviewWindow>true</showPreviewWindow>
</video>
</JMathAnimConfig>
And this for production, called productionWithShadow.xml. The background tag controls aspects like image or color background, or shadow effect.
<JMathAnimConfig>
<video>
<size width="1920" height="1080" fps="60"/>
<createMovie>true</createMovie>
<outputDir>c:\media</outputDir>
<showPreviewWindow>false</showPreviewWindow>
</video>
<background>
<shadows kernelSize="8" offsetX="15" offsetY="15" alpha=".5">true</shadows>
<image>background1080.png</image>
</background>
</JMathAnimConfig>
This way, you can change program behavior by just changing the config file loaded.
You can have several config files with different, independent aspects. This is the light.xml config I used in examples shown:
<JMathAnimConfig>
<include>#axes_and_functions_light.xml</include>
<include>#dots.xml</include>
<background>
<color>#FDFDFD</color>
</background>
<styles>
<style name="default">
<drawColor>black</drawColor>
<fillColor>#00000000</fillColor>
<thickness>4</thickness>
</style>
<style name="dotdefault">
<drawColor>black</drawColor>
<fillColor>#00000000</fillColor>
<thickness>30</thickness>
</style>
<style name="latexdefault">
<drawColor>black</drawColor>
<fillColor>black</fillColor>
<thickness>1</thickness>
</style>
<style name="solidRed">
<drawColor>black</drawColor>
<fillColor>#f55652</fillColor>
<thickness>8</thickness>
</style>
<style name="solidBlue">
<drawColor>black</drawColor>
<fillColor>#7ca0c0</fillColor>
<thickness>8</thickness>
</style>
<style name="solidGreen">
<drawColor>black</drawColor>
<fillColor>#9bc693</fillColor>
<thickness>8</thickness>
</style>
<style name="solidOrange">
<drawColor>black</drawColor>
<fillColor>orange</fillColor>
<thickness>8</thickness>
</style>
<style name="1">
<drawColor>#07004D</drawColor>
<thickness>30</thickness>
<dotStyle>circle</dotStyle>
</style>
<style name="2">
<drawColor>#BF2500</drawColor>
<thickness>30</thickness>
<dotStyle>cross</dotStyle>
</style>
<style name="3">
<drawColor>#5D2E8C</drawColor>
<thickness>30</thickness>
<dotStyle>plus</dotStyle>
</style>
<style name="fn1">
<drawColor>#0F4C5C</drawColor>
<fillColor>none</fillColor>
<thickness>8</thickness>
</style>
<style name="fn2">
<drawColor>#9A031E</drawColor>
<fillColor>none</fillColor>
<thickness>8</thickness>
</style>
<style name="fn3">
<drawColor>#5F0F40</drawColor>
<fillColor>none</fillColor>
<dashStyle>DASHED</dashStyle>
<thickness>4</thickness>
</style>
</styles>
</JMathAnimConfig>
The JAR of the JMathAnim library includes several predefined config files that you can load using the # prefix in the file name:
- The
config.parseFile("#preview.xml")loads settings for previewing the animation, with a low resolution of 1066x600 at 30fps, showing preview windows and not creating a movie, which is ideal for the creation process of the scene. This preset is automatically loaded when invoking Run (preview) from the editor. - The
config.parseFile("#production.xml")loads settings for generating the final animation, with high resolution 1920x1080 at 60fps, not showing preview windows and creating a movie. This config should be loaded when the design process is done and you want to create the final animation. This preset is automatically loaded when invoking Run (production) from the editor. - The
config.parseFile("#light.xml")loads settings for black drawings over a white background. The default colors are black. - The
config.parseFile("#dark.xml")loads settings for white drawings over a black background. The default colors are white.
You can check all the internal config files in the github sources folder.
The <styles> tag allows defining styles to apply to your animation. There are some convention-named styles that are important (names are case-insensitive):
- Style
default: All MathObjects (except the next ones mentioned) load this style when created. - Style
dotdefault: AllPointobjects load this style when created. - Style
latexDefault: Applied by default to allLaTexMathObjectinstances. - Style
functionGraphDefault: For function graphs. - Style
axisdefault: For x-axis and y-axis. - Style
axistickdefault: For x-ticks and y-ticks in the axes. - Style
axislegenddefault: For legends in ticks of axes. - If no style with these names is defined, a default style with white stroke and no fill will be applied.
Below is an example showing the same scene loaded with the dark.xml and light.xml config files. 
The <include> tag that appears at the beginning loads additional config files. In this example, a dots.xml file that defines styles for dots is included:
<JMathAnimConfig>
<styles>
<style name="redCircle">
<drawColor>RED</drawColor>
<thickness>30</thickness>
<dotStyle>circle</dotStyle>
</style>
<style name="blueCross">
<drawColor>#6ca2e0</drawColor>
<thickness>30</thickness>
<dotStyle>cross</dotStyle>
</style>
<style name="yellowPlus">
<drawColor>#FCE16D</drawColor>
<thickness>30</thickness>
<dotStyle>plus</dotStyle>
</style>
</styles>
Note that a dot requires a higher thickness than a Shape to be visible. This is because a Point with thickness 4 will have the same width as a stroke from a Shape with the same thickness, making it very small. Therefore, Point objects need a higher thickness value in order to be clearly visible.
Configuration files syntax
As mentioned above, config files use XML format. All files must have a root tag called <JMathAnimConfig>. Everything outside of this tag is ignored.
Inside this tag, we may have:
-
The
<include>tag allows loading another XML config file. For example<include>#axes_and_functions_dark.xml</include>(an internal config file included in the jar library) or<include>myColors.xml</include>(a file located in theresources/configfolder). -
The
<video>tag controls the output format. Inside this tag you can use: -
The
<size/>tag, with attributeswidth,heightandfps. For example<size width="1066" height="600" fps="30"/>. - The
<createMovie>tag takes a boolean value that determines whether a movie file is actually created. For example<createMovie>false</createMovie>. - The
<saveToPNG>tag takes a boolean value that determines whether each frame should be saved as a separate, auto-numbered PNG file. For example<saveToPNG>false</saveToPNG>. - The
<showPreviewWindow>tag controls whether the preview window is shown. For example<showPreviewWindow>true</showPreviewWindow>. - The
<outputDir>tag specifies the folder where the movie will be saved. If this tag is not present, the generated movie will be saved in theROOT_PROJECT_DIR/mediafolder by default. For example<outputDir>c:/my_generated_movies</outputDir>. -
The
<outputFileName>tag sets part of the file name of the generated movie. A suffix with the media height is always appended to the name. If this tag is not present, the name defaults to the class name of yourScene2Dsubclass. -
The
<background>tag contains settings related to the background and visual effects: -
The
<color>tag sets the background color. You can specify a color using a JavaFX color name (case insensitive), like<color>white</color>, or a hex format like<color>#F0A3C5</color>. The hex format can be 8 digits (RGBA), 6 digits (RGB with alpha 1) or 3 digits (RGB with alpha 1). - The
<image>tag sets a background image. You can use the!modifier to specify an absolute path. Otherwise, the program will look in theresources/imagesfolder. Note that no scaling or adjustment is performed, so the image dimensions should match those specified in the<size/>tag. -
The
<shadow>tag adds a shadow effect to all objects drawn on the screen, except for the background image. For example,<shadows kernelSize="8" offsetX="5" offsetY="5" alpha=".7">true</shadows>sets a shadow with a kernel size of 8 (the amount of shadow blurring), offsets of (5, 5) relative to the original objects, and an alpha transparency of 70%. -
The
<styles>tag defines all the styles you may need, each one inside a<style>subtag. For example:
```xml
```
It is pretty self-explanatory. The attribute name defines the style name (case sensitive) and the <drawColor>, <fillColor>, <thickness> and <dashStyle> tags define their respective properties. The <dashStyle> may take a value from the enum DashStyle, that is SOLID, DASHED, or DOTTED.
The <absoluteThickness> tag controls whether the thickness of the object is affected by scaling transformations. By default this is true, meaning that if you zoom in on a circle, for example, its stroke thickness will remain the same on screen. For objects like SVG imports, the absolute thickness flag is set to false.
The <dotStyle> tag accepts one of the values of the enum DotStyle in the Point class, and determines how a Point object with this style will be drawn. Currently, the possible values are CIRCLE, CROSS and PLUS.