Autodesk forge 无法在Autodesk Forge Viewer中显示文本精灵
我无法在Autodesk Forge Viewer中显示文本精灵 我创建了一个示例项目,在加载的模型下显示一个罗盘玫瑰。虽然罗盘的度数显示正常,但不显示基数(N、S、E、W)的文本 下面的TextSprite.ts文件已从转换为Typescript。我注释掉了THREE.LinearFilter的用法,因为它似乎不在Forge viewer中Autodesk forge 无法在Autodesk Forge Viewer中显示文本精灵,autodesk-forge,autodesk-viewer,Autodesk Forge,Autodesk Viewer,我无法在Autodesk Forge Viewer中显示文本精灵 我创建了一个示例项目,在加载的模型下显示一个罗盘玫瑰。虽然罗盘的度数显示正常,但不显示基数(N、S、E、W)的文本 下面的TextSprite.ts文件已从转换为Typescript。我注释掉了THREE.LinearFilter的用法,因为它似乎不在Forge viewer中 // Converted to Typescript from https://github.com/vasturiano/three-spritete
// Converted to Typescript from https://github.com/vasturiano/three-spritetext.
//
// Commented out usage to THREE.LinearFilter as that doesn't seem to be present in the
// Forge viewer.
export default class TextSprite extends THREE.Sprite {
_text: string;
_textHeight: number;
_color: string;
_backgroundColor: string;
_padding: number;
_borderWidth: number;
_borderColor: string;
_strokeWidth: number;
_strokeColor: string;
_fontFace: string;
_fontSize: number;
_fontWeight: string;
_canvas: HTMLCanvasElement;
_texture: THREE.Texture;
constructor(text = '', textHeight = 10, color = 'rgba(255, 255, 255, 1)') {
super(new THREE.SpriteMaterial({ map: new THREE.Texture() }));
this._text = `${text}`;
this._textHeight = textHeight;
this._color = color;
this._backgroundColor = ''; // no background color
this._padding = 0;
this._borderWidth = 0;
this._borderColor = 'blue';
this._strokeWidth = 0;
this._strokeColor = 'green';
this._fontFace = 'Arial';
this._fontSize = 90; // defines text resolution
this._fontWeight = 'normal';
this._canvas = document.createElement('canvas');
if (this.material instanceof THREE.SpriteMaterial) {
this._texture = this.material.map;
}
// this._texture.minFilter = three.LinearFilter;
this._genCanvas();
}
get text() { return this._text; }
set text(text) { this._text = text; this._genCanvas(); }
get textHeight() { return this._textHeight; }
set textHeight(textHeight) { this._textHeight = textHeight; this._genCanvas(); }
get color() { return this._color; }
set color(color) { this._color = color; this._genCanvas(); }
get backgroundColor() { return this._backgroundColor; }
set backgroundColor(color) { this._backgroundColor = color; this._genCanvas(); }
get padding() { return this._padding; }
set padding(padding) { this._padding = padding; this._genCanvas(); }
get borderWidth() { return this._borderWidth; }
set borderWidth(borderWidth) { this._borderWidth = borderWidth; this._genCanvas(); }
get borderColor() { return this._borderColor; }
set borderColor(borderColor) { this._borderColor = borderColor; this._genCanvas(); }
get fontFace() { return this._fontFace; }
set fontFace(fontFace) { this._fontFace = fontFace; this._genCanvas(); }
get fontSize() { return this._fontSize; }
set fontSize(fontSize) { this._fontSize = fontSize; this._genCanvas(); }
get fontWeight() { return this._fontWeight; }
set fontWeight(fontWeight) { this._fontWeight = fontWeight; this._genCanvas(); }
get strokeWidth() { return this._strokeWidth; }
set strokeWidth(strokeWidth) { this._strokeWidth = strokeWidth; this._genCanvas(); }
get strokeColor() { return this._strokeColor; }
set strokeColor(strokeColor) { this._strokeColor = strokeColor; this._genCanvas(); }
_genCanvas() {
const canvas = this._canvas;
const ctx = canvas.getContext('2d');
const border = [this.borderWidth, this.borderWidth]; // x,y border
const relBorder = border.map(b => b * this.fontSize * 0.1); // border in canvas units
const padding = [this.padding, this.padding]; // x,y padding
const relPadding = padding.map(p => p * this.fontSize * 0.1); // padding in canvas units
const lines = this.text.split('\n');
const font = `${this.fontWeight} ${this.fontSize}px ${this.fontFace}`;
ctx.font = font; // measure canvas with appropriate font
const innerWidth = Math.max(...lines.map(line => ctx.measureText(line).width));
const innerHeight = this.fontSize * lines.length;
canvas.width = innerWidth + relBorder[0] * 2 + relPadding[0] * 2;
canvas.height = innerHeight + relBorder[1] * 2 + relPadding[1] * 2;
// paint border
if (this.borderWidth) {
ctx.strokeStyle = this.borderColor;
if (relBorder[0]) {
ctx.lineWidth = relBorder[0] * 2;
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(0, canvas.height);
ctx.moveTo(canvas.width, 0);
ctx.lineTo(canvas.width, canvas.height);
ctx.stroke();
}
if (relBorder[1]) {
ctx.lineWidth = relBorder[1] * 2;
ctx.beginPath();
ctx.moveTo(relBorder[0], 0);
ctx.lineTo(canvas.width - relBorder[0], 0);
ctx.moveTo(relBorder[0], canvas.height);
ctx.lineTo(canvas.width - relBorder[0], canvas.height);
ctx.stroke();
}
}
ctx.translate(relBorder[0], relBorder[1]);
// paint background
if (this.backgroundColor) {
ctx.fillStyle = this.backgroundColor;
ctx.fillRect(0, 0, canvas.width - relBorder[0] * 2, canvas.height - relBorder[1] * 2);
}
ctx.translate(relPadding[0], relPadding[1]);
// paint text
ctx.font = font; // Set font again after canvas is resized, as context properties are reset
ctx.fillStyle = this.color;
ctx.textBaseline = 'bottom';
const drawTextStroke = this.strokeWidth > 0;
if (drawTextStroke) {
ctx.lineWidth = this.strokeWidth * this.fontSize / 10;
ctx.strokeStyle = this.strokeColor;
}
lines.forEach((line, index) => {
const lineX = (innerWidth - ctx.measureText(line).width) / 2;
const lineY = (index + 1) * this.fontSize;
drawTextStroke && ctx.strokeText(line, lineX, lineY);
ctx.fillText(line, lineX, lineY);
});
// Inject canvas into sprite
this._texture.image = canvas;
this._texture.needsUpdate = true;
const yScale = this.textHeight * lines.length + border[1] * 2 + padding[1] * 2;
this.scale.set(yScale * canvas.width / canvas.height, yScale, 0);
}
// clone(recursive?: boolean): this {
// return new SpriteText(this.text, this.textHeight, this.color).copy(this);
// }
copy(source: this, recursive?: boolean): this {
super.copy(source, recursive);
this.color = source.color;
this.backgroundColor = source.backgroundColor;
this.padding = source.padding;
this.borderWidth = source.borderWidth;
this.borderColor = source.borderColor;
this.fontFace = source.fontFace;
this.fontSize = source.fontSize;
this.fontWeight = source.fontWeight;
this.strokeWidth = source.strokeWidth;
this.strokeColor = source.strokeColor;
return this;
}
}
在CompassRose.ts中,将创建文本精灵,将其翻译并旋转到适当的位置,然后将其添加到THREE.Group中
import TextSprite from './TextSprite';
const markerLength = 1;
const fifthMarkerLength = 5;
const tenthMarkerLength = 10;
const ordinalMarkerLength = 15;
const color = 0x444444;
const fontSize = 4;
const halfFontSize = fontSize / 2;
const radius = 100;
const cardinalOuter = radius + 10;
export default class CompassRose {
build(): THREE.Group {
const group = new THREE.Group();
const material = new THREE.LineBasicMaterial({
color: color
});
for (let degree = 0; degree < 360; degree++) {
const lineParent = new THREE.Group();
const lineAngle = (Math.PI * degree) / 180;
lineParent.rotateX(lineAngle);
const length = this.getMarkerLength(degree);
const line = this.buildLine(radius, length, material);
lineParent.add(line);
group.add(lineParent);
}
let sprite = this.createHeadingTextSprite("N")
.translateX(cardinalOuter - fontSize)
.translateY(halfFontSize)
.rotateZ(-Math.PI / 2);
group.add(sprite);
sprite = this.createHeadingTextSprite("S")
.translateX(-cardinalOuter)
.translateY(halfFontSize)
.rotateZ(-Math.PI / 2);
group.add(sprite);
sprite = this.createHeadingTextSprite("W")
.translateY(cardinalOuter)
.translateX(-halfFontSize)
.rotateZ(-Math.PI / 2);
group.add(sprite);
sprite = this.createHeadingTextSprite("E")
.translateY(-cardinalOuter + fontSize)
.translateX(-halfFontSize)
.rotateZ(-Math.PI / 2);
group.add(sprite);
return group;
}
private getMarkerLength(degree: number) {
let length: number;
if (degree % 90 === 0) {
length = ordinalMarkerLength;
} else if (degree % 10 === 0) {
length = tenthMarkerLength;
} else if (degree % 5 === 0) {
length = fifthMarkerLength;
} else {
length = markerLength;
}
return length;
}
private buildLine(radius: number, length: number, material: THREE.Material) {
const lineGeometry = new THREE.Geometry();
lineGeometry.vertices.push(new THREE.Vector3(0, radius, 0));
lineGeometry.vertices.push(new THREE.Vector3(0, radius - length, 0));
return new THREE.Line(lineGeometry, material);
}
private createHeadingTextSprite(text: string): THREE.Sprite {
return new TextSprite(text, 10, '#888888');
}
}
从“/TextSprite”导入TextSprite;
常数markerLength=1;
常量第五个MarkerLength=5;
常数tenthMarkerLength=10;
常量序数MarkerLength=15;
常数颜色=0x4444;
常数fontSize=4;
const halfFontSize=fontSize/2;
常数半径=100;
常数=半径+10;
导出默认类CompassRose{
build():三个。组{
const group=new THREE.group();
常量材质=新的三线基本材质({
颜色:颜色
});
对于(让度=0;度<360;度++){
const lineParent=new THREE.Group();
常数lineAngle=(Math.PI*度)/180;
lineParent.rotateX(lineAngle);
常量长度=此.getMarkerLength(度);
常数线=该建筑线(半径、长度、材料);
lineParent.add(行);
添加组(lineParent);
}
让sprite=this.createHeadingTextSprite(“N”)
.translateX(基数外部-字体大小)
.translateY(半字体大小)
.rotateZ(-Math.PI/2);
添加(雪碧);
sprite=this.createHeadingTextSprite(“S”)
.translateX(-cardinalOuter)
.translateY(半字体大小)
.rotateZ(-Math.PI/2);
添加(雪碧);
sprite=this.createHeadingTextSprite(“W”)
.translateY(红衣主教)
.translateX(-halfFontSize)
.rotateZ(-Math.PI/2);
添加(雪碧);
sprite=this.createHeadingTextSprite(“E”)
.translateY(-cardinalOuter+fontSize)
.translateX(-halfFontSize)
.rotateZ(-Math.PI/2);
添加(雪碧);
返回组;
}
私人getMarkerLength(学位:编号){
让长度:数字;
如果(度%90==0){
长度=顺序标记长度;
}否则如果(学位%10==0){
长度=十分长;
}否则如果(学位%5==0){
长度=第五个MarkerLength;
}否则{
长度=标记长度;
}
返回长度;
}
专用建筑线(半径:编号,长度:编号,材质:三。材质){
const lineGeometry=新的三个.Geometry();
lineGeometry.vertices.push(新的3.Vector3(0,半径,0));
lineGeometry.vertices.push(新的3.Vector3(0,半径-长度,0));
返回新的三线(线几何体、材质);
}
私有createHeadingTextSprite(文本:字符串):3.Sprite{
返回新的TextSprite(文本,10'#8888888');
}
}
浏览器控制台窗口中未显示任何错误。请注意,Forge Viewer基于Three.js版本R71。在代码中,您似乎在使用Three.js R103的类型定义,Three spritetext依赖项引用Three.js R86。恐怕是这些版本的差异导致了文本精灵呈现的无声失败 作为替代方案,我建议:
- 使用(可在R71中获得)生成精简的3D文本
- 向场景中添加简单贴图,并将刻度盘和标签添加为纹理
- 考虑将拨号盘/标签作为另一个Forge模型(例如,从PDF或DWG文件)加载到查看器场景中