DEV Community

MxCAD
MxCAD

Posted on

Web CAD SDK to draw regular polygonal shapes

preamble
Drawing polygons is one of the common tasks in CAD (Computer Aided Design), and MxCAD is an online CAD-focused front-end library that provides a rich set of drawing and design features that make drawing polygons easy and flexible. In this article, we will walk you through the process of drawing polygons using MxCAD, giving you an in-depth understanding of its basic concepts and features.

mxcad is a TypeScript-based front-end library designed for CAD developers. It provides a rich set of APIs and features for creating, editing and displaying CAD drawings. Various drawing tasks can be realized by importing various modules.
Let's take drawing regular polygons as an example of how to use mxcad to draw polygons. Import the following code snippet into the modules that mxcad and mxdraw will use in this article.

import { DynamicInputType, MrxDbgUiPrInt, MrxDbgUiPrPoint, MxCursorType, MxFun } from "mxdraw" ;
import { MxCpp, McCmColor, McDbCircle, McDbPolyline, McGePoint3d, MxCADUiPrInt, MxCADUiPrPoint } from "mxcad" ;
Enter fullscreen mode Exit fullscreen mode

DynamicInputType, MrxDbgUiPrInt, etc. are function modules provided by MxCAD, while McDbCircle, McDbPolyline are objects that represent CAD graphics.
If you don't understand the API usage examples exported in the text you can find them in the mxcadAPI documentation or the mxdrawAPI documentation
Understanding each step of the computation of the algorithm for generating regular polygons is very important for drawing regular polygons. Here is a detailed explanation of the computeRegularPolygonVertices function:

/**
 * :: Generate vertex coordinates of regular polygons
 * @param {McGePoint3d} centerPoint - center point of polygon
 * @param {McGePoint3d} vertexPoint - polygon vertex
 * @param {number} sides - number of polygon sides (at least 3)
 * @returns {McGePoint3d[]} array of vertex coordinates of the polygon
 */
export function computeRegularPolygonVertices(centerPoint = new McGePoint3d(), vertexPoint = new McGePoint3d(), sides = 3): McGePoint3d[] {
    const verticesArray: McGePoint3d[] = [];
    sides = Math.max(3, sides);
    verticesArray.push(vertexPoint);

    // Calculate the angle increment for each vertex
    const angleIncrement = (Math.PI * 2) / sides.

    for (let i = 1; i < sides; i++) {
        // Calculate the cosine and sine on the angle corresponding to the current vertex
        const cosValue = Math.cos(angleIncrement * i),
            sinValue = Math.sin(angleIncrement * i);

        // Duplicate the center and vertices so as not to modify the values of the original points
        const startPt = centerPoint.clone();
        const endPt = vertexPoint.clone();

        // Calculate the offset with respect to the center point
        const deltaX = endPt.x - startPt.x;
        const deltaY = endPt.y - startPt.y;

        // Calculate the new vertex coordinates from the rotation formula
        const newX = deltaX * cosValue - deltaY * sinValue + startPt.x;
        const newY = deltaX * sinValue + deltaY * cosValue + startPt.y;

        // Create a new vertex object and add it to the array.
        const point = new McGePoint3d(newX, newY);
        verticesArray.push(point);
    }
   return verticesArray;
}

Enter fullscreen mode Exit fullscreen mode

Calculation of polygons
Below is a detailed explanation of each step of the algorithm's calculations:

  1. Initialization parameters: First, the function initializes an empty array verticesArray to store the coordinates of the vertices of the polygon. At the same time, make sure that the polygon's sides are at least 3. If the user inputs a number of sides less than 3, set the number of sides to 3.
const verticesArray: McGePoint3d[] = [];
sides = Math.max(3, sides);
verticesArray.push(vertexPoint);
Enter fullscreen mode Exit fullscreen mode
  1. Calculate the angle increment: Calculate the angle increment between each vertex by dividing the complete circumferential angle (2π) by the number of sides of the polygon.
const angleIncrement = (Math.PI * 2) / sides.
Enter fullscreen mode Exit fullscreen mode
  1. Calculate the vertex coordinates: Calculate the offset of each vertex with respect to the start point using the cosine and sine values. Here the rotation formula is used to calculate the new vertex coordinates by rotating the coordinate system.
const cosValue = Math.cos(angleIncrement * i),
sinValue = Math.sin(angleIncrement * i);
Enter fullscreen mode Exit fullscreen mode
  1. Duplicate Center and Vertex: To prevent modification of the values of the original points, duplicates of the center and vertex are created.
const startPt = centerPoint.clone();
const endPt = vertexPoint.clone();
Enter fullscreen mode Exit fullscreen mode
  1. Calculate Offset: Calculate the offset with respect to the center point, i.e. the position of the vertex with respect to the center point.
const deltaX = endPt.x - startPt.x;
const deltaY = endPt.y - startPt.y;
Enter fullscreen mode Exit fullscreen mode
  1. Rotate to calculate new coordinates: Use the rotation formula to calculate new vertex coordinates and add them to the vertex array.
const newX = deltaX * cosValue - deltaY * sinValue + startPt.x;
const newY = deltaX * sinValue + deltaY * cosValue + startPt.y;
const point = new McGePoint3d(newX, newY);
verticesArray.push(point);
Enter fullscreen mode Exit fullscreen mode
  1. Returns the result: Finally, returns an array of the coordinates of the vertices of the computed polygon.
return verticesArray;
Enter fullscreen mode Exit fullscreen mode

With this algorithm, we can draw regular polygons in CAD, not just simple vertices in a Cartesian coordinate system. This makes the drawing of polygons more flexible and adaptable.
Correspondingly, as we can see from the comments, they compute the vertex coordinates of the entire polygon from the polygon center and polygon vertices.

Other calculation methods
Then in AutoCAD, there are other ways to draw orthogonal polygons, then we will implement these algorithms one by one.
Here is a detailed explanation of the computePolygonVerticesFromEdge function:

/**
 * :: Calculate the coordinates of polygon vertices (based on edges)
 * @param {McGePoint3d} startPoint - the start point of the polygon edge
 * @param {McGePoint3d} endPoint - The end point of the polygon edge.
 * @param {number} sides - number of polygon sides (at least 3)
 * @returns {McGePoint3d[]} array of vertex coordinates of the polygon
 */
export function computePolygonVerticesFromEdge(startPoint: McGePoint3d, endPoint: McGePoint3d, sides: number): McGePoint3d[] {
    // Calculate the length and angle of an edge
    let dx = endPoint.x - startPoint.x;
    let dy = endPoint.y - startPoint.y;
    let length = Math.sqrt(dx * dx + dy * dy);
    let angle = Math.atan2(dy, dx);

    // Calculate the angle increment for each vertex
    let angleIncrement = (2 * Math.PI) / Math.max(3, sides);

    let polygonVertices = [startPoint, endPoint];

    for (let i = 0; i < sides; i++) {
        // Calculate the coordinates of the current vertex
        let x = startPoint.x + length * Math.cos(angle + i * angleIncrement);
        let y = startPoint.y + length * Math.sin(angle + i * angleIncrement);

        // Update the starting point and add it to the array
        startPoint = new McGePoint3d(x, y);
        polygonVertices.push(startPoint);
    }

    return polygonVertices.
}
Enter fullscreen mode Exit fullscreen mode

A detailed explanation of each step of the algorithm's calculations is given below:

  1. Calculate the length and angle of an edge: First, calculate the length and angle of a given edge. This is done by calculating the transverse and longitudinal differences between the start and end points, then calculating the length using the collinearity theorem, and finally calculating the angle using the inverse tangent function.
let dx = endPoint.x - startPoint.x;
let dy = endPoint.y - startPoint.y;
let length = Math.sqrt(dx * dx + dy * dy);
let angle = Math.atan2(dy, dx);
Enter fullscreen mode Exit fullscreen mode
  1. Calculate the angle increment for each vertex: In order to evenly distribute the vertices of the polygon, calculate the angle increment between each vertex.
let angleIncrement = (2 * Math.PI) / Math.max(3, sides);
Enter fullscreen mode Exit fullscreen mode
  1. Initialize the vertex array: Create an array containing the start and end points, this is to ensure that the polygon is closed.
let polygonVertices = [startPoint, endPoint];
Enter fullscreen mode Exit fullscreen mode
  1. Calculate vertex coordinates: Cycle through the coordinates of each vertex. The coordinates of each vertex relative to the start point are computed by a given angular increment using the transformation of the polar coordinate system.
for (let i = 0; i < sides; i++) {
    let x = startPoint.x + length * Math.cos(angle + i * angleIncrement);
    let y = startPoint.y + length * Math.sin(angle + i * angleIncrement);
    startPoint = new McGePoint3d(x, y);
    polygonVertices.push(startPoint);
}
Enter fullscreen mode Exit fullscreen mode
  1. Returns the result: Finally, returns an array of the coordinates of the vertices of the computed polygon.
return polygonVertices.
Enter fullscreen mode Exit fullscreen mode

With this algorithm, we can draw polygons based on the given start and end points.
Here is a detailed explanation of the computePolygonVerticesFromMidpoint function:

/**
 * :: Calculate polygon vertex coordinates (based on midpoints)
 * @param {McGePoint3d} centerPoint - center point of polygon
 * @param {McGePoint3d} edgeMidPoint - the midpoint of an edge of the polygon
 * @param {number} sides - number of polygon sides (at least 3)
 * @returns {McGePoint3d[]} array of vertex coordinates of the polygon
 */
function computePolygonVerticesFromMidpoint(centerPoint = new McGePoint3d(), edgeMidPoint = new McGePoint3d(), sides = 3): McGePoint3d[] {
    const midX = edgeMidPoint.x;
    const midY = edgeMidPoint.y;
    const centerX = centerPoint.x;
    const centerY = centerPoint.y;
    const numberOfSides = Math.max(3, sides);

    // Calculate the distance from the midpoint to the center of the polygon
    const distanceToCenter = Math.sqrt((midX - centerX) ** 2 + (midY - centerY) ** 2);

    // Calculate the radius from the midpoint to the center of the polygon
    const radius = distanceToCenter / Math.cos(Math.PI / numberOfSides);

    // Calculate the starting angle
    const startAngle = Math.atan2(midY - centerY, midX - centerX) - Math.PI / numberOfSides;

    const vertices = [];

    for (let i = 0; i < numberOfSides; i++) {
        // Calculate the angle of the current vertex
        const angle = startAngle + (i * 2 * Math.PI / numberOfSides);

        // Conversion to coordinates in the Cartesian coordinate system from the polar coordinate system
        const x = centerX + radius * Math.cos(angle);
        const y = centerY + radius * Math.sin(angle);

        // Create a new vertex object and add it to the array.
        vertices.push(new McGePoint3d(x, y));
    }

    return vertices.
}
Enter fullscreen mode Exit fullscreen mode

A detailed explanation of each step of the algorithm's calculations is given below:

  1. Get the coordinates of the midpoint and center point: First, get the coordinates of the midpoint of the given edge (edgeMidPoint) and the center point of the polygon (centerPoint).
const midX = edgeMidPoint.x;
const midY = edgeMidPoint.y;
const centerX = centerPoint.x;
const centerY = centerPoint.y;
Enter fullscreen mode Exit fullscreen mode
  1. Calculate the distance from the midpoint to the center and the radius: Use the Pythagorean Theorem to calculate the distance from the midpoint to the center and then calculate the radius of the polygon.
const distanceToCenter = Math.sqrt((midX - centerX) ** 2 + (midY - centerY) ** 2);
const radius = distanceToCenter / Math.cos(Math.PI / numberOfSides);
Enter fullscreen mode Exit fullscreen mode
  1. Calculate the starting angle: Calculate the direction angle from the midpoint to the center using the inverse tangent function and subtract half of the angle increment to ensure an even distribution of polygon vertices.
const startAngle = Math.atan2(midY - centerY, midX - centerX) - Math.PI / numberOfSides;
Enter fullscreen mode Exit fullscreen mode
  1. Calculate vertex coordinates: Cycle through the coordinates of each vertex. Convert angles in polar coordinate system to coordinates in right angle coordinate system by polar coordinate system conversion.
for (let i = 0; i < numberOfSides; i++) {
    const angle = startAngle + (i * 2 * Math.PI / numberOfSides);
    const x = centerX + radius * Math.cos(angle);
    const y = centerY + radius * Math.sin(angle);
    vertices.push(new McGePoint3d(x, y));
}
Enter fullscreen mode Exit fullscreen mode
  1. Returns the result: Finally, returns an array of the coordinates of the vertices of the computed polygon.
return vertices.
Enter fullscreen mode Exit fullscreen mode

With this algorithm, we can draw polygons based on the midpoints of the given edges and the centroid of the polygon.

Interactive drawing process
Above we have introduced these three algorithms, have been autoCAD to draw orthogonal polygon algorithm are simulated, then the next step is to simulate its interactive drawing process, the code is as follows.

/**
 * :: Functions for drawing polygons
 */
export async function drawPolygon() {
    // Create a user input object to get the number of sides
    const getNum = new MxCADUiPrInt();
    getNum.setMessage("\n input side number <5>")

    // Get the number of sides entered by the user
    let sideNum = await getNum.go() as number;
    if (!sideNum) sideNum = 5;

    // Create a user input object that can be used to get the center point or edge of a polygon.
    const getPoint = new MxCADUiPrPoint();
    getPoint.setMessage("\n Specify the center point of the positive polytope");;
    getPoint.setKeyWords("[Side(E)]");

    // Set the cursor type
    getPoint.setCursorType(MxCursorType.kCross);

    // Get the center point or edge of the user input
    const centerPoint = await getPoint.go();

    if (!centerPoint) {
        // If the user selects an edge, enter the edge drawing process
        if (getPoint.isKeyWordPicked("e")) {
            // Get the first endpoint of the edge entered by the user
            getPoint.setMessage("\n the first endpoint of the specified edge");;
            getPoint.setKeyWords("");
            const startPoint = await getPoint.go();

            if (!startPoint) return;

            // Set the user draw callback function to draw polygons in real time.
            getPoint.setUserDraw((currentPoint, pWorldDraw) => {
                const pPolyline = new McDbPolyline();
                // Calculate polygon vertices
                const points = computePolygonVerticesFromEdge(startPoint, currentPoint, sideNum);

                // Add vertices to polygons
                points.forEach((points) => {
                    pPolyline.addVertexAt(point);
                }).

                // Set the polygon to closed
                pPolyline.isClosed = true;

                // Draw polygons in real time
                pWorldDraw.drawMcDbEntity(pPolyline);
            }).

            // Get the second endpoint of the edge entered by the user
            getPoint.setMessage("\n second endpoint of the specified edge");;
            await getPoint.go();

            // Draw polygons and clear draw retention
            getPoint.drawReserve();
        }
        return;
    }

    // Plotting flow after the user selects the center point
    getPoint.setMessage("\n input options");;
    getPoint.setKeyWords("[Inside Circle (I)/Tangent to Circle (C)]");;

    // Get whether the user selected an inside or outside tangent circle
    await getPoint.go();
    let isTangentToTheCircle = true;
    If(getPoint.isKeyWordPicked("i")) isTangentToTheCircle = false;

    // Set the user draw callback function to draw polygons in real time.
    getPoint.setUserDraw((currentPoint, pWorldDraw) => {
        // Get the current drawing color
        let drawColor = MxCpp.getCurrentMxCAD().getCurrentDatabaseDrawColor();

        // Create polygon objects
        const pPolyline = new McDbPolyline();
        pPolyline.trueColor = new McCmColor(drawColor.r, drawColor.g, drawColor.b);

        // Calculate polygon vertices
        const points = isTangentToTheCircle ? computePolygonVerticesFromMidpoint(centerPoint, currentPoint, sideNum) : computeRegularPolygonVertices(centerPoint, currentPoint, sideNum); ? sideNum);

        // Add vertices to polygons
        points.forEach((pt) => {
            pPolyline.addVertexAt(pt);
        }).

        // Set the polygon to closed
        pPolyline.isClosed = true;

        // Draw polygons in real time
        pWorldDraw.drawMcDbEntity(pPolyline);
    }).

    // Get the radius of the circle entered by the user
    getPoint.setMessage("\n the radius of the specified circle");;
    await getPoint.go();

    // Draw polygons and clear draw retention
    getPoint.drawReserve();
}

// Register the function for drawing polygons as an MxCAD command.
MxFun.addCommand("Mx_Polygon", drawPolygon);

Enter fullscreen mode Exit fullscreen mode
  1. User input First, we need to get some information from the user, including the number of sides of the polygon and the location of the center point or edge. To accomplish this, we use the MxCADUiPrInt and MxCADUiPrPoint classes.
const getNum = new MxCADUiPrInt();
getNum.setMessage("\n input side number <5>")
let sideNum = await getNum.go() as number;
if (!sideNum) sideNum = 5;
const getPoint = new MxCADUiPrPoint();
getPoint.setMessage("\n Specify the center point of the positive polytope");;
getPoint.setKeyWords("[Side(E)]");
Enter fullscreen mode Exit fullscreen mode

Here, we set up a message prompting the user to enter the number of sides of the polygon. If the user doesn't enter one, the default is 5. We then create an object for getting the points and set a few parameters, including possible keywords for the user (in this case, a flag for choosing sides).

  1. Select the center point or edge With getPoint.go(), we wait for the user to select either the center point or the edge. If the user selects an edge, we go into the process of drawing the edge, otherwise, we continue with the center point.
const centerPoint = await getPoint.go();
if (!centerPoint) {
    if (getPoint.isKeyWordPicked("e")) {
        // Drawing the flow on the side...
    }
    return;
}
Enter fullscreen mode Exit fullscreen mode

This step is the first interaction between the user and the program, and allows the user to choose whether to draw the polygon through the center point or to select an edge to start with. This increases the flexibility of the user and makes the tool more useful.

  1. Side drawing process If the user selects an edge, we first get the starting point of the edge and then set the user draw callback function. This callback function is used to draw the polygon in real time so that the user can see a preview of what the edge will look like as it is selected.
const startPoint = await getPoint.go();
if (!startPoint) return;

getPoint.setUserDraw((currentPoint, pWorldDraw) => {
    // Draw polygons in real time...
}).

await getPoint.go();
getPoint.drawReserve();
Enter fullscreen mode Exit fullscreen mode

In this step, we calculate and draw a preview effect of the polygon in real time using the starting point entered by the user. The user can see a dynamic polygon that updates as the mouse moves.

  1. Center point drawing process If the user selects a center point, we first get whether the user selected an inner or outer tangent circle. Then, we set up the user draw callback function to draw the polygon in real time and get the radius of the circle entered by the user.
getPoint.setMessage("\n input options");;
getPoint.setKeyWords("[Inside Circle(I)/Tangent to Circle(C)]");;
await getPoint.go();

getPoint.setUserDraw((currentPoint, pWorldDraw) => {
    // Draw polygons in real time...
}).

getPoint.setMessage("\n the radius of the specified circle");;
await getPoint.go();
getPoint.drawReserve();
Enter fullscreen mode Exit fullscreen mode

This step optionally specifies whether the polygon is internally attached to the circle or externally tangent to the circle, further adding to the functionality of the tool.

  1. Real-time polygon drawing After the user selects a center point or an edge, we calculate the vertices of the polygon in real time through the user draw callback function and draw a preview of the polygon in real time using the drawing tools provided by MxCAD.
getPoint.setUserDraw((currentPoint, pWorldDraw) => {
    // Get the current drawing color...
    let drawColor = MxCpp.getCurrentMxCAD().getCurrentDatabaseDrawColor();

    // Create polygon objects...
    const pPolyline = new McDbPolyline();
    pPolyline.trueColor = new McCmColor(drawColor.r, drawColor.g, drawColor.b);

    // Calculate polygon vertices...
    const points = isTangentToTheCircle ? computePolygonVerticesFromMidpoint(centerPoint, currentPoint, sideNum) : computeRegularPolygonVertices(centerPoint, currentPoint, sideNum); ? sideNum);

    // Add vertices to polygons...
    points.forEach((pt) => {
        pPolyline.addVertexAt(pt);
    }).

    // Set the polygon to be closed...
    pPolyline.isClosed = true;

    // Draw polygons in real time...
    pWorldDraw.drawMcDbEntity(pPolyline);
}).
Enter fullscreen mode Exit fullscreen mode

This step is the key to the entire process and shows how the user interacts with real-time drawing. The user can dynamically see the shape of the polygon by moving the mouse after selecting the center point or edge. This real-time feedback is an important factor in improving the user experience.

  1. Registered as MxCAD command Finally, we register the functions for drawing polygons as MxCAD commands so that they can be called by the user from the command line.
MxFun.addCommand("Mx_Polygon", drawPolygon);
Enter fullscreen mode Exit fullscreen mode

In this step-by-step series, we demonstrate how to use MxCAD to implement an interactive polygon drawing. This not only covers the processing of user input, but also shows how to combine the functionality provided by MxCAD for real-time drawing and user selection. By using mxcad, it enables the developer to focus on the business logic rather than the underlying graphics processing and interaction handling.

View the actual effect:https://demo.mxdraw3d.com:3000/mxcad/ Type Mx_Polygon at the command line of the page

Top comments (0)