I am trying to convert some isolated elements on forge viewer to GLTF I have included this script
<script src="https://unpkg.com/three#0.126.0/examples/js/exporters/GLTFExporter.js"></script>
I am using this code to export to GLTF
const exporter=new THREE.GLTFExporter()
const exportGLTF = () => {
var scene = viewer.impl.scene
exporter.parse( scene, function ( gltf ) {
const output = JSON.stringify( gltf, null, 2 );
console.log(output)
//saveString( output, 'scene.gltf' );
}, {trs: true} );
}
but unfortunately I receive empty GLTF output like this
{
"asset": {
"version": "2.0",
"generator": "THREE.GLTFExporter"
},
"scenes": [
{}
],
"scene": 0
}
R103+ THREE.GLTFExporter() is not compatible with THREE R71 (since we use a modified BufferGeometry), hence why you are getting no geometry.
Alex is right, use glTF-convert-utils.
Here's an example node.js script, with DBID filter. Use this to filter out a sub-set of the full drawing:
// convert SVF to dstPath/output.glb (with zeux compression)
// but only convert a subset of objects (see filter on line 23)
// INSTALL:
// > npm install forge-convert-utils forge-server-utils fs-extra gltfpack
// RUN:
// > node convert url guid token dstPath
const path = require('path');
const fs = require('fs-extra');
var gltfpack = require('gltfpack');
const { SvfReader, GltfWriter } = require('forge-convert-utils');
async function convert(urn, guid, token, dstPath) {
// Convert SVF to glTF
const reader = await SvfReader.FromDerivativeService(urn, guid, { token });
const writer = new GltfWriter({
ignoreLineGeometry: true,
ignorePointGeometry: true,
skipUnusedUvs: false,
center: false,
filter: (dbid) => (
[34044, 40936, 41095, 39471, 40933, 40939, 41092, 41097, 41090, 40946].indexOf(dbid)>-1)
});
const svf = await reader.read();
const gltfDir = path.join(path.dirname(dstPath), 'gltf');
fs.ensureDirSync(gltfDir);
await writer.write(svf, gltfDir);
gltfpack.pack(['-cc', '-i', './gltf/output.gltf', '-o', 'output.glb'], { read: fs.readFileSync, write: fs.writeFileSync})
}
const urn = `dXJuOm...j0z`;
const guid = `2588ca45-8487-cddf-b974-7f04179909a2`;
const token = `eyJhbG......gJHNA`
const dstPath = `out`;
convert(urn, guid, token, dstPath);
Remember to fill in the urn, guid & token with your forge details. The script will connect to forge, download svf, convert to gltf, then convert to glb (with MeshOpt compression using glTFpack).
I am not sure it's possible to extract the Forge scene with ThreeJS exporters.
If you want to extract your Forge model into glTF format, one of the best way (as of today) is to use the Forge Convert Utils package.
Related
I am getting error attached when i try to upload a file from angular. But, the same azure storage account configuration working from asp.net but not from angular. Can you please help me on this?cors error azure blob storage
I am trying to upload a file to azure blob storage from angular, the below is the code that, i implemented. I have directly added the sas token url instead of generating it to check whether file upload works or not.
below is the code
// environment file
export const environment = {
production: false,
accountName : "<accountname>",
containerName:"<containername>",
key:"<key>"
};
// added in pollyfills
(window as any).global = window;
(window as any).process = require( 'process' );
(window as any).Buffer = require( 'buffer' ).Buffer
//file upload method`
async uploadFileToBlob() {
// generate account sas token
const accountName = environment.accountName;
const key = environment.key;
var str = CryptoJS.HmacSHA256(StringToSign, CryptoJS.enc.Base64.parse(key));
var sig = CryptoJS.enc.Base64.stringify(str);
const sasToken = `sp=rac&st=2022-11-21T15:37:13Z&se=2023-12-31T23:37:13Z&spr=https&sv=2021-06-08&sr=c&sig=rYx4JWTcGPVSceUkuxJDSXN8u1%2BbNSFh3A7dYPqw3EA%3D`;
const containerName = environment.containerName;
const pipeline = newPipeline(new AnonymousCredential(), {
retryOptions: { maxTries: 4 }, // Retry options
userAgentOptions: { userAgentPrefix: "AdvancedSample V1.0.0" }, // Customized telemetry string
keepAliveOptions: {
// Keep alive is enabled by default, disable keep alive by setting false
enable: false
}
});
const blobServiceClient = new BlobServiceClient(`https://${accountName}.blob.core.windows.net?${sasToken}`,
pipeline)
const containerClient = blobServiceClient.getContainerClient(containerName)
if (!containerClient.exists()) {
console.log("the container does not exit")
await containerClient.create()
}
const client = containerClient.getBlockBlobClient(this.fileName.name)
const response = await client.uploadData(this.fileName, {
blockSize: 4 * 1024 * 1024, // 4MB block size
concurrency: 20, // 20 concurrency
onProgress: (ev) => console.log(ev),
blobHTTPHeaders: { blobContentType: this.fileName.type }
})
console.log(response._response.status)
}
Can you help me to resolve this issue ?
I use the plugin "vue-qrcode" to generate a qr code for my users to for a link to their public profile so they can share it e.g. on thei business card.
The Idea is now to give my users the possibility via a button to download the qr code and via another button to copy the qr code to the clipboard to make it easier to send it e.g. via mail (specially for the smartphone users).
Problem: I don't know how i can download or copy the qr code. Does anybody know to copy or download the qr code? Currently I use 'vue-clipboard2' to copy links etc. but it seems it can't copy images.
I use the below code to display the qr code on our site:
<template>
<qrcode-vue
style = "cursor: pointer;"
:value = "linkToProfile"
size = 160
level = "M"
:background = "backgroundcolor_qrcode"
:foreground = "color_qrcode"
></qrcode-vue>
</template>
<script>
import Vue from 'vue'
import QrcodeVue from 'qrcode.vue'
Vue.component('qrcode-vue', QrcodeVue )
export default {
data: () => ({
linkToProfile: "http://www.example.com/johnDoe",
})
</script>
Thanks -
Christian
I figured it out. The solution looks like this:
TEMPLATE AREA:
<qrcode-vue
id="qrblock"
:value = "linkToSki"
size = 220
level = "M"
ref="qrcode"
></qrcode-vue>
FUNCITONS AREA:
// -- FUNCTIONS AREA TO COPY / DOWNLOAD QR CODE - END ---
function selectText(element) {
if (document.body.createTextRange) {
const range = document.body.createTextRange();
range.moveToElementText(element);
range.select();
} else if (window.getSelection) {
const selection = window.getSelection();
const range = document.createRange();
range.selectNodeContents(element);
selection.removeAllRanges();
selection.addRange(range);
}
}
function copyBlobToClipboardFirefox(href) {
const img = document.createElement('img');
img.src = href;
const div = document.createElement('div');
div.contentEditable = true;
div.appendChild(img);
document.body.appendChild(div);
selectText(div);
const done = document.execCommand('Copy');
document.body.removeChild(div);
return done;
}
function copyBlobToClipboard(blob) {
// eslint-disable-next-line no-undef
const clipboardItemInput = new ClipboardItem({
'image/png' : blob
});
return navigator.clipboard
.write([clipboardItemInput])
.then(() => true)
.catch(() => false);
}
function downloadLink(name, href) {
const a = document.createElement('a');
a.download = name;
a.href = href;
document.body.append();
a.click();
a.remove();
}
// -- FUNCTIONS AREA TO COPY / DOWNLOAD QR CODE - END ---
anyone here implemented Dialog flow fullfilment on graphql server? How do you handle it? Do you handle fulfillment as a mutation or you implement it as a separate rest endpoint?
I am able to expose my local server using ngrok but I am not sure how to go about setting up the fulfillment. I had separated my DF code from GraphQL code such that the DF module only exposes the methods that handle event and text queries to Dialog flow:
// df/index.js
module.exports={
text: ()=>{
self=module.exports
// ...
return self.getResult()
},
event: ()=>{
self=module.exports
// ...
return self.getResult()
},
getResult:()=>{
//...
return{
query,
response,
cards,
quickReply
}
}
}
Then this is passed through the graphQL context and exposed to the bot.resolver.js module where respective mutations for handling text and events are defined as shown
// schema/resolvers/bot.resolver.js
module.exports={
// Mutation
Mutation:{
sendText: (parent,args,context) => {
const {df}=context;
const response = df.text(args);
return response;
},
sendEvent: (parent,args,context) => {
const {df}=context;
const response = df.event(args);
return response;
},
},
};
The corresponding graphQL types are defined in bot.type.js as shown:
const { gql } = require('apollo-server-express');
module.exports=gql`
type Course {
id:ID
header:String!
image:String!
description:String
price:String!
}
type Option {
value:String!
payload:String
link:String
}
type QuickReply {
text:String!
options:[Option!]!
}
type Bot {
query:String!,
response:String!
cards:[Course!]
quickReply:QuickReply
}
type Mutation {
sendText(text: String!, userId:String!, parameters:String): Bot!
sendEvent(name: String!, userId:String!, parameters:String): Bot!
}
`;
Please advise where I can write the code below that sets up dialog flow fulfillment
dialogflow-fulfillment setup code
😊Surprisingly it was as simple as writing it as a middleware on my graphQl api.
// import the required dependencies
const express = require('express');
const bodyParser = require('body-parser')
const cors = require('cors');
const { ApolloServer, } = require('apollo-server-express');
// do not forget your graphQl schema definition
const schema = require('./schema');
// we shall also need to import the data source.
// I will assume an array for our data source defined as below
const models ={
Course:[
{id:1, name:'Chatbots',}
{id:2, name:'React',}
{id:3, name:'Express',}
{id:4, name:'GraphQl',}
],
Book:[
{id:1, title:'Fundermentals in Chatbots',courseId:1},
{id:2, title:'Express for Newbies',courseId:3},
{id:3, title:'Advanced AI on Bots',courseId:1},
{id:4, title:'GraphQl Simplified',courseId:4},
]
}
// Import the WebhookClient class
const { WebhookClient } = require('dialogflow-fulfillment');
// Do the graphQl gymnastics ... I find Apollo server 2 just on point.
const server = new ApolloServer(schema);
const path='/'
const port = process.env.PORT || 4000
const app = express(); // we will merge express with the Apollo server 2
// do the express gymnastics ...
app.use(path,cors(),bodyParser.json(),)
// **IT'S HERE THAT WE DEFINE DIALOG FLOW'S WEB-HOOK AS A MIDDLEWARE**
app.use('/webhook', async (request,response,next)=>{
const agent = new WebhookClient({ request, response });
const {parameters}=request.body.queryResult;
const course =parameters['course'];
// ... do the database logic here ...
// eg get the title of available text books for the particular course
// I will assume
const {id} = await models.Course.find(({name})=>name ===course)
const books = await model.Book.filter(({courseId})=>courseId===id)
const booksTitleArray = books.map(({title})=>title)
let titleList = booksTitle.Array.toString();
titleList.replace(',', ' , ') // put space btn comas
titleList.replace(/\,(?=[^,]*$)/, "and")
let intentMap = new Map();
const recommendBooks courses=>{
agent.add(`For ${course}, We use the following books: ${titleList}`);
};
intentMap.set('course.recommended.books',recommendBooks);
agent.handleRequest(intentMap);
next();
})
server.applyMiddleware({ app, path });
app.listen(port,()=>{
console.log( `Apollo Server Running on http://localhost:${port}`)
})
I feel like writing an article on this because I tried looking for help almost everywhere in vain. Incase I get the time to do so, I will provide it in the comments.😏😉🤔🤭
Guys, we should not forget the ngrok magic if we are testing from localhost 😁
Trying to follow the samples from https://github.com/Azure/ms-rest-nodeauth
When passing authresponse to a client to generate a client to ping resources, I end up getting:
Error: credentials argument needs to implement signRequest method
I am trying to read through the documents to see if I need to sign the token's I am getting back from the SDK/Azure AD, but the documentation for the new SDK doesnt show anything
Figured it out, have to call .credentials on the authresponse
Adding the code, using #azure/arm-billing, in case the full code file is helpful.
// auth.json
// Create auth file with Azure CLI:`az ad sp create-for-rbac --sdk-auth > auth.json`
{
"clientId": "",
"clientSecret": "",
"subscriptionId": "",
"tenantId": "",
"activeDirectoryEndpointUrl": "https://login.microsoftonline.com",
"resourceManagerEndpointUrl": "https://management.azure.com/",
"activeDirectoryGraphResourceId": "https://graph.windows.net/",
"sqlManagementEndpointUrl": "https://management.core.windows.net:8443/",
"galleryEndpointUrl": "https://gallery.azure.com/",
"managementEndpointUrl": "https://management.core.windows.net/"
}
// index.js
const msRest = require("#azure/ms-rest-js");
const msRestAzure = require("#azure/ms-rest-azure-js");
const msRestNodeAuth = require("#azure/ms-rest-nodeauth");
const armBilling = require("#azure/arm-billing");
const path = require('path');
// list billing information
const lists = async (client) => {
try {
let lists = [];
const enrollmentAccounts = await client.enrollmentAccounts.list();
lists.push(enrollmentAccounts);
const billingPeriods = await client.billingPeriods.list();
lists.push(billingPeriods);
const invoices = await client.invoices.list();
lists.push(invoices);
return lists;
} catch (err) {
console.log(err);
throw (err);
}
}
// sample auth file created from Azure CLI - removed PII
const authenticationFile = path.join(__dirname, "./auth.json");
const options = {
filePath: authenticationFile
};
// get subscriptionId from auth file
const subscriptionIdFromAuthFile = require('./auth.json').subscriptionId;
// authenticate and getting billing information
msRestNodeAuth.loginWithAuthFileWithAuthResponse(options).then(async (response) => {
console.log("authenticated");
// --- CHANGE response parameter to -> response.credentials
const client = new armBilling.BillingManagementClient(response.credentials, subscriptionIdFromAuthFile);
console.log("client created");
const results = await lists(client);
console.log(`The result is:${JSON.stringify(results)}`);
}).catch((err) => {
console.error(err);
});
Programmatically, my code is detecting a difference between two classes of images, and always rejecting one class, while always allowing the other.
I have yet to find any difference between the images that yield the error and the ones that don't an yield error. But there has to be some difference, because the ones that yield an error do so 100% of the time, and the others work as expected 100% of the time.
In particular, I have inspected color format: RGB in both groups; size: no notable difference; datatype: uint8 in both; magnitude of pixel values: similar in both.
Below are two images that never work, followed by two images that always work:
This image never works: https://www.colourbox.com/preview/11906131-maple-tree-and-grass-silhouette.jpg
This image never works: http://feldmanphoto.com/wp-content/uploads/awe-inspiring-house-clipart-black-and-white-disney-coloring-pages-big-clipartxtras-illistration-background-housewives-bouncy.jpeg
This image always works: http://www.spacedesign.us/wp-content/uploads/landscape-with-old-tree-and-grass-over-white-background-black-and-black-and-white-trees.jpg
This image always works: http://www.modernhouse.co/wp-content/uploads/2017/07/1024px-RoseSeidlerHouseSulmanPrize.jpg
How can I spot the difference?
The scenario is that I am using Firebase with Swift iOS front end to send these images to a Google Cloud ML-engine hosted convnet. Some images work all the time and certain others never work as above. Further, all images work when I use the gcloud versions predict CLI. To me the issue is necessarily something in the images. Hence I am posting here for the imaging group. Code is included as requested for completeness.
CODE of index.js file is included:
'use strict';
const functions = require('firebase-functions');
const gcs = require('#google-cloud/storage');
const admin = require('firebase-admin');
const exec = require('child_process').exec;
const path = require('path');
const fs = require('fs');
const google = require('googleapis');
const sizeOf = require('image-size');
admin.initializeApp(functions.config().firebase);
const db = admin.firestore();
const rtdb = admin.database();
const dbRef = rtdb.ref();
function cmlePredict(b64img) {
return new Promise((resolve, reject) => {
google.auth.getApplicationDefault(function (err, authClient) {
if (err) {
reject(err);
}
if (authClient.createScopedRequired && authClient.createScopedRequired()) {
authClient = authClient.createScoped([
'https://www.googleapis.com/auth/cloud-platform'
]);
}
var ml = google.ml({
version: 'v1'
});
const params = {
auth: authClient,
name: 'projects/myproject-18865/models/my_model',
resource: {
instances: [
{
"image_bytes": {
"b64": b64img
}
}
]
}
};
ml.projects.predict(params, (err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
});
});
});
}
function resizeImg(filepath) {
return new Promise((resolve, reject) => {
exec(`convert ${filepath} -resize 224x ${filepath}`, (err) => {
if (err) {
console.error('Failed to resize image', err);
reject(err);
} else {
console.log('resized image successfully');
resolve(filepath);
}
});
});
}
exports.runPrediction = functions.storage.object().onChange((event) => {
fs.rmdir('./tmp/', (err) => {
if (err) {
console.log('error deleting tmp/ dir');
}
});
const object = event.data;
const fileBucket = object.bucket;
const filePath = object.name;
const bucket = gcs().bucket(fileBucket);
const fileName = path.basename(filePath);
const file = bucket.file(filePath);
if (filePath.startsWith('images/')) {
const destination = '/tmp/' + fileName;
console.log('got a new image', filePath);
return file.download({
destination: destination
}).then(() => {
if(sizeOf(destination).width > 224) {
console.log('scaling image down...');
return resizeImg(destination);
} else {
return destination;
}
}).then(() => {
console.log('base64 encoding image...');
let bitmap = fs.readFileSync(destination);
return new Buffer(bitmap).toString('base64');
}).then((b64string) => {
console.log('sending image to CMLE...');
return cmlePredict(b64string);
}).then((result) => {
console.log(`results just returned and is: ${result}`);
let predict_proba = result.predictions[0]
const res_pred_val = Object.keys(predict_proba).map(k => predict_proba[k])
const res_val = Object.keys(result).map(k => result[k])
const class_proba = [1-res_pred_val,res_pred_val]
const opera_proba_init = 1-res_pred_val
const capitol_proba_init = res_pred_val-0
// convert fraction double to percentage int
let opera_proba = (Math.floor((opera_proba_init.toFixed(2))*100))|0
let capitol_proba = (Math.floor((capitol_proba_init.toFixed(2))*100))|0
let feature_list = ["houses", "trees"]
let outlinedImgPath = '';
let imageRef = db.collection('predicted_images').doc(filePath.slice(7));
outlinedImgPath = `outlined_img/${filePath.slice(7)}`;
imageRef.set({
image_path: outlinedImgPath,
opera_proba: opera_proba,
capitol_proba: capitol_proba
});
let predRef = dbRef.child("prediction_categories");
let arrayRef = dbRef.child("prediction_array");
predRef.set({
opera_proba: opera_proba,
capitol_proba: capitol_proba,
});
arrayRef.set({first: {
array_proba: [opera_proba,capitol_proba],
brief_description: ["a","b"],
more_details: ["aaaa","bbbb"],
feature_list: feature_list},
zummy1: "",
zummy2: ""});
return bucket.upload(destination, {destination: outlinedImgPath});
});
} else {
return 'not a new image';
}
});
Issue was that the bad images were grayscale, not RGB as expected by my model. I initially had checked this first by looking at the shape. But the 'bad' images had 3 color channels, each of those 3 channels stored the same number --- so my model was refusing to accept them. Also, as expected and contrary to what I initially thought I observed, turns out the gcloud ML-engine predict CLI actually also failed for these images. Took me 2 days to figure this out!