Merge branch 'main' into dim/cleanup

This commit is contained in:
Dimitrie Stefanescu
2022-01-11 12:46:38 +00:00
5 changed files with 224 additions and 12 deletions
+1 -1
View File
@@ -19,7 +19,7 @@
"dompurify": "^2.2.4",
"lodash.debounce": "^4.0.8",
"lodash.throttle": "^4.1.1",
"marked": "^1.2.6",
"marked": "^2.0.0",
"numeral": "^2.0.6",
"portal-vue": "^2.1.7",
"tween": "^0.9.0",
@@ -5,6 +5,7 @@ import * as BufferGeometryUtils from 'three/examples/jsm/utils/BufferGeometryUti
import ObjectWrapper from './ObjectWrapper'
import { getConversionFactor } from './Units'
import MeshTriangulationHelper from './MeshTriangulationHelper'
/**
* Utility class providing some top level conversion methods.
@@ -68,7 +69,7 @@ export default class Coverter {
// If we can convert it, we should invoke the respective conversion routine.
const type = this.getSpeckleType( obj )
if ( this[`${type}ToBufferGeometry`] ) {
try {
await callback( await this[`${type}ToBufferGeometry`]( obj.data || obj, scale ) )
@@ -310,16 +311,13 @@ export default class Coverter {
let n = faces[ k ]
if ( n <= 3 ) n += 3 // 0 -> 3, 1 -> 4
if ( n === 4 ) { // QUAD FACE
if ( n === 3 ) { // TRIANGLE FACE
indices.push( faces[ k + 1 ], faces[ k + 2 ], faces[ k + 3 ] )
indices.push( faces[ k + 1 ], faces[ k + 3 ], faces[ k + 4 ] )
} else if ( n === 3 ) { // TRIANGLE FACE
indices.push( faces[ k + 1 ], faces[ k + 2 ], faces[ k + 3 ] )
} else { //N-GON FACE
//TODO triangulate n-gon face.
//There is no need to throw an exception here, unsupported faces will simply be ignored.
//throw new Error( `Mesh type not supported. Face topology indicator: ${faces[k]}` )
} else { //Quad or N-GON FACE
const triangulation = MeshTriangulationHelper.triangulateFace( k, faces, vertices )
for( let t = 0; t < triangulation.length; t += 3 ) {
indices.push( triangulation[ t ], triangulation[ t + 1 ], triangulation[ t + 2 ] )
}
}
k += n + 1
@@ -0,0 +1,213 @@
/**
* Set of functions to triangulate n-gon faces (i.e. polygon faces with an arbitrary (n) number of vertices).
* This class is a JavaScript port of https://github.com/specklesystems/speckle-sharp/blob/main/Objects/Objects/Utils/MeshTriangulationHelper.cs
*/
export default class MeshTriangulationHelper {
/**
* Calculates the triangulation of the face at given faceIndex.
* @remarks This implementation is based the ear clipping method proposed by "Christer Ericson (2005) <i>Real-Time Collision Detection</i>.
* @param {Number} faceIndex The index of the face's cardinality indicator `n`
* @param {Number[]} faces The list of faces in the mesh
* @param {Number[]} vertices The list of vertices in the mesh
* @return {Number[]} flat list of triangle faces (without cardinality indicators)
*/
static triangulateFace( faceIndex, faces, vertices ) {
let n = faces[faceIndex]
if ( n <= 3 ) n += 3 // 0 -> 3, 1 -> 4
//Converts from relative to absolute index (returns index in mesh.vertices list)
function asIndex( v ) {
return faceIndex + v + 1
}
//Gets vertex from a relative vert index
function V( v )
{
let index = faces[ asIndex( v ) ] * 3
return new Vector3( vertices[index], vertices[index + 1], vertices[index + 2] )
}
let triangleFaces = Array( ( n - 2 ) * 3 )
//Calculate face normal using the Newell Method
let faceNormal = new Vector3( 0,0, 0 )
for ( let ii = n - 1, jj = 0; jj < n; ii = jj, jj++ )
{
let iPos = V( ii )
let jPos = V( jj )
faceNormal.x += ( jPos.y - iPos.y ) * ( iPos.z + jPos.z ) // projection on yz
faceNormal.y += ( jPos.z - iPos.z ) * ( iPos.x + jPos.x ) // projection on xz
faceNormal.z += ( jPos.x - iPos.x ) * ( iPos.y + jPos.y ) // projection on xy
}
faceNormal.normalize()
//Set up previous and next links to effectively form a double-linked vertex list
const prev = Array( n )
const next = Array( n )
for ( let j = 0; j < n; j++ )
{
prev[j] = j - 1
next[j] = j + 1
}
prev[0] = n - 1
next[n - 1] = 0
//Start clipping ears until we are left with a triangle
let i = 0
let counter = 0
while ( n >= 3 )
{
let isEar = true
//If we are the last triangle or we have exhausted our vertices, the below statement will be false
if ( n > 3 && counter < n )
{
const prevVertex = V( prev[i] )
const earVertex = V( i )
const nextVertex = V( next[i] )
if ( this.triangleIsCCW( faceNormal, prevVertex, earVertex, nextVertex ) )
{
let k = next[next[i]]
do
{
if ( this.testPointTriangle( V( k ), prevVertex, earVertex, nextVertex ) )
{
isEar = false
break
}
k = next[k]
} while ( k !== prev[i] )
}
else
{
isEar = false
}
}
if ( isEar )
{
const a = faces[asIndex( i )]
const b = faces[asIndex( next[i] )]
const c = faces[asIndex( prev[i] )]
triangleFaces.push( a, b, c )
next[prev[i]] = next[i]
prev[next[i]] = prev[i]
n--
i = prev[i]
counter = 0
}
else
{
i = next[i]
counter++
}
}
return triangleFaces
}
/**
* Tests if point v is within the triangle *abc*.
* @param {Vector3} v
* @param {Vector3} a
* @param {Vector3} b
* @param {Vector3} c
* @returns {boolean} true if v is within triangle.
*/
static testPointTriangle( v, a, b, c )
{
function Test( _v, _a, _b )
{
let crossA = _v.cross( _a )
let crossB = _v.cross( _b )
let dotWithEpsilon = Number.EPSILON + crossA.dot( crossB )
return Math.sign( dotWithEpsilon ) !== -1
}
return Test( b.sub( a ), v.sub( a ), c.sub( a ) )
&& Test( c.sub( b ), v.sub( b ), a.sub( b ) )
&& Test( a.sub( c ), v.sub( c ), b.sub( c ) )
}
/**
* Checks that triangle abc is clockwise with reference to referenceNormal.
* @param {Vector3} referenceNormal The normal direction of the face.
* @param {Vector3} a
* @param {Vector3} b
* @param {Vector3} c
* @returns {boolean} true if triangle is ccw
*/
static triangleIsCCW( referenceNormal, a, b, c )
{
const triangleNormal = c.sub( a ).cross( b.sub( a ) ).getNormal()
return referenceNormal.dot( triangleNormal ) > 0.0
}
}
/**
* Encapsulates vector maths operations required for polygon triangulation
*/
class Vector3 {
constructor( x, y, z ) {
this.x = x
this.y = y
this.z = z
}
getNormal( )
{
const scale = 1.0 / Math.sqrt( this.squareSum() )
return new Vector3( this.x * scale, this.y * scale, this.z * scale )
}
add( v ) {
this.x += v.x
this.y += v.y
this.z += v.z
return this
}
sub( v ) {
this.x -= v.x
this.y -= v.y
this.z -= v.z
return this
}
mul( n ) {
this.x *= n
this.y *= n
this.z *= n
return this
}
dot( v ) { return this.x * v.x + this.y * v.y + this.z * v.z }
cross( v ) {
this.x = this.y * v.z - this.z * v.y
this.y = this.z * v.x - this.x * v.z
this.z = this.x * v.y - this.y * v.x
return this
}
squareSum( ) {
return this.x * this.x + this.y * this.y + this.z * this.z
}
normalize() {
const scale = 1.0 / Math.sqrt( this.squareSum() )
return this.mul( scale )
}
}
@@ -26,7 +26,7 @@ async function isLocalNetworkUrl( url ) {
}
async function makeNetworkRequest( { url, data, headersData } ) {
if ( process.env.ALLOW_LOCAL_NETWORK !== 'true' && isLocalNetworkUrl( url ) ) {
if ( process.env.ALLOW_LOCAL_NETWORK !== 'true' && ( await isLocalNetworkUrl( url ) ) ) {
return {
success: false,
error: 'Local network requests are not allowed. To allow, use ALLOW_LOCAL_NETWORK=true environment variable',
+1
View File
@@ -91,3 +91,4 @@ For any security vulnerabilities or concerns, please contact us directly at secu
### License
Unless otherwise described, the code in this repository is licensed under the Apache-2.0 License. Please note that some modules, extensions or code herein might be otherwise licensed. This is indicated either in the root of the containing folder under a different license file, or in the respective file's header. If you have any questions, don't hesitate to get in touch with us via [email](mailto:hello@speckle.systems).