簡體   English   中英

無法在 Autodesk Forge Viewer 中顯示文本精靈

[英]Unable to display text sprites in Autodesk Forge Viewer

我無法在 Autodesk Forge Viewer 中顯示文本精靈。

我創建了一個示例項目ForgeTextSprite ,它在加載的模型下方顯示一個羅盤。 盡管羅盤的度數顯示正常,但未顯示基數(N、S、E、W)的文本。

Forge TextSprite 截圖

下面的 TextSprite.ts 文件從三個 spritetext轉換為Typescript 我注釋掉了 THREE.LinearFilter 的用法,因為 Forge 查看器中似乎沒有它。

// 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');
    }
}

瀏覽器控制台窗口中未顯示任何錯誤。

請注意 Forge Viewer 基於 Three.js 版本 R71。 在您的代碼中,您似乎使用了 Three.js R103 的類型定義,並且三個 spritetext 依賴項引用了 Three.js R86。 恐怕是這些版本的差異導致了渲染文本精靈的無聲失敗。

作為替代方案,我建議:

  • 使用THREE.TextGeometry (在 R71 中可用)來生成薄的 3D 文本
  • 向場景中添加一個簡單的THREE.PlaneBufferGeometry並將刻度盤和標簽添加為紋理
  • 考慮將表盤/標簽作為另一個 Forge 模型(例如,從 PDF 或從 DWG 文件)加載到查看器場景中

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM