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
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
- Download the 3D static asset BALL.glb.
- Download the 2D static asset RING_PROFILE.dxf.
- In your app dashboard upload both files and create static sketches/models by checking the checkboxes in the popup.
- Confirm both assets appear in the dashboard:
- RING_PROFILE under Static Sketches.
- BALL under Static Models.
Import the assets so you can use them:
- Open the BEARING component.
- At the top click ⚙️ > Edit Imports, check ASSETS, then Apply changes.
3. Create models
A. Ball
Create the ball model using a snippet.
- Click Model and create an Extrude from any shape.
- Rename the function to
getBallModel()
. - Replace the model body with the code below. Keep
return model
and remove unused generated code. - 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.
- Click Sketch and create one from any shape.
- Rename the function to
getRingProfileSketch
. - Add the input parameter:
export function getRingProfileSketch(diameter: number) { ... }
. - Replace the sketch assignment with
const sketch = ASSETS.STATIC_SKETCHES.RING_PROFILE
. - 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
} - Create the outer ring by cloning and modifying the sketch before the return statement:
- Clone:
const outerSketch = sketch.clone()
(safe copy so the static asset is not modified). - Mirror in Y:
outerSketch.mirrorInYaxis()
. - Translate to leave space for the ball:
outerSketch.translate(30, 0)
. - Merge:
sketch.mergeSketch(outerSketch)
.
- Clone:
- 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
} - Adjust for the inner diameter before the return statement:
- Add:
const innerDiameter = diameter - 30
. - Translate in x to account for
innerDiameter
and ball diameter:sketch.translate(innerDiameter, 0)
.
- Add:
- 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:
- Click Model and choose Revolve using
getRingProfileSketch
. - Rename the function to
getRingsModel
.
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
.
- In GEOM3D, next to
getBallModel
andgetRingsModel
, create a third function namedgetBearingGeometry
. - Start it with an empty group:
const geometryGroup = new SKYCAD.GeometryGroup
and return it. - Add the
diameter
input:export function getBearingGeometry(diameter: number) { ... }
.export function getBearingGeometry(diameter: number) {
const geometryGroup = new SKYCAD.GeometryGroup()
return geometryGroup
} - Add the rings model before the return:
const ringsModel = getRingsModel(diameter)
geometryGroup.addGeometry(ringsModel) - Add a loop that calculates
nrBalls
fromdiameter
and positions each ball. You can write your own or use the code below which uses the built-inMath
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 })
} - 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
}
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.
- Click Component using
getBearingGeometry
as source. - Observe the code section automatically updated with a
class GeometryGroup
.
- Rename the class to Bearing.
- Rename the properties interface to BearingProperties.
- 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
}
}
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:
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
}
}
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
}
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.