Skip to main content

Parts

In this tutorial you learn the basics of creating a part (a component) with parametric geometry. The example product is a bearing whose diameter is configurable and whose number of balls updates automatically.

You complete five steps:

  1. Create component
  2. Create assets
  3. Create models
  4. Add balls dynamically
  5. Create component with properties

1. Create Component

Create a component container in your app and name it BEARING.

The rings are produced by revolving a profile. The balls are simple spheres. You could model these manually, but for simplicity this tutorial uses provided static assets. The bearing depends on one property: diameter. The number of balls and ring positions derive from it.

2. Create assets

  1. Download the 3D static asset BALL.glb.
  2. Download the 2D static asset RING_PROFILE.dxf.
  3. In your app dashboard upload both files and create static sketches/models by checking the checkboxes in the popup.
  4. Confirm both assets appear in the dashboard:
    • RING_PROFILE under Static Sketches.
    • BALL under Static Models.

Import the assets so you can use them:

  1. Open the BEARING component.
  2. At the top click ⚙️ > Edit Imports, check ASSETS, then Apply changes.

3. Create models

A. Ball

Create the ball model using a snippet.

  1. Click Model and create an Extrude from any shape.
  2. Rename the function to getBallModel().
  3. Replace the model body with the code below. Keep return model and remove unused generated code.
  4. Click Save & Run, then select the function in the left sidebar to view it.
    export function getBallModel() {
    const model = ASSETS.STATIC_MODELS.BALL
    return model
    }

B. Rings

The ring profile is reused for inner and outer rings. For the outer ring you clone, mirror, move, and merge the profile. Use a sketch snippet to work faster.

  1. Click Sketch and create one from any shape.
  2. Rename the function to getRingProfileSketch.
  3. Add the input parameter: export function getRingProfileSketch(diameter: number) { ... }.
  4. Replace the sketch assignment with const sketch = ASSETS.STATIC_SKETCHES.RING_PROFILE.
  5. At this point the sketch represents the inner ring and should look like the snippet below.
    export function getRingProfileSketch(diameter: number) {
    const sketch = ASSETS.STATIC_SKETCHES.PROFILE_75X75
    return sketch
    }
  6. Create the outer ring by cloning and modifying the sketch before the return statement:
    1. Clone: const outerSketch = sketch.clone() (safe copy so the static asset is not modified).
    2. Mirror in Y: outerSketch.mirrorInYaxis().
    3. Translate to leave space for the ball: outerSketch.translate(30, 0).
    4. Merge: sketch.mergeSketch(outerSketch).
  7. After these steps both rings are in place. Your code should look like:
    export function getRingProfileSketch(diameter: number) {
    const sketch = ASSETS.STATIC_SKETCHES.RING_PROFILE

    const outerSketch = sketch.clone()
    outerSketch.mirrorInYaxis()
    outerSketch.translate(30, 0)
    sketch.mergeSketch(outerSketch)

    return sketch
    }
  8. Adjust for the inner diameter before the return statement:
    1. Add: const innerDiameter = diameter - 30.
    2. Translate in x to account for innerDiameter and ball diameter: sketch.translate(innerDiameter, 0).
  9. Final function:
    export function getRingProfileSketch(diameter: number) {
    const sketch = ASSETS.STATIC_SKETCHES.RING_PROFILE

    const outerSketch = sketch.clone()
    outerSketch.mirrorInYaxis()
    outerSketch.translate(30, 0)
    sketch.mergeSketch(outerSketch)

    const innerDiameter = diameter - 30
    sketch.translate(innerDiameter, 0)

    return sketch
    }

Create the revolve model:

  1. Click Model and choose Revolve using getRingProfileSketch.
  2. Rename the function to getRingsModel.

note

diameter is already connected automatically because the sketch takes it as input.

4. Add balls dynamically

Now assemble both models. The ball model is duplicated in a loop and positioned evenly. The number of balls depends on diameter.

  1. In GEOM3D, next to getBallModel and getRingsModel, create a third function named getBearingGeometry.
  2. Start it with an empty group: const geometryGroup = new SKYCAD.GeometryGroup and return it.
  3. Add the diameter input: export function getBearingGeometry(diameter: number) { ... }.
    export function getBearingGeometry(diameter: number) {
    const geometryGroup = new SKYCAD.GeometryGroup()
    return geometryGroup
    }
  4. Add the rings model before the return:
    const ringsModel = getRingsModel(diameter)
    geometryGroup.addGeometry(ringsModel)
  5. Add a loop that calculates nrBalls from diameter and positions each ball. You can write your own or use the code below which uses the built-in Math library:
    const ballModel = getBallModel()
    const nrBalls = Math.round(diameter / 8)
    const stepAngle = (2 * Math.PI) / nrBalls // [radians]
    for (let i = 0; i < nrBalls; i++) {
    const position = new SKYMATH.Vector3D(
    (diameter - 15) * Math.cos(i * stepAngle), // x-position
    (diameter - 15) * Math.sin(i * stepAngle), // y-position
    0, // z-position
    )
    geometryGroup.addGeometry(ballModel, { position })
    }
  6. Add a material to the ring:
    const ringsModel = getRingsModel(diameter)
    const ringMaterials = [new SKYCAD.Material({ color: 0x777777, metalness: 1, roughness: 0.3 })]
    geometryGroup.addGeometry(ringsModel, { materials: ringMaterials })

7. Final function:

export function getBearingGeometry(diameter: number) {
const geometryGroup = new SKYCAD.GeometryGroup()

const ringsModel = getRingsModel(diameter)
const ringMaterials = [new SKYCAD.Material({ color: 0x777777, metalness: 1, roughness: 0.3 })]
geometryGroup.addGeometry(ringsModel, { materials: ringMaterials })

const ballModel = getBallModel()
const nrBalls = Math.round(diameter / 8) // Math.round ensures an integer number (i.e. without decimals)
const stepAngle = (2 * Math.PI) / nrBalls // 360° divided by nr balls
for (let i = 0; i < nrBalls; i++) {
const position = new SKYMATH.Vector3D(
(diameter - 15) * Math.cos(i * stepAngle), // x-position
(diameter - 15) * Math.sin(i * stepAngle), // y-position
0, // z-position
)
geometryGroup.addGeometry(ballModel, { position })
}

return geometryGroup
}
note

The ball model already includes a convenient Z offset which simplifies placement. This is not always the case and shows how well oriented static assets can make it easier to model in DynaMaker.

5. Create Component With Properties

Now that the geometry depends completely on the single variable diameter, create a component with this geometry.

  1. Click Component using getBearingGeometry as source.
  2. Observe the code section automatically updated with a class GeometryGroup.

  1. Rename the class to Bearing.
  2. Rename the properties interface to BearingProperties.
  3. Final code:
export interface BearingProperties {
diameter: number
}

export class Bearing extends STUDIO.BaseComponent<BearingProperties> {
constructor() {
super()
this.properties = {
diameter: 100, // starting value
}
}
generateGeometry() {
const { diameter } = this.properties
const geometryGroup = new SKYCAD.GeometryGroup()
const model = GEOM3D.getBearingGeometry(diameter)
geometryGroup.addGeometry(model)
return geometryGroup
}
}

tip

Properties define the component and drive its geometry. This is why nrBalls remains inside the geometry logic instead of being a property. In this case diameter determines it. A useful rule of thumb: ask: Will the user be able to change a value (for example color) in a configurator? If yes, make it a property. Otherwise keep it as a constant in the implementation.

Doublecheck the solution code here:
COMPONENTS
export interface BearingProperties {
diameter: number
}
export class Bearing extends STUDIO.BaseComponent<BearingProperties> {
constructor() {
super()
this.properties = {
diameter: 100,
}
}
generateGeometry() {
const { diameter } = this.properties
const geometryGroup = new SKYCAD.GeometryGroup()
const model = GEOM3D.getBearingGeometry(diameter)
geometryGroup.addGeometry(model, {})
return geometryGroup
}
}
GEOM3D
export function getBallModel() {
const model = ASSETS.STATIC_MODELS.BALL
return model
}

export function getRingsModel(diameter: number) {
const model = new SKYCAD.ParametricModel()

const sketch = GEOM2D.getRingProfileSketch(diameter)
const plane = new SKYCAD.Plane(0, -1, 0)

model.addRevolve(sketch, plane)

return model
}

export function getBearingGeometry(diameter: number) {
const geometryGroup = new SKYCAD.GeometryGroup()

const ringsModel = getRingsModel(diameter)
const ringMaterials = [new SKYCAD.Material({ color: 0x777777, metalness: 1, roughness: 0.3 })]
geometryGroup.addGeometry(ringsModel, { materials: ringMaterials })

const ballModel = getBallModel()
const nrBalls = Math.round(diameter / 8)
const stepAngle = (2 * Math.PI) / nrBalls // [radians]
for (let i = 0; i < nrBalls; i++) {
const position = new SKYMATH.Vector3D(
(diameter - 15) * Math.cos(i * stepAngle), // x-position
(diameter - 15) * Math.sin(i * stepAngle), // y-position
0, // z-position
)
geometryGroup.addGeometry(ballModel, { position })
}

return geometryGroup
}
GEOM2D
export function getRingProfileSketch(diameter: number) {
const sketch = ASSETS.STATIC_SKETCHES.RING_PROFILE

const outerSketch = sketch.clone()
outerSketch.mirrorInYaxis()
outerSketch.translate(30, 0)
sketch.mergeSketch(outerSketch)

const innerDiameter = diameter - 30
sketch.translate(innerDiameter, 0)

return sketch
}

You now know how to create components. Continue with the next tutorial to build an assembly with multiple components: Assemblies.