This library handles everything related to 2D & 3D geometry, such as:

## Plane​

A plane can be used to place a sketch in the world or for different 3D operations.

### How To Create A Plane​

``const plane = SKYCAD.Plane(nX, nY, nZ, offset)``

Here, `nX` , `nY` and `nZ` are the coordinates of the normal vector `n` and `offset` the distance from the origin. #### Things To Consider​

• A plane is not visible in the world
• A plane has its own local coordinate system X'Y'Z'
• `n` does not need to be a unit vector
• `offset` handles negative values
• `θ` is defined from the xy-projection of the plane towards the negative y-axis

### Uses​

• 3D operations:
``model.addExtrude(sketch, plane, extrudeLength)``
• Projections 2D:
``const sketch = model.generateProjection(plane).visible``

### Trigonometry: Offset, Θ And Φ​

Being `n` the normal vector as `plane.n` :

``const offset = plane.offsetconst theta = SKYCAD.computeAngleTheta(n)const phi = SKYCAD.computePhiAngle(n)``

#### Examples​ ## Sketch​

2D Sketches is a very important part of parametric CAD. Below we have listed the basics.

### How To Create A Sketch​

#### By Moving The Marker​

``const myTriangleSketch = new SKYCAD.Sketch()myTriangleSketch.moveTo(0, 0)myTriangleSketch.lineTo(100, 0)myTriangleSketch.curveTo(100, 50, { clockwise: true })myTriangleSketch.lineToId(0)``

`lineToId(0)` ensures a closed path by going to the node `0` with a line. Useful to avoid possible bugs in 3D geometries. Same applies for `curveToId(0)`.

All curves are counter-clockwise defined by default (`clockwise: false`).

#### By Node Reference​

``const myTriangleSketch = new SKYCAD.Sketch()const n0 = myTriangleSketch.addNode(0, 0)const n1 = myTriangleSketch.addNode(100, 0)const n2 = myTriangleSketch.addNode(100, 50)myTriangleSketch.addLine(n0, n1)myTriangleSketch.addCurve(n1, n2)myTriangleSketch.addLine(n2, n0)``

### Geometric Transformations​

``sketch.translate(5, 5) // 2D translation as (X, Y)sketch.rotate(Math.PI / 4) // [rad], rotation around the origin (0, 0) of the sketchsketch.scale(10) // scales the skech from its center, negative values are also acceptedconst clonedSketch = sketch.clone() // creates a copy``

Rotation & translation are cumulative operations. If you write `sketch.translate(1, 3)` and after `sketch.translate(-5, 10)`, this will result in `sketch.translate(-4, 13)`. Same applies for rotation.

### Chamfer & Fillet​

``const sketch = new SKYCAD.Sketch()const n0 = sketch.addNode(0, 0)const n1 = sketch.addNode(100, 0)const n2 = sketch.addNode(50, 100)sketch.addLine(n0, n1)sketch.addLine(n1, n2)sketch.addLine(n2, n0)const size = 10sketch.addChamfer(size, n0)sketch.addFillet(size, n1)``

Fillets and chamfers are easiest when applied last

### Offset Sketch​

An offset sketch can be created from another by generating shapes. As an example:

``const shape = exampleSketch.generateShapes()const offsettedShape = SKYCAD.generateShapesWithOffset(shape, offset)const offsettedSketch = offsettedShape.generateSketch()``

### Sketch From Bounds​

``const sketch = SKYCAD.generateSketchFromBounds(bounds2D) // generates a rectangular sketch from SKYCAD.Bounds2D``

### Boolean Operations​

Boolean operations can be done between sketches. Create a new sketch as a result of a boolean operation as:

``const unionSketch = SKYCAD.generateSketchUnion(sketchA, sketchB)const subtractionSketch = SKYCAD.generateSketchSubtraction(sketchA, sketchB)const intersectionSketch = SKYCAD.generateSketchIntersection(sketchA, sketchB)``

#### Example​ If you want to merge two different sketches into a single one, regardless of their interaction, you should do `sketchA.mergeSketch(sketchB)` instead.

### isSketch​

``const isItemSketch = SKYCAD.isSketch(item) // returns true if item is a SKYCAD.Sketch``

## Layout​

Layouts are used to compose 2D content to be displayed in the application or in drawings.

#### Example​

A layout with a sketch, dimension and text element: ``const layout = new SKYCAD.Layout()const squareSketch = SKYCAD.generateRectangleSketch(0, 0, 300, 300)layout.addSketch(squareSketch)const startNode = new SKYMATH.Vector2D(0, 300)const endNode = new SKYMATH.Vector2D(300, 300)layout.addDimension(startNode, endNode, { decimals: 0,  offset: 40 })layout.addText('DynaMaker', { align: 'center', position: new SKYMATH.Vector2D(150, 150), size: 20 })``

### How To Create A Layout​

``const layout = new SKYCAD.Layout({  width?: number,   // default: 1000  height?: number,  // default: 1000  margin?: number,  // default: 0})``

When creating a layout you have the optional arguments to define its width, height and margin, which are useful when working with anchor positions.

### Content​

The following content types can be added to a layout:

#### Sketches​

``layout.addSketch(sketch)``
``layout.addSketch(sketch: SKYCAD.Sketch, args?: {  anchor?: string,              // default: ''  ignoreMargins?: boolean,      // default:  position?: SKYMATH.Vector2D,  // default:  rotation?: number,            // default: 0  scale?: number,               // default: 1}): void``

#### Dimensions​

``const p1 = new SKYMATH.Vector2D(0, 0)const p2 = new SKYMATH.Vector2D(1000, 0)layout.addDimension(p1, p2)``
``layout.addDimension(startPos: SKYMATH.Vector2D, endPos: SKYMATH.Vector2D, args?: {  decimals?: number,     // default:  offset?: number,       // default:  textSize?: number,     // default:  textRotation?: number, // default: 0}): void``

#### Text​

``layout.addText('DynaMaker')``
``layout.addText(text: string, args?: {  align?: string,               // default: 'left'  anchor?: string,              // default: ''  ignoreMargins?: boolean,      // default:  position?: SKYMATH.Vector2D,  // default:  rotation?: number,            // default: 0  size?: number,                // default: 1}): void``

#### Images​

``const img = document.createElement('img')img.src = '...' // url to imglayout.addImage(img)``
``layout.addImage(image: HTMLImageElement, args?: {  anchor?: string,              // default: ''  ignoreMargins?: boolean,      // default:  position?: SKYMATH.Vector2D,  // default:  rotation?: number,            // default: 0  scale?: number,               // default: 1}): void``

#### Layouts​

``layout.addLayout(otherLayout)``
``layout.addLayout(layout: SKYCAD.Layout, args?: {  anchor?: string,              // default: ''  ignoreMargins?: boolean,      // default:  position?: SKYMATH.Vector2D,  // default:  rotation?: number,            // default: 0}): void``

### Content Geometric Transformations​

``layout.rotateContent(Math.PI / 4) // [rad], rotation around the origin (0, 0) of the sketchlayout.scaleContent(4) // scales the content, negative values are also acceptedlayout.translateContent(5, -3) // 2D translation of content as (X,Y)const clonedLayout = layout.clone() // creates a copy``

### Anchor Positions​

Anchor positions allows easy positioning of elements within a layout without knowing the exact coordinates, taking into account the size of the elements and the margins of the layout. These are:

• `bottom-left`
• `bottom-right`
• `center`
• `top-left`
• `top-right`

#### Example​

A layout with five sketches (squares) placed with anchor positions. Code to generate the outer border and the squares in the picture above:

``const layout = new SKYCAD.Layout({ width: 600, height: 300, margin: 20 })// Draw the borderconst size = layout.getSize()const borderSketch = SKYCAD.generateRectangleSketch(0, 0, size.x, size.y)layout.addSketch(borderSketch)// Adding the squaresconst square = SKYCAD.generateRectangleSketch(0, 0, 50, 50)layout.addSketch(square, { anchor: 'bottom-left'})layout.addSketch(square, { anchor: 'bottom-right', ignoreMargins: true})layout.addSketch(square, { anchor: 'center'})layout.addSketch(square, { anchor: 'top-left'})layout.addSketch(square, { anchor: 'top-right' })``

Notice that on the `bottom-right` one we have set `ignoreMargins: true` to ignore the margin.

#### Custom Anchor Position​

Custom anchors can be added to a layout by defining a unique id and a position.

``layout.addAnchor(id: string, position: SKYMATH.Vector2D): void``

### Size​

``const size = layout.getSize() // bounds = { x: number, y: number }const height = layout.getHeight()const width = layout.getWidth()``

### Margins​

``const margins = getMargins () // margins = { top: number, bottom: number, left: number, right: number }setMargins ( { top: 5, bottom: 1, left: 0, right: 0 } )``

### isLayout​

``const isItemLayout = SKYCAD.isLayout(item) // returns true if item is a SKYCAD.Layout``

## Bounds​

We defined the bounds as an object containing the position of two corners.

### How To Create Bounds​

``const bounds2D = new SKYCAD.Bounds2D({  min: new SKYMATH.Vector2D(1, 3),  max: new SKYMATH.Vector2D(10, -7)})const bounds3D = new SKYCAD.Bounds3D({  min: new SKYMATH.Vector3D(1, 3, -5),  max: new SKYMATH.Vector3D(10, -7, 8)})``

### Get Bounds​

``const sketchBounds2D = sketch.getBounds()const layoutContentBounds2D = layout.getContentBounds()const modelBounds3D = model.getBounds()const geometryBounds3D = geometryGroup.getBounds()`` ### Geometric Transformations​

``const clonedBounds = bounds.clone() // creates a copybounds.translate(exampleVector) // translation as new SKYMATH.Vector2D(X, Y)bounds.offset(10) // extends the bounds 10 mm from the center``

### Other​

``const size = bounds.getSize() // gives the width (x) and height (y) as: { x: number, y: number }const centerPosition = bounds.getCenter() // gives the center position``
``const mergedBounds2D = SKYCAD.mergeBounds2D([bounds2D_A, bounds2D_B, ...]) // merges all 2D bounds into a single SKYCAD.Bounds2Dconst mergedBounds3D = SKYCAD.mergeBounds3D([bounds3D_A, bounds3D_B, ...]) // merges all 3 bounds into a single SKYCAD.Bounds3D``
``const mergedBounds2D = SKYCAD.mergeBounds2D([bounds2D_A, bounds2D_B, ...]) // merges all 2D bounds into a single SKYCAD.Bounds2Dconst mergedBounds3D = SKYCAD.mergeBounds3D([bounds3D_A, bounds3D_B, ...]) // merges all 3 bounds into a single SKYCAD.Bounds3D``

### Collision Detection Booleans​

``const isPositionInsideBounds = bounds.checkPositionInside(position)const isBoundsAInsideBoundsB = boundsA.isFullyInsideBounds(boundsB)const isOverlapping = boundsA.isOverlappingBounds(boundsB, { treatTangencyAsOverlap: true })`` For the second case of `isOverlappingBounds()` , the result depends on `treatTangencyAsOverlap` , which is set to `true` by default.

## Table​

Tables are great for displaying organized data, such as BOM-lists or headers for your drawings.
A table can contain text, images and sketches and any cell can span any number of rows and columns.

#### Example​ ``const table = new SKYCAD.Table({ defaultTextSize: 10, width: 400 })table.addText('1', 0, 1)table.addText('2', 0, 2)table.addText('3', 0, 3)table.addText('Row 1: Left', 1, 0, { align: 'left' })table.addText('Row 2: Center', 2, 0)table.addText('Row 3: Right', 3, 0, { align: 'right' })table.addText('1x2', 1, 1, { colspan: 2, label: 'Cell Label 1' })table.addText('2x2', 2, 1, { colspan: 2, rowspan: 2, label: 'Cell Label 2' })table.addSketch(SKYCAD.generateCircleSketch(5, 5, 10), 0, 0)const img = document.createElement('img')img.src = 'https://avatars3.githubusercontent.com/u/24880879'table.addImage(img, 1, 3, { rowspan: 3 })const tableLayout = table.generateLayout()``

Notice that the table is converted to a layout with `table.generateLayout()`, so it can be added later to other layouts or drawings.

### How To Create A Table​

Create a table as:

``const table = new SKYCAD.Table({  defaultTextSize: number, // default: 10  width: number,           // default: 512})``

A table can be converted into a sketch by using `const layout = table.generateLayout()`.

The cell size is defined dynamically with its content. The rows and columns of a table will always stretch to fill the full width of the table.

### Content​

Content is added into a specific row and column. Supported content types are text, images and sketches.

Table coordinates are 0 indexed. `(0, 1)` corresponds to first row and second column

#### Text​

``// adding 'some text' to the second cell on the first rowtable.addText('some text', 0, 0)``
``table.addText(text: string, row: number, column: number, {  label?: string,   // default: ''  colspan?: number, // default: 1, nr of columns to cover  rowspan?: number, // default: 1, nr of rows to cover  align?: string,   // default: 'left', ['left', 'center', 'right']}): void``

#### Sketches​

``  // adding a sketch to the second cell on the first row  table.addSketch(sketch, 0, 0)``
``table.addSketch(sketch: SKYCAD.Sketch, row: number, column: number, {  label?: string,   // default: ''  colspan?: number, // default: 1, nr of columns to cover  rowspan?: number, // default: 1, nr of rows to cover}): void``

#### Images​

``const img = document.createElement('img')img.src = 'https://avatars3.githubusercontent.com/u/24880879?s=200&v=4'table.addImage(image: HTMLImageElement, 0, 0)``
``table.addImage(img: string, row: number, column: number, {  label?: string,   // default: ''  colspan?: number, // default: 1, nr of columns to cover  rowspan?: number, // default: 1, nr of rows to cover}): void``

### isTable​

``const isItemTable = SKYCAD.isTable(item) // returns true if item is a SKYCAD.Table``

## Parametric Model​

A parametric model is a pure 3D representation, without any information about its material or position. Create a model as:

``const model = new SKYCAD.ParametricModel()``

In DynaMaker there are 6 CAD features available

#### Extrusion​

``model.addExtrude(sketch, refPlane, extrusionLength)``

#### Cut​

``model.addExtrudeCut(sketch, refPlane, extrusionCutLength)``

#### Revolve​

``model.addRevolve(sketch, refPlane, {  axisDirection: new SKYMATH.Vector2D(0, -1),  revolveAngle: 235 * Math.PI / 180})``

The following examples use the same sketch and reference plane:

``const model = new SKYCAD.ParametricModel()const sketch = SKYCAD.generateLogo()const refPlane = new SKYCAD.Plane(1, 0, 0, 0)`` #### Union​

``cubeModel.union(sphereModel) // adds sphere to cube, modifying cubeModel``

#### Subtract​

``cubeModel.subtract(sphereModel) // removes sphere from cube, modifying cubeModel``

#### Split​

``const model = new SKYCAD.ParametricModel()const sketch = SKYCAD.generateLogo()const extrusionPlane = new SKYCAD.Plane(1, 0, 0, 0)model.addExtrude(sketch, extrusionPlane, 100)const splitPlane = new SKYCAD.Plane(1.5, 0, -1, 25)model.addSplit(splitPlane, { keepInside: false, keepOutside: true })`` Inside and Outside is defined according to the direction of the normal of `splitPlane`.

## Mesh Model​

A mesh model is a collection of vertices, edges and faces that defines the shape of a polyhedral object of an imported file (e.g. stl).

Like a parametric model it lacks the information about its material or position. However, a mesh model also lacks bounds when imported. You can add them when creating the mesh model if you know them or use an open source stl viewer to retrieve them automatically.

``const meshModel = new SKYCAD.MeshModel(ASSETS.URLS.MY_STL, {  bounds: new SKYCAD.Bounds3D(    new SKYMATH.Vector3D(0, 0, 0),    new SKYMATH.Vector3D(50, 20, 100),  )})``

Having bounds in a mesh model is useful when updating the camera, for collision detection with other existing models, selection handling and more.

#### Example - Bike Wheel​

Here is a complete example that uses mesh models, together with materials and a container for the models called geometry group.

``// Rim component geometrygenerateGeometry() {  const geometryGroup = new SKYCAD.GeometryGroup()  const model = new SKYCAD.MeshModel(ASSESTS.URLS.RIM_STL)  geometryGroup.addGeometry(model, { materials: [new SKYCAD.Material({ color: 0x333333 })] })  return geometryGroup}``
``// Tyre component geometrygenerateGeometry() {  const geometryGroup = new SKYCAD.GeometryGroup()  const model = new SKYCAD.MeshModel(ASSESTS.URLS.TYRE_STL)  geometryGroup.addGeometry(model, { materials: [new SKYCAD.Material({ color: 0xfbfbf0 })] })  return geometryGroup}``
``// Wheel componentexport class WheelComponent {  private componentHandler = new STUDIO.ComponentHandler()  constructor() {    this.componentHandler = new STUDIO.ComponentHandler()    this.componentHandler.add(new RIM.Component())    this.componentHandler.add(new TYRE.Component())  }  generateGeometry() {    const geometryGroup = this.componentHandler.generateAllGeometry()    return geometryGroup  }}``

Instead of creating a mesh model like `const rimModel = new SKYCAD.MeshModel(ASSESTS.URLS.RIM_STL)`, you can also create a static model directly like `const rimModel = ASSESTS.STATIC_MODELS.RIM_STL`. The difference between both is that a static model includes the 3D bounds (useful eg. for collision detection or camera centering), whereas a mesh model lacks this information and therefore must be defined in its definition as seen before. You can read more in this how-to example to see how to create static models efficiently.

## Connector​

A 3D connector is a point in the world with its own coordinate system. It can be defined by the position and rotation vectors.

``const myConnector = new SKYCAD.Connector3D({  position: new SKYMATH.Vector3D(50, 100, 0),  rotation: new SKYMATH.Vector3D(Math.PI / 2, Math.PI / 4, 0),})``

Connectors can be used in different ways. However, check their most common use in this how-to example with a robotic arm.

## Color​

In DynaMaker it is possible to work with different color systems:

Create a hexadecimal color by just adding `0x` before the 6 digits/letters that define the color.

``const hexColor = 0xFFFFFF``

If you try to `console.log()` white as `0xFFFFFF` and you will see that it automatically returns the decimal color `16777215`. Also, notice that `0xFFFFFF === 16777215` returns `true` so both definitions (hexadecimal and decimal) can be used interchangeably without any problem. See other examples here.

You can convert a hexadecimal color to RGB (and use other functions) with:

``const rgbColor = SKYCAD.parseColor(0xffffff) // returns SKYCAD.RgbColor``

### RGB​

Create a RGB color with values from `0` to `255`, with `alpha` to represent its opacity (from 0 to 1) as:

``const rgbColor = new SKYCAD.RgbColor(0, 125, 255, { alpha: 0.2 }) // light blue``
``const clonedRgbColor = rgbColor.clone() // creates a copyconst isSameRgbColor = rgbColorA.equals(rgbColorB) // returns true if rgbColorA is rgbColorB, ignoring alphaconst alpha = rgbColor.getAlpha() // gets the value of alpha``
``const rgbArray = rgbColor.toRgbArray() // returns list of values from 0 to 255 as [R, G, B]const cmykArray = rgbColor.toCmykArray() // returns list of values from 0 to 100 as [C, M, Y, K]const hexadecimalColor = rgbColor.toRgbNumber() // returns a hexadecimal colorconst hexadecimalColorString = rgbColor.toHexString() // returns a hexadecimal color as string with # before the 6 digits/letters (eg. '#FFFFFF')``
``const isItemRgbColor = SKYCAD.isRgbColor(item) // returns true if item is SKYCAD.RgbColor``

### CMYK​

Create a CMYK color with values from `0` to `100`, with `alpha` to represent its opacity (from 0 to 1) as:

``const cmykColor = new SKYCAD.CmykColor(100, 50, 0, 0, { alpha: 0.3 }) // light blue``
``const clonedCmykColor = cmykColor.clone() // creates a copyconst isSameCmykColor = cmykColorA.equals(cmykColorB) // returns true if cmykColorA is cmykColorB, ignoring alphaconst alpha = cmykColor.getAlpha() // gets the value of alpha``
``const cmykArray = cmykColor.toCmykArray() // returns list of values from 0 to 100 as [C, M, Y, K]const hexadecimalColor = cmykColor.toRgbNumber() // returns a hexadecimal colorconst hexadecimalColorString = cmykColor.toHexString() // returns a hexadecimal color as string with # before the 6 digits/letters (eg. '#FFFFFF')``
``const isItemCmykColor = SKYCAD.isCmykColor(item) // returns true if item is SKYCAD.CmykColor``

### RAL​

In DynaMaker RAL colors (used mainly in the industry for varnish, powder coating and plastics) are treated as numbers as well. Since it is a color system difficult to produce, RAL colors in websites are close representations of their true color.

The main use of RAL colors In DynaMaker is in the RAL color parameter, which always operates in RAL colors (ie. input and output as 4 digits). For example the RAL color `Yellow orange` can be represented as `2000`.

Any other purpose, like color in sketches, models, etc, needs to follow the other mentioned color systems.

## Material​

Materials are added to models when added to the geometry groups. Create a material, which needs the color in hexadecimal, as:

``const materials = [ new SKYCAD.Material({ color: 0x445464, opacity: 0.3 }) ]``

Textures can be also added together with the material. See the how-to example.

## Geometry Group​

Whenever you have multiple models that belong together, e.g. a door blade and its door handle, you can put them in the same GeometryGroup. You can then position, rotate and scale the group as one unit without having to fiddle with its internal structure.

``const doorGeometryGroup = new SKYCAD.GeometryGroup()doorGeometryGroup.addGeometry(doorBladeModel, {  position: new SKYMATH.Vector3D(-100, 100, 0),  rotation: new SKYMATH.Vector3D(Math.PI / 2, 0, 0),  materials: [ new SKYCAD.Material({ color: 0x445464, opacity: 0.3 }) ]  scale: 1})doorGeometryGroup.addGeometry(doorHandleModel, {  position: new SKYMATH.Vector3D(0, 100, 0),  materials: [ new SKYCAD.Material({ color: 0x25ADF3, opacity: 0 }) ]})``

Changing the position of `geometry` makes all its members follow the new position. This is ideal if you want to move subgroups of geometries. Eg. `doorGeometryGroup`, containing the door blade and handle models, could be within `houseGeometryGroup`.

### 2D Projections​

2D projections are created via geometry. Given a plane, create a projection as:

``const componentGeometry = component.generateGeometry()const projection = componentGeometry.generateProjection(refPlane)// projection: { visible: SKYCAD.Sketch(), hidden: SKYCAD.Sketch() }``

It returns an object with two items:

• `visible` : a sketch with all the visible lines from that plane.
• `hidden` : a sketch with all the hidden lines from that plane.

Notice that a geometry is required. If you have a lot of models in your geometry, it might be worth considering to create a sketch for each one to significantly improve performance. This is especially relevant if you are generating or updating the projection dynamically with a parameter or similar.

### Tags​

Track the geometry group easier by using tags.

``geometryGroup.addTag('door') // tags the geometry with the string 'door'const hasDoorTag = geometryGroup.hasTag('door') // returns true if geometryGroup has the given tag 'door'const tags = geometryGroup.getTags() // returns a list of the tags as strings``

### Geometric Transformations​

``const scale = geometryGroup.getScale() // gets scale as numberconst position = geometryGroup.getPosition() // gets position as SKYMATH.Vector3Dconst rotation = geometryGroup.getRotation() // gets rotation as SKYMATH.Vector3DgeometryGroup.setPosition(new SKYMATH.Vector3D(1, 10, -7))geometryGroup.setRotation(new SKYMATH.Vector3D(0, 0, Math.PI / 2))geometryGroup.setScale(0.7)``

### Other​

``const clonedGeometryGroup = geometryGroup.clone() // creates a copyconst isGeometryEmpty = geometryGroup.isEmpty() // returns true if no models/geometry was added with addGeometry()const geometries = geometryGroup.getGeometry() // returns a list of geometries and models added``