Solibri API - Code Example

ClashDetectionRule Example

This example shows how to develop a simple rule that checks and visualized the clash between two different entities. User defines the checked components and the allowed tolerance between them.

This example shows also how to develop a simple UI Definition for this rule where the rule UI allows user to filter the selected components and and input a tolerance parameter represented in cubic meters.

You can download the examples project from here.

Table of Contents

  • Implementing the Clash Detection Rule Example
    • ClashDetectionRule.java
  • Implementing the UI Definition
    • ClashDetectionRuleUIDefinition.java
    • ClashDetectionRule.properties

Implementing the Clash Detection Rule Example

ClashDetectionRule.java

package com.solibri.smc.api.examples.beginner;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Set;

import com.solibri.smc.api.SMC;
import com.solibri.smc.api.checking.DoubleParameter;
import com.solibri.smc.api.checking.FilterParameter;
import com.solibri.smc.api.checking.OneByOneRule;
import com.solibri.smc.api.checking.Result;
import com.solibri.smc.api.checking.ResultFactory;
import com.solibri.smc.api.checking.RuleParameters;
import com.solibri.smc.api.filter.AABBIntersectionFilter;
import com.solibri.smc.api.filter.ComponentFilter;
import com.solibri.smc.api.intersection.Intersection;
import com.solibri.smc.api.model.Component;
import com.solibri.smc.api.model.PropertyType;
import com.solibri.smc.api.ui.UIContainer;

/**
 * An example rule that checks all clashes between components. A tolerance
 * parameter is included for specifying how much volume the clash must have to
 * be considered an issue.
 */
public final class ClashDetectionRule extends OneByOneRule {

	/**
	 * Constant strings are created to be used when creating different rule
	 * parameters. The declaration of constants keeps the code clean and
	 * reusable.
	 */
	private static final String COMPONENT_FILTER_PARAMETER_ID2 = "rpComponentFilter2";

	private static final String ALLOWED_TOLERANCE_PARAMETER_ID = "rpAllowedTolerance";

	/**
	 * Retrieve the rule parameters handler, used to define parameters for
	 * this rule.
	 */
	private final RuleParameters params = RuleParameters.of(this);

	/**
	 * The default FilterParameter is used for source components, and a new one is
	 * created for target components. A DoubleParameter is created to state the
	 * allowed tolerance for components.
	 * Every component that passes the default filter filter is then forwarded to
	 * the check method.
	 */
	final FilterParameter rpComponentFilter = this.getDefaultFilterParameter();

	final FilterParameter rpComponentFilter2 = params.createFilter(COMPONENT_FILTER_PARAMETER_ID2);

	final DoubleParameter rpAllowedTolerance = params.createDouble(ALLOWED_TOLERANCE_PARAMETER_ID, PropertyType.VOLUME);

	/**
	 * Add the UI definition from ClashDetectionRuleUIDefinition class.
	 */
	private final ClashDetectionRuleUIDefinition uiDefinition = new ClashDetectionRuleUIDefinition(this);

	@Override
	public Collection<Result> check(Component component, ResultFactory resultFactory) {
		/*
		 * Get the values from the model using a geometric filter combined with the
		 * second filter from the UI. It is best to start the filter chains with
		 * a geometric filter to allow the use of optimized geometric queries.
		 */
		ComponentFilter secondFilter = rpComponentFilter2.getValue();
		ComponentFilter targetComponentFilter = AABBIntersectionFilter.ofComponentBounds(component).and(secondFilter);
		Collection<Component> targets = SMC.getModel().getComponents(targetComponentFilter);

		/*
		 * Run the clash check for each component in the bounding box.
		 */
		Collection<Result> results = new ArrayList<>();
		for (Component target : targets) {
			results.addAll(clashCheck(component, target, resultFactory));
		}

		return results;
	}

	@Override
	public UIContainer getParametersUIDefinition() {
		return uiDefinition.getDefinitionContainer();
	}

	/**
	 * Checks for clashes between components and generates results.
	 *
	 * @param source the source component
	 * @param target the target component
	 */
	private Collection<Result> clashCheck(Component source, Component target, ResultFactory resultFactory) {

		final double allowedTolerance = rpAllowedTolerance.getValue();
		final double transparency = 0.5;

		Collection<Result> results = new ArrayList<>();

		Set<Intersection> intersections = source.getIntersections(target);

		/*
		 * Loop through each intersection to find the involved components, create
		 * results, and visualize them.
		 */
		for (Intersection intersection : intersections) {
			if (intersection.getVolume() < allowedTolerance) {
				continue;
			} else {
				String name = source.getName() + " clashes with " + target.getName();
				String description = "There is a clash between " + source.getName() + " and " + target.getName();

				Result result = resultFactory
					.create(name, description)
					.withInvolvedComponent(target)
					.withVisualization(visualization -> {
						visualization.addComponent(source, transparency);
						visualization.addComponent(target, transparency);
					});

				results.add(result);
			}
		}

		return results;
	}
}

ClashDetectionRuleUIDefinition.java

package com.solibri.smc.api.examples.beginner;

import com.solibri.smc.api.checking.RuleResources;
import com.solibri.smc.api.ui.BorderType;
import com.solibri.smc.api.ui.UIComponent;
import com.solibri.smc.api.ui.UIContainer;
import com.solibri.smc.api.ui.UIContainerVertical;
import com.solibri.smc.api.ui.UILabel;
import com.solibri.smc.api.ui.UIRuleParameter;

/**
 * Class that provides the UI layout for Clash Detection Rule. The UI Consists
 * of two component filters and one double values parameter field for length.
 */
class ClashDetectionRuleUIDefinition {

	/**
	 * The Clash Detection Rule.
	 */
	private final ClashDetectionRule clashDetectionRule;

	/**
	 * The UI definition container.
	 */
	private final UIContainer uiDefinition;

	/**
	 * Basic constructor.
	 *
	 * @param clashDetectionRule the clash detection rule
	 */
	public ClashDetectionRuleUIDefinition(ClashDetectionRule clashDetectionRule) {
		this.clashDetectionRule = clashDetectionRule;
		this.uiDefinition = createUIDefinition();
	}

	/**
	 * Returns the UI definition of the rule.
	 *
	 * @return the UI definition container of the rule
	 */
	public UIContainer getDefinitionContainer() {
		return uiDefinition;
	}

	/**
	 * Create the UI definition of the rule.
	 *
	 * @return the UI definition container of the rule
	 */
	private UIContainer createUIDefinition() {
		/*
		 * Fetch the resources for this rule.
		 */
		RuleResources resources = RuleResources.of(clashDetectionRule);

		/*
		 * Create the vertical component container.
		 */
		UIContainer uiContainer = UIContainerVertical.create(resources.getString("UI.ClashDetectionRule.TITLE"),
			BorderType.LINE);

		/*
		 * Add the description of the rule.
		 */
		uiContainer.addComponent(UILabel.create(resources.getString("UI.ClashDetectionRule.DESCRIPTION")));

		/*
		 * Add the first filter for components to check.
		 */
		uiContainer.addComponent(createFirstComponentFilterUIDefinition());

		/*
		 * Add the second filter for components to check.
		 */
		uiContainer.addComponent(createSecondComponentFilterUIDefinition());

		/*
		 * Add the tolerance filter for clash of components.
		 */
		uiContainer.addComponent(createAllowedToleranceUIDefinition());

		return uiContainer;
	}

	/**
	 * Create the UI definition of the first component filter.
	 *
	 * @return the UI definition container of the first component filter
	 */
	private UIComponent createFirstComponentFilterUIDefinition() {
		UIContainer uiContainer = UIContainerVertical.create();
		uiContainer.addComponent(UIRuleParameter.create(clashDetectionRule.rpComponentFilter));
		return uiContainer;
	}

	/**
	 * Create the UI definition of the second component filter.
	 *
	 * @return the UI definition container of the second component filter
	 */
	private UIComponent createSecondComponentFilterUIDefinition() {
		UIContainer uiContainer = UIContainerVertical.create();
		uiContainer.addComponent(UIRuleParameter.create(clashDetectionRule.rpComponentFilter2));
		return uiContainer;
	}

	/**
	 * Create the UI definition of the allowed tolerance.
	 *
	 * @return the UI definition container of the allowed tolerance
	 */
	private UIComponent createAllowedToleranceUIDefinition() {
		UIContainer uiContainer = UIContainerVertical.create();
		uiContainer.addComponent(UIRuleParameter.create(clashDetectionRule.rpAllowedTolerance));
		return uiContainer;
	}
}

ClashDetectionRule.properties

DEFAULT_NAME=Clash Detection Rule
DEFAULT_DESCRIPTION=This rule detects the clash between filtered components.
AUTHOR=Solibri, Inc.
AUTHOR_TAG=SOL
UID=EX4
VERSION=1.0
DATE=2019-8-15

rpComponentFilter.NAME=Source Components
rpComponentFilter.DESCRIPTION=This filter specifies the set of components to check.

rpComponentFilter2.NAME=Target Components
rpComponentFilter.DESCRIPTION=This filter specifies the set of components to check.

rpAllowedTolerance.NAME=Allowed Tolerance
rpAllowedTolerance.DESCRIPTION=Allowed tolerance before components clash. Represented as volume in cubic meters.
rpAllowedTolerance.DEFAULT_VALUE=0.01

UI.ClashDetectionRule.TITLE=Clash Detection Rule
UI.ClashDetectionRule.DESCRIPTION=This rule detects the clash between filtered components.