Shape
Mx.MxDbShape
is a graphic Shape base class based on THREE. shape, which can realize dynamic shape drawing. You can realize various 2d and 3d graphics effects by extending the MxDbShape class.
Mx.MxDbShape
default support shape curve closure, fill, picture fill, dotted line, solid line, line width and other Settings, The class based on Mx.MxDbEntity (opens new window).
Click Mx.MxDbShape API (opens new window) to check the properties and methods in detail.
# MxDbShape extension implementation process
Inheriting MxDbShape => Extending the _propertyDbKeys attribute array => Overrides the worldDraw method
# Extend the _propertyDbKeys property array:
class MxDbPolygonShape extends MxDbShape {
points = []
constructor() {
super()
this._propertyDbKeys = [...this._propertyDbKeys, 'points']
}
...
}
The _propertyDbKeys property records the names of the data to be retained, such as the 'points' property in the example, which is constantly redrawn during interactive (command) dynamic drawing, and the points array is reinitialized to an empty array. If you need to perform operations such as archive restoration, dynamic drawing, and drag to pinch points to change graphs, you need to save points data in real time to avoid initialization.
# Rewrite the worldDraw method
worldDraw is a rendering function, and the following is the default implementation:
worldDraw() {
// THREE is mounted to the window object by default, and is mounted only after the loadCoreCode function is called
const THREE:THREE = Mx.MxFun.getMxFunTHREE()
// Create shape path
const paths = this.createPaths(new THREE.Curve<THREE.Vector3>())
// The point of composition is obtained through the shape path
const points = this.getShapePoints(paths)
// Draw shape
this._draw(pWorldDraw, points)
// Draw the stroke of the shape
this._drawStoreLine(pWorldDraw, points)
}
We can implement various situations based on THREE.Curve and its derivative classes and create a shape path through the createPaths method, get the shape path points through getShapePoints, and finally draw the shape and stroke from _draw and _drawStoreLine. Or some algorithm that calculates the points that make up the shape and draws them directly from _draw and _drawStoreLine.
The rewriting methods for displaying the clip getGripPoints and moving the clip moveGripPointsAt are shown in Mx.MxDbEntity Custom Graphic Object.
# Example
Let's take drawing an arrow shape as an example.
export class MxDbArrow extends MxDbShape {
/** Whether the starting Angle is sharp */
isSharpCorner = true
/** Internal offset */
innerOffset = 10
/** External offset */
outerOffset = 22
/** Top offset */
topOffset = 36
startPoint = new THREE.Vector3()
endPoint = new THREE.Vector3()
constructor() {
super()
this._propertyDbKeys = [...this._propertyDbKeys, 'outerOffset', 'topOffset', 'innerOffset', 'isSharpCorner', 'startPoint', 'endPoint']
}
public worldDraw(pWorldDraw: McGiWorldDraw): void {
const _points = this.getArrowVertex(this.startPoint, this.endPoint)
if(_points) {
this._draw(pWorldDraw, _points)
this._drawStoreLine(pWorldDraw, _points)
}
}
getArrowVertex(p1:THREE.Vector3, p2:THREE.Vector3, isSharpCorner = this.isSharpCorner) {
let { innerOffset, topOffset, outerOffset, } = this
const coord: THREE.Vector3[] = [];
// 顶点
coord[3] = p2
const p1_p2 = Math.sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y));
if (p1_p2 === 0) {
return;
}
const sina = -(p2.x - p1.x) / p1_p2; //The sine of the Angle of rotation
const cosa = (p2.y - p1.y) / p1_p2; //Cosine value
const lInnerx = p1.x + innerOffset;
const lInnery = p1.y + p1_p2 - topOffset;
//Original coordinates of the outer turning point (left)
const lOuterx = p1.x + outerOffset;
const lOutery = p1.y + p1_p2 - topOffset;
const rInnerx = p1.x - innerOffset;
const rInnery = p1.y + p1_p2 - topOffset;
const rOuterx = p1.x - outerOffset;
const rOutery = p1.y + p1_p2 - topOffset;
if(isSharpCorner) {
coord[0] = p1
coord[6] = coord[0]
}else {
coord[0] = new THREE.Vector3(p1.x - (rInnerx - p1.x) * cosa, p1.y - (rInnerx - p1.x) * sina )
coord[6] = new THREE.Vector3(p1.x + (rInnerx - p1.x) * cosa, p1.y + (rInnerx - p1.x) * sina )
coord[7] = coord[0]
}
//The new coordinates of the inner and outer rotation point after the rotation Angle a
coord[1] = new THREE.Vector3(p1.x + (lInnerx - p1.x) * cosa - (lInnery - p1.y) * sina, p1.y + (lInnerx - p1.x) * sina + (lInnery - p1.y) * cosa);
coord[2] = new THREE.Vector3(p1.x + (lOuterx - p1.x) * cosa - (lOutery - p1.y) * sina, p1.y + (lOuterx - p1.x) * sina + (lOutery - p1.y) * cosa);
coord[4] = new THREE.Vector3(p1.x + (rOuterx - p1.x) * cosa - (rOutery - p1.y) * sina, p1.y + (rOuterx - p1.x) * sina + (rOutery - p1.y) * cosa);
coord[5] = new THREE.Vector3(p1.x + (rInnerx - p1.x) * cosa - (rInnery - p1.y) * sina, p1.y + (rInnerx - p1.x) * sina + (rInnery - p1.y) * cosa);
return coord
}
getGripPoints(): THREE.Vector3[] {
const center = new THREE.Vector3()
new THREE.Line3(this.startPoint, this.endPoint).getCenter(center)
return [
this.startPoint,
center,
this.endPoint,
]
}
moveGripPointsAt(index: number, offset: Vector3): boolean {
if(index === 0) this.startPoint.add(offset)
if(index === 1) this.startPoint.add(offset), this.endPoint.add(offset)
if(index === 2) this.endPoint.add(offset)
return true
}
}
Effect: Reference Mx.MxDbArrow()