SVG Vector Graphics

12/2/2022

MxDbShape represents the base class for a shape, which is implemented based on THREE.Shape.

By default, MxDbShape supports shape curve closure, fill, image fill, dashed line, solid line, and line width settings. This class is implemented based on Mx.MxDbEntity (opens new window).

Various graphic effects can be achieved by extending the MxDbShape class.

Click Mx.MxDbShape API (opens new window) to view detailed property and method descriptions.

# Implementation Process of MxDbShape Extension

Inherit MxDbShape => Extend _propertyDbKeys attribute array => Override worldDraw method.

  1. Extend _propertyDbKeys attribute array:
class MxDbPolygonShape extends MxDbShape {
    points  = []
    constructor() {
        super()
        this._propertyDbKeys = [...this._propertyDbKeys, 'points']
    }
    ...
}

The _propertyDbKeys attribute records the names of the data to be retained, as in the example, the 'points' attribute.

During the interactive (command) dynamic drawing process, the points array will be reinitialized to an empty array.

If points data needs to be saved in real time when the graphic needs to implement functions such as archiving and restoration, dynamic drawing, and selection and drag to joint points to change the graphic, avoiding being initialized.

Here, points represent the points that make up the polygon.

  1. Override worldDraw method.

worldDraw is the rendering function, and the default implementation is as follows:

worldDraw() {
    // THREE is mounted on the window object by default, and needs to be called
    // loadCoreCode function to mount first
    const THREE:THREE =  Mx.MxFun.getMxFunTHREE()
    // Create a shape path
    const paths = this.createPaths(new THREE.Curve<THREE.Vector3>())
    // Get the points that make up the shape through the shape path
    const points = this.getShapePoints(paths)
    // Draw the shape
    this._draw(pWorldDraw, points)
    // Draw the outline of the shape
    this._drawStoreLine(pWorldDraw, points)
}

We can implement various shapes based on the THREE.Curve and its derived classes and create a shape path through the createPaths method.

Get the points that make up the shape with getShapePoints, and finally draw the shape and outline with _draw and _drawStoreLine.

Of course, we can also not use the shape path provided by classes such as THREE.Curve but directly calculate the points that make up the shape through some algorithms and draw them through _draw and _drawStoreLine.

Here, points refer to a point represented by a THREE.Vector3 vector.

There are also some methods for displaying grip points getGripPoints and moving grip points moveGripPointsAt, refer to Mx.MxDbEntity custom graphic object for rewriting.

# Arrow Shape MxDbArrow


const worldDraw = new Mx.McEdGetPointWorldDrawObject()
const lines = new MxDbArrow()
const mxDraw = MxFun.getCurrentDraw();
const point = new Mx.MrxDbgUiPrPoint();
point.setUserDraw(worldDraw)
lines.setLineWidth(10)
lines.innerOffset = getScreenPixel(10)
lines.outerOffset = getScreenPixel(22)
lines.topOffset = getScreenPixel(36)
point.go(() => {
    lines.startPoint = point.value()
    worldDraw.setDraw((v) => {
        lines.endPoint = v
        worldDraw.drawCustomEntity(lines)
    })
    point.go(async (status) => {
        lines.endPoint = point.value()

        mxDraw.addMxEntity(lines)

        /** Property adjustment panel UI */ 
        addShapeGui(lines, [{
            name: "topOffset",
            label: "topOffset the top offset of the arrow",
            box: {
                min: 0,
                max: 10000
            }
        },
        {
            name: "innerOffset",
            label: "innerOffset the inner offset of the arrow",
            box: {
                min: 0,
                max: 10000
            }
        },
        {
            name: "outerOffset",
            label: "outerOffset the outer offset of the arrow",
            box: {
                min: 0,
                max: 10000
            }
        },
        {
            name: "isSharpCorner",
            label: "isSharpCorner whether it is a bottom pointed arrow"
        }
        ])
    })
})

View the complete source code for this example: github (opens new window) | gitee (opens new window)

Effect:

# Drawing Arc Shape MxDbArcShapeDraw

const getPoint = new MrxDbgUiPrPoint()
const mxobj = MxFun.getCurrentDraw();
const worldDraw = new McEdGetPointWorldDrawObject()
let arc = new MxDbArcShapeDraw()
getPoint.setMessage("\nDetermine the midpoint of the arc:");
getPoint.setUserDraw(worldDraw)
getPoint.go(async () => {
    getPoint.setMessage("\nDetermine the starting point of the arc:");

    arc.center = getPoint.value()
    worldDraw.setDraw((v)=> {
        arc.interRadiusPoint = v
        worldDraw.drawCircle(arc.center, v.distanceTo(arc.center))
    })
    arc.interRadiusPoint = await getPoint.go() || new THREE.Vector3()
    worldDraw.setDraw((v)=> {
        arc.outerRadiusPoint = v
        worldDraw.drawCustomEntity(arc)
    })
    arc.outerRadiusPoint = await getPoint.go() || new THREE.Vector3()
    mxobj.addMxEntity(arc)
    addEllipseShapeGui(arc)

})

View the complete source code for this example: github (opens new window) | gitee (opens new window)

Effect:

# Arc Shape MxDbCircleArc

const getPoint = new MrxDbgUiPrPoint()
const mxObj = MxFun.getCurrentDraw();
const draw = new McEdGetPointWorldDrawObject()
let obj = new MxDbCircleArc()
obj.stroke = "#ff0000"
getPoint.setUserDraw(draw)
getPoint.setMessage("\nDetermine the midpoint of the arc:");
getPoint.go(async () => {

    // Determine the center of the arc with the first point
    obj.center = getPoint.value()
    // The second point determines the radius and start angle
    draw.setDraw((v) => {
        draw.drawLine(obj.center, v)
    })
    getPoint.setMessage("\nDetermine the starting point of the arc:");
    obj.startPoint = await getPoint.go() || new THREE.Vector3()
    draw.setDraw((v) => {
        obj.endPoint = v
        draw.drawCustomEntity(obj)
    })
    getPoint.setMessage("\nDetermine the ending point of the arc:");
    // The third point determines the end angle
    obj.endPoint = await getPoint.go() || new THREE.Vector3()
    draw.setDraw(()=> {})
    mxObj.addMxEntity(obj);
    addEllipseShapeGui(obj);
})

View the complete source code for this example: github (opens new window) | gitee (opens new window)

Effect:

# Ellipse arc shape MxDbEllipseArc


const getPoint = new MrxDbgUiPrPoint()
const mxObj = MxFun.getCurrentDraw();
const draw = new McEdGetPointWorldDrawObject()
let obj = new MxDbEllipseArc()
getPoint.setUserDraw(draw)
getPoint.go(async () => {
    // The first point determines the center
    obj.center = getPoint.value()
    // The second point determines the radius and start angle
    draw.setDraw((v, pWorldDraw) => {
        obj.startPoint = v
        obj.yRadius = obj.center.distanceTo(v)
        pWorldDraw.drawCustomEntity(obj)
    })
    obj.startPoint = await getPoint.go() || new THREE.Vector3()
    draw.setDraw((v, pWorldDraw) => {
        obj.endPoint = v
        pWorldDraw.drawCustomEntity(obj)
    })
    obj.endPoint = await getPoint.go() || new THREE.Vector3()

    mxObj.addMxEntity(obj);
    addEllipseShapeGui(obj)
})

View the complete source code of this example: github (opens new window) | gitee (opens new window)

Effect:

# Ring shape MxDbRingShape

class MxDbRingShapeDraw extends MxDbRingShape {
    innerPoint = new THREE.Vector3();
    outerPoint = new THREE.Vector3();
    constructor() {
        super();
        this._propertyDbKeys = [...this._propertyDbKeys, 'innerPoint', 'outerPoint']
    }
    getGripPoints(): THREE.Vector3[] {
        return [this.center, this.innerPoint, this.outerPoint]
    }
    moveGripPointsAt(index: number, offset: THREE.Vector3) {
        if(index === 0) this.center.add(offset), this.innerPoint.add(offset), this.outerPoint.add(offset)
        if(index === 1) this.innerPoint.add(offset)
        if(index === 2) this.outerPoint.add(offset)
        return true
    }
    worldDraw(pWorldDraw: McGiWorldDraw): void {
        if(this.innerPoint) this.innerRadius = this.center.distanceTo(this.innerPoint)
        if(this.outerPoint) this.outerRadius = this.center.distanceTo(this.outerPoint)
        super.worldDraw(pWorldDraw)
    }
}
const getPoint = new MrxDbgUiPrPoint()
const mxObj = MxFun.getCurrentDraw();
const draw = new McEdGetPointWorldDrawObject()
getPoint.setUserDraw(draw)
const worldDraw = new McEdGetPointWorldDrawObject()
const obj = new MxDbRingShapeDraw()
getPoint.setUserDraw(worldDraw)
getPoint.setMessage("\n确定圆弧开始点:");

getPoint.go(async (status) => {
    obj.center = getPoint.value()
    worldDraw.setDraw((v)=> {
        worldDraw.drawCircle(obj.center, obj.center.distanceTo(v))
    })
    obj.innerPoint = await getPoint.go() || new THREE.Vector3()
    worldDraw.setDraw((v)=> {
        obj.outerPoint = v
        worldDraw.drawCustomEntity(obj)
    })
    obj.outerPoint = await getPoint.go() || new THREE.Vector3()

    mxObj.addMxEntity(obj)

    addEllipseShapeGui(obj)
})

View the complete source code of this example: github (opens new window) | gitee (opens new window)

Effect:

# Star shape MxDbStarShape

class MxDbStarShapeDraw extends MxDbStarShape {
    innerPoint = new THREE.Vector3();
    outerPoint = new THREE.Vector3();
    constructor() {
        super();
        this._propertyDbKeys = [...this._propertyDbKeys, 'innerPoint', 'outerPoint']
    }
    getGripPoints(): THREE.Vector3[] {
        return [this.center, this.innerPoint, this.outerPoint]
    }
    moveGripPointsAt(index: number, offset: THREE.Vector3) {
        if(index === 0) this.center.add(offset), this.innerPoint.add(offset), this.outerPoint.add(offset)
        if(index === 1) this.innerPoint.add(offset)
        if(index === 2) this.outerPoint.add(offset)
        return true
    }
    worldDraw(pWorldDraw: McGiWorldDraw): void {
        if(this.innerPoint) this.innerRadius = this.center.distanceTo(this.innerPoint)
        if(this.outerPoint) this.outerRadius = this.center.distanceTo(this.outerPoint)
        super.worldDraw(pWorldDraw)
    }
}
const getPoint = new MrxDbgUiPrPoint()
const mxObj = MxFun.getCurrentDraw();
const draw = new McEdGetPointWorldDrawObject()
getPoint.setUserDraw(draw)
const worldDraw = new McEdGetPointWorldDrawObject()
const obj = new MxDbStarShapeDraw()
getPoint.setUserDraw(worldDraw)
getPoint.setMessage("\n确定圆弧开始点:");

getPoint.go(async (status) => {
    obj.center = getPoint.value()
    worldDraw.setDraw((v)=> {
        obj.innerPoint = v
        worldDraw.drawCircle(obj.center, obj.center.distanceTo(v))
    })
    obj.innerPoint = await getPoint.go() || new THREE.Vector3()
    worldDraw.setDraw((v)=> {
        obj.outerPoint = v
        worldDraw.drawCustomEntity(obj)
    })
    obj.outerPoint = await getPoint.go() || new THREE.Vector3()
    mxObj.addMxEntity(obj)

    addShapeGui(obj)
})

View the complete source code of this example: github (opens new window) | gitee (opens new window)

Effect: