troika-three-text
Advanced tools
Comparing version 0.39.2 to 0.40.0
@@ -6,2 +6,13 @@ # Change Log | ||
# [0.40.0](https://github.com/protectwise/troika/compare/v0.39.2...v0.40.0) (2021-02-28) | ||
### Bug Fixes | ||
* **troika-three-text:** fix boundingBox, boundingSphere, and raycasting with curveRadius ([7cc7c82](https://github.com/protectwise/troika/commit/7cc7c821eca8f7ae63170d9a484e806bc8814a94)), closes [#103](https://github.com/protectwise/troika/issues/103) | ||
## [0.39.2](https://github.com/protectwise/troika/compare/v0.39.1...v0.39.2) (2021-02-18) | ||
@@ -8,0 +19,0 @@ |
{ | ||
"name": "troika-three-text", | ||
"version": "0.39.2", | ||
"version": "0.40.0", | ||
"description": "SDF-based text rendering for Three.js", | ||
@@ -17,4 +17,4 @@ "author": "Jason Johnston <jason.johnston@protectwise.com>", | ||
"dependencies": { | ||
"troika-three-utils": "^0.39.0", | ||
"troika-worker-utils": "^0.39.0" | ||
"troika-three-utils": "^0.40.0", | ||
"troika-worker-utils": "^0.40.0" | ||
}, | ||
@@ -32,3 +32,3 @@ "devDependencies": { | ||
}, | ||
"gitHead": "37cf5738d3e9ba311fa0661a96e47e51e965e5ae" | ||
"gitHead": "4d8af2a4acc4a47b12b5d6d7f852632222bfb11b" | ||
} |
@@ -61,2 +61,3 @@ import { | ||
this.detail = 1 | ||
this.curveRadius = 0 | ||
@@ -70,13 +71,13 @@ // Define groups for rendering text outline as a separate pass; these will only | ||
// Preallocate zero-radius bounding sphere | ||
// Preallocate empty bounding objects | ||
this.boundingSphere = new Sphere() | ||
this.boundingBox = new Box3(); | ||
this.boundingBox = new Box3() | ||
} | ||
computeBoundingSphere () { | ||
// No-op; we'll sync the boundingSphere proactively in `updateGlyphs`. | ||
// No-op; we'll sync the boundingSphere proactively when needed. | ||
} | ||
computeBoundingBox() { | ||
// No-op; we'll sync the boundingBox proactively in `updateGlyphs`. | ||
// No-op; we'll sync the boundingBox proactively when needed. | ||
} | ||
@@ -101,2 +102,12 @@ | ||
set curveRadius(r) { | ||
if (r !== this._curveRadius) { | ||
this._curveRadius = r | ||
this._updateBounds() | ||
} | ||
} | ||
get curveRadius() { | ||
return this._curveRadius | ||
} | ||
/** | ||
@@ -119,18 +130,33 @@ * Update the geometry for a new set of glyphs. | ||
updateBufferAttr(this, glyphColorAttrName, glyphColors, 3) | ||
this._blockBounds = blockBounds | ||
this._chunkedBounds = chunkedBounds | ||
setInstanceCount(this, glyphAtlasIndices.length) | ||
this._updateBounds() | ||
} | ||
// Update the boundingSphere based on the total bounds | ||
const sphere = this.boundingSphere | ||
sphere.center.set( | ||
(blockBounds[0] + blockBounds[2]) / 2, | ||
(blockBounds[1] + blockBounds[3]) / 2, | ||
0 | ||
) | ||
sphere.radius = sphere.center.distanceTo(tempVec3.set(blockBounds[0], blockBounds[1], 0)) | ||
// Update the boundingBox based on the total bounds | ||
const box = this.boundingBox; | ||
box.min.set(blockBounds[0], blockBounds[1], 0); | ||
box.max.set(blockBounds[2], blockBounds[3], 0); | ||
_updateBounds() { | ||
const bounds = this._blockBounds | ||
if (bounds) { | ||
const { curveRadius, boundingBox: bbox } = this | ||
if (curveRadius) { | ||
const { PI, floor, min, max, sin, cos } = Math | ||
const halfPi = PI / 2 | ||
const twoPi = PI * 2 | ||
const absR = Math.abs(curveRadius) | ||
const leftAngle = bounds[0] / absR | ||
const rightAngle = bounds[2] / absR | ||
const minX = floor((leftAngle + halfPi) / twoPi) !== floor((rightAngle + halfPi) / twoPi) | ||
? -absR : min(sin(leftAngle) * absR, sin(rightAngle) * absR) | ||
const maxX = floor((leftAngle - halfPi) / twoPi) !== floor((rightAngle - halfPi) / twoPi) | ||
? absR : max(sin(leftAngle) * absR, sin(rightAngle) * absR) | ||
const maxZ = floor((leftAngle + PI) / twoPi) !== floor((rightAngle + PI) / twoPi) | ||
? absR * 2 : max(absR - cos(leftAngle) * absR, absR - cos(rightAngle) * absR) | ||
bbox.min.set(minX, bounds[1], curveRadius < 0 ? -maxZ : 0) | ||
bbox.max.set(maxX, bounds[3], curveRadius < 0 ? 0 : maxZ) | ||
} else { | ||
bbox.min.set(bounds[0], bounds[1], 0) | ||
bbox.max.set(bounds[2], bounds[3], 0) | ||
} | ||
bbox.getBoundingSphere(this.boundingSphere) | ||
} | ||
} | ||
@@ -137,0 +163,0 @@ |
@@ -35,6 +35,18 @@ import { | ||
const raycastMesh = new Mesh( | ||
new PlaneBufferGeometry(1, 1).translate(0.5, 0.5, 0), | ||
defaultMaterial | ||
) | ||
let getFlatRaycastMesh = () => { | ||
const mesh = new Mesh( | ||
new PlaneBufferGeometry(1, 1), | ||
defaultMaterial | ||
) | ||
getFlatRaycastMesh = () => mesh | ||
return mesh | ||
} | ||
let getCurvedRaycastMesh = () => { | ||
const mesh = new Mesh( | ||
new PlaneBufferGeometry(1, 1, 32, 1), | ||
defaultMaterial | ||
) | ||
getCurvedRaycastMesh = () => mesh | ||
return mesh | ||
} | ||
@@ -66,2 +78,3 @@ const syncStartEvent = {type: 'syncstart'} | ||
'clipRect', | ||
'curveRadius', | ||
'orientation', | ||
@@ -525,2 +538,9 @@ 'glyphGeometryDetail' | ||
get curveRadius() { | ||
return this.geometry.curveRadius | ||
} | ||
set curveRadius(r) { | ||
this.geometry.curveRadius = r | ||
} | ||
// Create and update material for shadows upon request: | ||
@@ -643,2 +663,21 @@ get customDepthMaterial() { | ||
/** | ||
* Translate a point in local space to an x/y in the text plane. | ||
*/ | ||
localPositionToTextCoords(position, target = new Vector2()) { | ||
target.copy(position) //simple non-curved case is 1:1 | ||
const r = this.curveRadius | ||
if (r) { //flatten the curve | ||
target.x = Math.atan2(position.x, Math.abs(r) - Math.abs(position.z)) * Math.abs(r) | ||
} | ||
return target | ||
} | ||
/** | ||
* Translate a point in world space to an x/y in the text plane. | ||
*/ | ||
worldPositionToTextCoords(position, target = new Vector2()) { | ||
return this.localPositionToTextCoords(this.worldToLocal(position), target) | ||
} | ||
/** | ||
* @override Custom raycasting to test against the whole text block's max rectangular bounds | ||
@@ -648,14 +687,22 @@ * TODO is there any reason to make this more granular, like within individual line or glyph rects? | ||
raycast(raycaster, intersects) { | ||
const textInfo = this.textRenderInfo | ||
if (textInfo) { | ||
const bounds = textInfo.blockBounds | ||
raycastMesh.matrixWorld.multiplyMatrices( | ||
this.matrixWorld, | ||
tempMat4.set( | ||
bounds[2] - bounds[0], 0, 0, bounds[0], | ||
0, bounds[3] - bounds[1], 0, bounds[1], | ||
0, 0, 1, 0, | ||
0, 0, 0, 1 | ||
) | ||
) | ||
const {textRenderInfo, curveRadius} = this | ||
if (textRenderInfo) { | ||
const bounds = textRenderInfo.blockBounds | ||
const raycastMesh = curveRadius ? getCurvedRaycastMesh() : getFlatRaycastMesh() | ||
const geom = raycastMesh.geometry | ||
const {position, uv} = geom.attributes | ||
for (let i = 0; i < uv.count; i++) { | ||
let x = bounds[0] + (uv.getX(i) * (bounds[2] - bounds[0])) | ||
const y = bounds[1] + (uv.getY(i) * (bounds[3] - bounds[1])) | ||
let z = 0 | ||
if (curveRadius) { | ||
z = curveRadius - Math.cos(x / curveRadius) * curveRadius | ||
x = Math.sin(x / curveRadius) * curveRadius | ||
} | ||
position.setXYZ(i, x, y, z) | ||
} | ||
geom.boundingSphere = this.geometry.boundingSphere | ||
geom.boundingBox = this.geometry.boundingBox | ||
raycastMesh.matrixWorld = this.matrixWorld | ||
raycastMesh.material.side = this.material.side | ||
tempArray.length = 0 | ||
@@ -662,0 +709,0 @@ raycastMesh.raycast(raycaster, tempArray) |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
685804
9343
+ Addedtroika-three-utils@0.40.0(transitive)
+ Addedtroika-worker-utils@0.40.0(transitive)
- Removedtroika-three-utils@0.39.0(transitive)
- Removedtroika-worker-utils@0.39.0(transitive)
Updatedtroika-three-utils@^0.40.0
Updatedtroika-worker-utils@^0.40.0