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 models
  3. Assemble models
  4. Create part

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 models

A. Ball

  1. Download the 3D static asset BALL.glb.
  2. Click Model > From file... > Choose file BALL.glb, then Create.
  3. Rename function to getBallModel.

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.

B1. Sketch

  1. Download the 2D static asset RING_PROFILE.dxf.

  2. Click Sketch > Sketch > From file... > Choose file RING_PROFILE.dxf, then Create.

  3. Rename the function to getRingProfileSketch, so like you see the inner ring:

  4. Create the outer ring by cloning and modifying the sketch before the final return line:

    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).
  5. After these steps both rings are in place, so the function should look like:

    export function getRingProfileSketch() {
    const sketch = ASSETS.STATIC_SKETCHES.RING_PROFILE

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

    return sketch
    }
  6. Add the input parameter like: export function getRingProfileSketch(diameter: number) { ... }.

  7. Adjust for the inner diameter before the final return line:

    1. Add: const innerDiameter = diameter - 30.
    2. Translate in x to account for innerDiameter and ball diameter: sketch.translate(innerDiameter, 0).
  8. 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
    }

B2. Model

  1. Click Revolve > Sketch > getRingProfileSketch, then Create.
  2. Click Model and choose Revolve using getRingProfileSketch.
  3. Rename the function to getRingsModel.

note

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

3. Assemble models

Now assemble both models. The ball model will be 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 line:
    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) // highlight-next-line
const ringMaterials = [new SKYCAD.Material({ color: 0x777777, metalness: 1, roughness: 0.3 })]
geometryGroup.addGeometry(ringsModel, { materials: ringMaterials })

  1. 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° in radians 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.

4. Create Part

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

  1. Click Component > Part > getBearingGeometry
  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.