Shape Intersection, Subtraction, and Union

Learn how to create new shapes by using shape intersection, subtraction, and union in the JavaFX Script programming language.

The JavaFX API Desktop Profile has classes to create common shapes: rectangles, circles, ellipses, polygons, and shapes with complex paths. But you can create many other shapes, including semicircles, crescents, and curves, by performing union, intersection, and subtraction operations on two or more shapes. This article describes union, intersection, and subtraction operations in JavaFX and shows some simple and more advanced examples of each type of operation.

Constructive Area Geometry Operations

Constructive area geometry operations perform Boolean operations on two or more existing shapes. These Boolean shape operations are implemented as node types in the JavaFX ShapeIntersect and ShapeSubtract classes. The following Boolean shape operations are implemented in JavaFX, assuming two shapes, Shape 1 and Shape 2:

  • Intersection, or Boolean AND
    This operation calculates the geometric outline that contains the area common to both Shape 1 and Shape 2. Use the (ShapeIntersect class.)
  • Subtraction, or Boolean NOT
    This operation calculates the geometric outline of the area in Shape 1 minus the area in Shape 2. (Use the ShapeSubtract class.)
  • Union, or Boolean OR
    This operation calculates the geometric outline that contains the area of both Shape 1 and Shape 2. (Assign both Shape 1 and Shape 2 to the sequence variable a of either the ShapeIntersect or ShapeSubtract class, which creates a union.)

The ShapeIntersect and ShapeSubtract class have the following common properties:

  • ShapeIntersect and ShapeSubtract are in the Desktop profile of the API, which means they cannot be used for mobile deployment.
  • ShapeIntersect and ShapeSubtract have two variables, a and b, which contain the member shapes to be intersected or subtracted. Both a and b are sequence variables, which means they can contain more than one member shape. Sequences within a variable are combined in Union operations before being operated against the other variable.
  • All nongeometric variables of member shapes are ignored. Variables such as fill and stroke should be set on the ShapeSubtract or ShapeIntersect object instance.
  • ShapeIntersect and ShapeSubtract do not respond to binding changes in variables of the component shapes. For example, the width of the shapes defined in the a and b variables of the ShapeSubtract instance cannot be bound to other variables such as scene width or scene height. This means that animations that involve dynamic changes to one of the member width or height variables are not possible.
  • ShapeIntersect and ShapeSubtract do not recognize transformations applied to member shapes.
  • Line instances do not work as member shapes of ShapeIntersect and ShapeSubtract . However, Polyline and Path instances work as long as they construct a shape with an internal geometric area.
  • If you want to rotate ShapeIntersect or ShapeSubtract objects, you might need to use the Node class transforms variable with a Rotate class instance instead of the rotate variable. The rotate variable calculates the center point of the object automatically, and this calculation can be unpredictable for ShapeIntersect or ShapeSubtract objects. By using a Rotate class instance, you can specify the pivot point. For more information about how to do transformations, see the related links at the end of this article.

Simple Examples of Intersection, Subtraction, and Union

The following example shows a classic Venn diagram. Because the three circles overlap, they are a good example to demonstrate intersection, subtraction, and union.

Venn diagram Figure 1: Classic Venn Diagram

Code Example 1: Classic Venn Diagram
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.scene.shape.Circle;

Stage {
    title: "Venn Diagram"
    scene: Scene {
        width: 90
        height: 150
        content: [
            //Circle 1
            Circle {
                centerX: 60
                centerY: 60
                radius: 30
                fill: Color.YELLOW
                opacity: 0.5
            }
            //Circle 2
            Circle {
                centerX: 45
                centerY: 85.98
                radius: 30
                fill: Color.INDIANRED
                opacity: 0.5
            }
            //Circle 3
            Circle {
                centerX: 75
                centerY: 85.98
                radius: 30
                fill: Color.BLUE
                opacity: 0.5
            }
        ]
    }
} 

Shape Intersection

The ShapeIntersect class intersects the unions of each sequence in variable a and b, as in (A1 OR A2 OR A3) AND (B1 OR B2 OR B3).

If Circle 1 in the Venn diagram is assigned to sequence a, and Circles 2 and 3 in the Venn diagram are assigned to sequence b, the following shape is produced, which consists of the Circle 1 intersected with the union of Circles 2 and 3. The code example adds a linear gradient plus a reflection effect.

Venn two-way intersection Figure 2: Venn Two-Way Intersection

Code Example 2: Two-Way Shape Intersection
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.ShapeIntersect;
import javafx.scene.effect.Reflection;
import javafx.scene.paint.LinearGradient;
import javafx.scene.paint.Stop;

def shapeintersect = ShapeIntersect {
    a:
        Circle {
            centerX: 60
            centerY: 60
            radius: 30
            fill: Color.YELLOW
            opacity: 0.5
        }
    b: [
        Circle {
            centerX: 45
            centerY: 85.98
            radius: 30
            fill: Color.INDIANRED
            opacity: 0.5
        },
        Circle {
            centerX: 75
            centerY: 85.98
            radius: 30
            fill: Color.BLUE
            opacity: 0.5
        }
     ]
     fill: LinearGradient {
            startX: 0.5, startY: 0.0, endX: 1.0, endY: 1.0
            proportional: true
            stops: [
                Stop { offset: 0.0 color: Color.CRIMSON }
                Stop { offset: 1.0 color: Color.DARKRED }
            ]
        }
    effect: Reflection {fraction: 0.6 }
}

Stage {
    title: "Intersect"
    scene: Scene {
        width: 90
        height: 150
        content: shapeintersect
    }
}

You can obtain the intersection of only two shapes at a time, so obtaining the intersection of all three circles requires two ShapeIntersect object instances. The first ShapeIntersect instance takes the intersection of any two of the circles, and the second ShapeIntersect instance takes the result of the first intersection and intersects it with the third circle.

Intersection of theree circles in Venn diagram Figure 3: Venn Three-Way Intersection

Code Example 3: Three-Way Shape Intersection
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.ShapeIntersect;

def shapeintersect1 = ShapeIntersect {
    a: Circle {
        centerX: 60
        centerY: 60
        radius: 30
        fill: Color.YELLOW
        opacity: 0.5
    }
    b: Circle {
        centerX: 45
        centerY: 85.98
        radius: 30
        fill: Color.INDIANRED
        opacity: 0.5
    }
}
def shapeintersect2 = ShapeIntersect {
    a: shapeintersect1
    b: Circle {
        centerX: 75
        centerY: 85.98
        radius: 30
        fill: Color.BLUE
        opacity: 0.5
    }
}

Stage {
    title: "Venn Diagram"
    scene: Scene {
        width: 90
        height: 150
        content: shapeintersect2
    }
}

Shape Subtraction

In terms of Boolean logic, shape subtraction equates to a NOT b. With the ShapeSubtract class, the union of all shapes in variable b is subtracted from the union of all shapes in variable a. The following images show some of the possible outcomes of the Venn diagram, depending on whether they are assigned to variable a or variable b.

Examples of shape subtraction using the Venn diagram Figure 4: Examples of Shape Subtraction

The following code example corresponds to the image on the right.

Code Example 4: Shape Subtraction
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.scene.shape.Circle;
import javafx.scene.shape.ShapeSubtract;
import javafx.scene.effect.DropShadow;
import javafx.scene.paint.LinearGradient;
import javafx.scene.paint.Stop;

Stage {
    title: "Subtraction"
    scene: Scene {
        width: 90
        height: 150
        content: [
            ShapeSubtract {
                a: Circle {
                    centerX: 60
                    centerY: 60
                    radius: 30
                    fill: Color.YELLOW
                    opacity: 0.5
                }
                
                b:  [ Circle {
                    centerX: 45
                    centerY: 85.98
                    radius: 30
                    fill: Color.INDIANRED
                    opacity: 0.5
                },
                    Circle {
                        centerX: 75
                        centerY: 85.98
                        radius: 30
                        fill: Color.BLUE
                        opacity: 0.5
                    }
                ]
                fill: LinearGradient {
                        startX: 0.0, startY: 0.0, endX: 1.0, endY: 1.0
                        proportional: true
                        stops: [
                            Stop { offset: 0.0 color: Color.WHITE }
                            Stop { offset: 1.0 color: Color.GRAY }
                        ]
                    }
                stroke: Color.BLACK
                effect: DropShadow {offsetX: 2 offsetY: 4}
            }
        ]
    }
}

Shape Union

In the JavaFX Script programming language, shape union can be accomplished by listing the shapes in variable a of either the ShapeIntersect or ShapeSubtract class. Without variable b, only the union of shapes in variable a will occur, with no intersection or subtraction.

The following code creates the union of the three circles in the Venn diagram. A drop shadow effect plus a linear gradient and stroke are added to the ShapeSubtract object instance.

Union of Venn diagram circles Figure 5: Union of Venn Diagram Circles

Code Example 5: Shape Union
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.scene.shape.Circle;
import javafx.scene.shape.ShapeSubtract;
import javafx.scene.effect.DropShadow;
import javafx.scene.paint.LinearGradient;
import javafx.scene.paint.Stop;

Stage {
    title: "Union"
    scene: Scene {
        width: 90
        height: 150
        content: [
            ShapeSubtract {
                a: [
                    Circle {
                        centerX: 60
                        centerY: 60
                        radius: 30
                    }
                    Circle {
                        centerX: 45
                        centerY: 85.98
                        radius: 30
                    }
                    Circle {
                        centerX: 75
                        centerY: 85.98
                        radius: 30
                    }
                ]
                fill: LinearGradient {
                    startX: 0.0, startY: 0.0, endX: 1.0, endY: 1.0
                    proportional: true
                    stops: [
                        Stop { offset: 0.0 color: Color.WHITE }
                        Stop { offset: 1.0 color: Color.GRAY }
                    ]
                }
                stroke: Color.BLACK
                effect: DropShadow {offsetX: 2 offsetY: 4}
            }
        ]
    }
}

Advanced Topics

The following topics demonstrate some effects and UI controls that you can construct in the JavaFX Script programming language by using shape union, subtraction, and intersection.

Tool Tip or Caption Box

The following example demonstrates a tool tip or caption box, created by taking the union of a rounded rectangle plus two lines created with the Polyline class. By using variables for the rectangle's position and radius, the definition text and caption box pointer can be placed to move with the rectangle if its location changes.

When the application runs, the caption box becomes visible when the mouse cursor is hovered over the word. To enable the caption box to appear when the mouse cursor is anywhere over the word, including the white space inside of and between letters, the following must happen:

  • A group must be created that includes the text and a transparent rectangle.
  • The mouse event must be defined for the group.

Caption box Figure 6: Tool Tip or Caption Box

Code Example 6: Tool Tip or Caption Box
import javafx.scene.shape.ShapeSubtract;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.shape.Path;
import javafx.scene.shape.LineTo;
import javafx.scene.shape.MoveTo;
import javafx.scene.text.Text;
import javafx.scene.text.Font;
import javafx.scene.input.MouseEvent;
import javafx.scene.Group;
import javafx.stage.Stage;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.Polyline; 

def scenewidth = 300;
def sceneheight = 200;
def rectanglex = 100;
def rectangley = 90;
def rectanglewidth = 180;
def rectangleheight = 80;
var visible:Boolean = false; //controls whether definition is visible

// Definition term
def term: Text = Text {
    content: "in·ter·sperse"
    font: Font {
        name: "Times New Roman"
        size: 20 }
    fill: Color.DARKGREEN
    x: 20
    y: 50
}

//Transparent rectangle over text to make clicking easier
def test = Rectangle {
    x: 20
    y: 30
    width: 150
    height: 30
    fill: Color.GRAY
    opacity: 0.0
}

// Group with term and transparent rectangle and mouse event.
def termgroup = Group {
    content: [
        test, term
    ]
    onMouseEntered: function( e: MouseEvent ):Void {
        visible = true;
    }
    onMouseExited: function( e: MouseEvent ):Void {
        visible = false;
    }
}

// Caption box shape
def captionbox = ShapeSubtract {
    a: [ Rectangle {
            x: rectanglex
            y: rectangley
            width: rectanglewidth
            height: rectangleheight
            arcHeight: 14
            arcWidth: 14
          },
          Polyline {
            points: [
                rectanglex, rectangley,
                rectanglex - 40, rectangley - 30,
                rectanglex  + 20, rectangley + 50
             ]
          }
        ]
    fill: null
    stroke: Color.LIGHTGREY
    strokeWidth: 2
    //visible: bind visible
}

// Definition text
def definition = Text {
    content: "to diversify or adorn with things set or scattered at intervals"
    wrappingWidth: rectanglewidth - 40
        font: Font {
            name: "Times New Roman"
            size: 12 
        }
    fill: Color.GREY
    x: rectanglex + 20
    y: rectangley + 20
}

def captiongroup: Group = Group {
    content: [ captionbox, definition ]
    visible: bind visible
} 

Stage {
    title: "Caption Box"
    scene: Scene {
         width: scenewidth
         height: sceneheight
         content: [ termgroup, captiongroup ]
    }
}

Citation for definition: "intersperse." Webster's Third New International Dictionary, Unabridged. 2002. [http://unabridged.merriam-webster.com], accessed November 11, 2009).

Next Button Created by Intersection, Subtraction, and Union

Triangles are created with the Polygon class, but you can make your triangle a little more unusual by adding one rounded edge. In this example, the triangle is embedded in a rounded rectangle with the text "Next" subtracted from it to achieve a cutout effect.

Next button Figure 7: Next Button as It Appears on Windows and Mac OS X

The steps to create this button are shown in the following subtasks.

Task 1: Create the Triangle

Triangles are typically created by using an instance of the Polygon class. The coordinates for this triangle were taken from Building GUI Applications With JavaFX Lesson 4: Creating Graphical Objects, and the fill color was changed. The exact placement in the scene doesn't matter right now, because the final object can be placed elsewhere. The fill color doesn't matter either, because it will be ignored when the shape is combined with the curve.

Triangle for Next button Figure 8: Triangle for Next Button

Code Example 7: Triangle
import javafx.scene.paint.Color;
import javafx.scene.shape.Polygon;
import javafx.scene.Scene;
import javafx.stage.Stage;

Stage {
    title: "Triangle"
    scene: Scene {
        width: 200
        height: 120
        content: [
           Polygon {
            points: [162.0, 61.0, 130.0, 43.0, 130.0, 79.0]
            fill: Color.TURQUOISE
        }
        ]
    }
}

Task 2: Create the Curve Using Shape Intersection

The curve at the end of the triangle is created by taking the intersection of a circle and the mirror image of the triangle. Use the following series of steps:

  1. Create a mirror shape of the original triangle, as shown in (a).
  2. Create a circle whose center point is the right tip of the original triangle, and whose radius is the same as the distance from the tip to the base of the original triangle, as shown in (b).
  3. Use the ShapeIntersect class to create an intersection between the mirrored (gray) triangle and the black circle, as shown in (c) in Figure 9 and Code Example 8.

Triangle intersected with circle Figure 9: Triangle Intersected With Circle

Code Example 8: Triangle Intersection With Circle
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Polygon;
import javafx.scene.shape.ShapeIntersect;

//Create the curve by intersecting a circle
// with a mirror image of the triangle
def curve = ShapeIntersect {
    a: Circle {
            centerX: 162
            centerY: 61
            radius: 36
        }
    b: Polygon {
            points: [98.0, 61.0, 130.0, 43.0, 130.0, 79.0]
        }
}

Stage {
    title: "Rounded portion of triangle"
    scene: Scene {
        width: 200
        height: 120
        content: [
            curve
        ]
    }
}

Task 3: Combine the Intersected Curve With the Original Triangle

To produce the rounded triangle, take the union of the curve shape and the original triangle. As described previously, shape union is accomplished by using the a sequence variable of the ShapeSubtract class. The following image and code show the rounded rectangle, to which a fill color, reduced opacity, and a drop shadow have been applied.

Rounded triangle Figure 10: Rounded Triangle

Code Example 9: Rounded Triangle With Shape Union
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Polygon;
import javafx.scene.shape.ShapeIntersect;
import javafx.scene.effect.DropShadow;
import javafx.scene.paint.Color;
import javafx.scene.shape.ShapeSubtract;

//Create the curve by intersecting a circle
// with a mirror image of the triangle
def curve = ShapeIntersect {
    a: Circle {
            centerX: 162
            centerY: 61
            radius: 36
        }
    b: Polygon {
            points: [98.0, 61.0, 130.0, 43.0, 130.0, 79.0]
        }
}

//Create a union of the curve plus the triangle
def arrow = ShapeSubtract {
    a: [Polygon {
            points: [162.0, 61.0, 130.0, 43.0, 130.0, 79.0]
        },
        curve
    ]
    // Set fill and effects on the subtracted shape
    fill: Color.web("#1F6592")
    //Decrease the opacity and add a drop shadow
    opacity: 0.7
    effect: DropShadow {offsetY: 2}
    // Reposition the triangle in the center of the scene
}

Stage {
    title: "Rounded Rectangle"
    scene: Scene {
        width: 200
        height: 200
        content: [
            arrow
        ]
    }
}

Task 4: Give the Arrow a 3-D Appearance

A common design trick to give the object more of a 3-D appearance is to create a darker-colored curve with low opacity on the lower half of the shape. In this case, the darker curve is created by defining a circle that overlaps the triangle in a small portion of its arc, as shown in (a) in Figure 11. The intersection of the rounded rectangle and the circle is taken, leaving the small portion of the arc shown in (b).

Rounded triangle Figure 11: Creating the Dark Underside of the Rounded Triangle

Code Example 10: Arrow With a Dark Underside
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.ShapeIntersect;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.scene.Group;
import javafx.scene.effect.DropShadow;
import javafx.scene.shape.Polygon;
import javafx.scene.shape.ShapeSubtract;

//Create the curve by intersecting a circle
// with a mirror image of the triangle
def curve = ShapeIntersect {
    a: Circle {
            centerX: 162
            centerY: 61
            radius: 36
        }
    b: Polygon {
            points: [98.0, 61.0, 130.0, 43.0, 130.0, 79.0]
        }
}

//Create a union of the curve plus the triangle
def arrow = ShapeSubtract {
    a: [Polygon {
            points: [162.0, 61.0, 130.0, 43.0, 130.0, 79.0]
        },
        curve
    ]
    // Set fill and effects on the subtracted shape
    fill: Color.web("#1F6592")
    //Decrease the opacity and add a drop shadow
    opacity: 0.7
    effect: DropShadow {offsetY: 2}
    // Reposition the triangle in the center of the scene
}

// Create the shadow on the lower half of the arrow
def shadow = ShapeIntersect {
    a: arrow,
    b: Circle {
            centerX: 160
            centerY: 98
            radius: 40
        }
    fill: Color.web("#245D82")
    opacity: 0.5
}

Stage {
    title: "Curved Arrow With Shadow"
    scene: Scene {
        width: 200
        height: 120
        content: [
            arrow, shadow
        ]
    }
}

Task 5: Create the Enclosing Rectangle, Text Cutout, and Background

Text can be subtracted from other shapes to create a cutout effect. In this case, a gray rectangle is created, the text is subtracted, and a drop shadow effect is applied to the subtracted shape. Because the drop shadow applies to the cutout text as well as to the outer edges of the gray rectangle, a rectangle of the same size but darker in color is inserted underneath. This manipulation reduces the drop shadow contrast in the cutout text, particularly when run on Windows.

Note: A type of intersection in which the upper layer is transparent and enables the lower layer to show through is called clipping. Clipping applies to all nodes, not just geometric area shapes. For more information, see How do I clip shapes?

Rectangle with cutout text Figure 12: Creating the Rectangle With Cutout Text

Code Example 11: Rectangle With Cutout Text and Black Background
import javafx.scene.paint.Color;
import javafx.scene.shape.ShapeSubtract;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.scene.effect.DropShadow;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.scene.text.TextOrigin;
import javafx.scene.text.FontWeight;

// Create a background rectangle to darken the subtracted text
def background = Rectangle {
    x: 20
    y: 25
    width: 160
    height: 75
    arcHeight: 20
    arcWidth: 20
    fill: Color.GREY
}

// Create the rounded rectangle with text subtracted
def textbox = ShapeSubtract {
    a: Rectangle {
        x: 20
        y: 25
        width: 160
        height: 75
        arcHeight: 20
        arcWidth: 20
    }
    b: Text {
        content: "Next"
        textOrigin: TextOrigin.TOP
        font: Font.font("Verdana", FontWeight.EXTRA_BOLD, 30)
        x: 35
        y: 48
    }
    // Set fill and effects on the subtracted shape
        fill: Color.DARKGRAY
        effect: DropShadow {offsetY: 2}
}

Stage {
    title: "Next button"
    scene: Scene {
        width: 200
        height: 120
        content: [
            background, textbox
        ]
    }
}

Task 6: Group, Resize, and Move the Objects

To make the button smaller or larger, combine the objects into a Group node, then use the scaleX and scaleY variables to resize the group. In this case, the object has been reduced to half its original size.

Use the layoutX and layoutY variables to move the button into position in the scene.

Figure 13 and Code Example 12 are the entire application to create the Next button, including all of the previous steps.

Next button Figure 13: Next Button at 50% Size and Moved to New Position

Code Example 12: Complete Code for a Next Button
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Polygon;
import javafx.scene.shape.ShapeIntersect;
import javafx.scene.shape.ShapeSubtract;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.scene.effect.DropShadow;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.scene.text.TextOrigin;
import javafx.scene.text.FontWeight;
import javafx.scene.Group;

//Create the curve by intersecting a circle
// with a mirror image of the triangle
def curve = ShapeIntersect {
    a: Circle {
            centerX: 162
            centerY: 61
            radius: 36
        }
    b: Polygon {
            points: [98.0, 61.0, 130.0, 43.0, 130.0, 79.0]
        }
}

//Create a union of the curve plus the triangle
def arrow = ShapeSubtract {
    a: [Polygon {
            points: [162.0, 61.0, 130.0, 43.0, 130.0, 79.0]
        },
        curve
    ]
    // Set fill and effects on the subtracted shape
    fill: Color.web("#1F6592")
    //Decrease the opacity and add a drop shadow
    opacity: 0.7
    effect: DropShadow {offsetY: 2}
    // Reposition the triangle in the center of the scene
}

// Create the shadow on the lower half of the arrow
def shadow = ShapeIntersect {
    a: arrow,
    b: Circle {
            centerX: 160
            centerY: 98
            radius: 40
        }
    fill: Color.web("#245D82")
    opacity: 0.5
}

// Create a background rectangle to darken the subtracted text
def background = Rectangle {
        x: 20
        y: 25
        width: 160
        height: 75
        arcHeight: 20
        arcWidth: 20
        fill: Color.GREY
    }

// Create the rounded rectangle with text subtracted
def textbox = ShapeSubtract {
    a: Rectangle {
        x: 20
        y: 25
        width: 160
        height: 75
        arcHeight: 20
        arcWidth: 20
    }
    b: Text {
        content: "Next"
        textOrigin: TextOrigin.TOP
        font: Font.font("Verdana", FontWeight.EXTRA_BOLD, 30)
        x: 35
        y: 48
    }
    // Set fill and effects on the subtracted shape
        fill: Color.DARKGRAY
        effect: DropShadow {offsetY: 2 }
}

//Group all of the objects, size to 50% and move
def button = Group {
    content: [background, textbox, arrow, shadow]
    scaleX: 0.5
    scaleY: 0.5
    layoutX: -20
    layoutY: -20
}

Stage {
    title: "Next button"
    scene: Scene {
        width: 200
        height: 120
        content: [
            button
        ]
    }
}

Related Links