You can use four different boolean operations on blocks to combine them into unique shapes. These operations are:
'Union'
: adds all the blocks’ shapes into one'Difference'
: removes from the bottom-most block the shapes of the other blocks overlapping with it'Intersection'
: keeps only the overlapping parts of all the blocks’ shapes'XOR'
: removes the overlapping parts of all the block’s shapes
Combining blocks allows you to create a new block with a customized shape.
Combining blocks with the union
, intersection
or XOR
operation will result in the new block whose fill is that of the top-most block and whose shape is the result of applying the operation pair-wise on blocks from the top-most block to the bottom-most block.
Combining blocks with the difference
operation will result in the new block whose fill is that of the bottom-most block and whose shape is the result of applying the operation pair-wise on blocks from the bottom-most block to the top-most block.
The combined blocks will be destroyed.
public func isCombinable(_ ids: [DesignBlockID]) throws -> Bool
Checks whether blocks could be combined. Only graphics blocks and text blocks can be combined. All blocks must have the “lifecycle/duplicate” scope enabled.
ids:
: The blocks for which the confirm combinability.- Returns: Whether the blocks can be combined.
public func combine(_ ids: [DesignBlockID], booleanOperation: BooleanOperation) throws -> DesignBlockID
Perform a boolean operation on the given blocks.
All blocks must be combinable. See isCombinable
.
The parent, fill and sort order of the new block is that of the prioritized block.
When performing a Union
, Intersection
or XOR
, the operation is performed pair-wise starting with the element
with the highest sort order.
When performing a Difference
, the operation is performed pair-wise starting with
the element with the lowest sort order.
Required scope: “editor/select”
ids
: The blocks to combine. They will be destroyed if “lifecycle/destroy” scope is enabled.booleanOperation
: The boolean operation to perform.- Returns: The newly created block.
Here’s the full code:
// Create blocks and append to scenelet star = try engine.block.create(.starShape)let rect = try engine.block.create(.rectShape)try engine.block.appendChild(to: scene, child: star)try engine.block.appendChild(to: scene, child: rect)
// Check whether the blocks may be combinedif try engine.block.isCombinable([star, rect]) { let combined = try engine.block.combine([star, rect], booleanOperation: .union)}
Combining three circles together
We create three circles and arrange in a recognizable pattern.
Combing them with 'Union'
result in a single block with a unique shape.
The result will inherit the top-most block’s fill, in this case circle3
’s fill.
let circle1 = try engine.block.create(.graphic) try engine.block.setShape(circle1, shape: engine.block.createShape(.ellipse)) try engine.block.setFill(circle1, fill: engine.block.createFill(.color)) try engine.block.setPositionX(circle1, value: 30) try engine.block.setPositionY(circle1, value: 30) try engine.block.setWidth(circle1, value: 40) try engine.block.setHeight(circle1, value: 40) try engine.block.appendChild(to: page, child: circle1)
let circle2 = try engine.block.create(.graphic) try engine.block.setShape(circle2, shape: engine.block.createShape(.ellipse)) try engine.block.setFill(circle2, fill: engine.block.createFill(.color)) try engine.block.setPositionX(circle2, value: 80) try engine.block.setPositionY(circle2, value: 30) try engine.block.setWidth(circle2, value: 40) try engine.block.setHeight(circle2, value: 40) try engine.block.appendChild(to: page, child: circle2)
let circle3 = try engine.block.create(.graphic) try engine.block.setShape(circle3, shape: engine.block.createShape(.ellipse)) try engine.block.setFill(circle3, fill: engine.block.createFill(.color)) try engine.block.setPositionX(circle3, value: 50) try engine.block.setPositionY(circle3, value: 50) try engine.block.setWidth(circle3, value: 50) try engine.block.setHeight(circle3, value: 50) try engine.block.appendChild(to: page, child: circle3)
let union = try engine.block.combine([circle1, circle2, circle3], booleanOperation: .union)
To create a special effect of text punched out from an image, we create an image and a text. We ensure that the image is at the bottom as that is the base block from which we want to remove shapes. The result will be a block with the size, shape and fill of the image but with a hole in it in the shape of the removed text.
let text = try engine.block.create(.text) try engine.block.replaceText(text, text: "Removed text") try engine.block.setPositionX(text, value: 10) try engine.block.setPositionY(text, value: 40) try engine.block.setWidth(text, value: 80) try engine.block.setHeight(text, value: 10) try engine.block.appendChild(to: page, child: text)
let image = try engine.block.create(.graphic) try engine.block.setShape(image, shape: engine.block.createShape(.rect)) let imageFill = try engine.block.createFill(.image) try engine.block.setFill(image, fill: imageFill) try engine.block.setPositionX(image, value: 0) try engine.block.setPositionY(image, value: 0) try engine.block.setWidth(image, value: 100) try engine.block.setHeight(image, value: 100) try engine.block.setString( imageFill, property: "fill/image/imageFileURI", value: "https://img.ly/static/ubq_samples/sample_1.jpg" ) try engine.block.appendChild(to: page, child: image)
try engine.block.sendToBack(image) let difference = try engine.block.combine([image, text], booleanOperation: .difference)
Full Code
Here’s the full code:
import Foundationimport IMGLYEngine
@MainActorfunc boolOps(engine: Engine) async throws { let scene = try engine.scene.create()
let page = try engine.block.create(.page) try engine.block.setWidth(page, value: 800) try engine.block.setHeight(page, value: 600) try engine.block.appendChild(to: scene, child: page)
let circle1 = try engine.block.create(.graphic) try engine.block.setShape(circle1, shape: engine.block.createShape(.ellipse)) try engine.block.setFill(circle1, fill: engine.block.createFill(.color)) try engine.block.setPositionX(circle1, value: 30) try engine.block.setPositionY(circle1, value: 30) try engine.block.setWidth(circle1, value: 40) try engine.block.setHeight(circle1, value: 40) try engine.block.appendChild(to: page, child: circle1)
let circle2 = try engine.block.create(.graphic) try engine.block.setShape(circle2, shape: engine.block.createShape(.ellipse)) try engine.block.setFill(circle2, fill: engine.block.createFill(.color)) try engine.block.setPositionX(circle2, value: 80) try engine.block.setPositionY(circle2, value: 30) try engine.block.setWidth(circle2, value: 40) try engine.block.setHeight(circle2, value: 40) try engine.block.appendChild(to: page, child: circle2)
let circle3 = try engine.block.create(.graphic) try engine.block.setShape(circle3, shape: engine.block.createShape(.ellipse)) try engine.block.setFill(circle3, fill: engine.block.createFill(.color)) try engine.block.setPositionX(circle3, value: 50) try engine.block.setPositionY(circle3, value: 50) try engine.block.setWidth(circle3, value: 50) try engine.block.setHeight(circle3, value: 50) try engine.block.appendChild(to: page, child: circle3)
let union = try engine.block.combine([circle1, circle2, circle3], booleanOperation: .union)
let text = try engine.block.create(.text) try engine.block.replaceText(text, text: "Removed text") try engine.block.setPositionX(text, value: 10) try engine.block.setPositionY(text, value: 40) try engine.block.setWidth(text, value: 80) try engine.block.setHeight(text, value: 10) try engine.block.appendChild(to: page, child: text)
let image = try engine.block.create(.graphic) try engine.block.setShape(image, shape: engine.block.createShape(.rect)) let imageFill = try engine.block.createFill(.image) try engine.block.setFill(image, fill: imageFill) try engine.block.setPositionX(image, value: 0) try engine.block.setPositionY(image, value: 0) try engine.block.setWidth(image, value: 100) try engine.block.setHeight(image, value: 100) try engine.block.setString( imageFill, property: "fill/image/imageFileURI", value: "https://img.ly/static/ubq_samples/sample_1.jpg" ) try engine.block.appendChild(to: page, child: image)
try engine.block.sendToBack(image) let difference = try engine.block.combine([image, text], booleanOperation: .difference)}