From 9c82d662b01f204501254f202757557fd6302609 Mon Sep 17 00:00:00 2001 From: Val Erastov Date: Sat, 30 Jul 2022 23:40:27 -0700 Subject: [PATCH] step file import support --- .../ui/components/controls/FileControl.tsx | 30 ++++++++- .../importModel/importModel.operation.ts | 65 ++++++++----------- package-lock.json | 14 ++-- package.json | 2 +- web/app/cad/craft/e0/OCI.d.ts | 2 +- web/app/cad/craft/e0/interact.d.ts | 3 + web/app/cad/craft/e0/interact.js | 8 +++ web/app/cad/craft/e0/occCommandInterface.ts | 1 + web/app/cad/mdf/ui/FileWidget.tsx | 21 +++++- 9 files changed, 96 insertions(+), 50 deletions(-) diff --git a/modules/ui/components/controls/FileControl.tsx b/modules/ui/components/controls/FileControl.tsx index d96b33d4..504b9034 100644 --- a/modules/ui/components/controls/FileControl.tsx +++ b/modules/ui/components/controls/FileControl.tsx @@ -3,7 +3,33 @@ import React from 'react'; export interface LocalFile { fileName: string; - content: string; + dataUrl: string; + +} + +export class LocalFileAdapter implements LocalFile { + + localFile: LocalFile; + + constructor(localFile: LocalFile) { + this.localFile = localFile; + } + + get fileName() { + return this.localFile.fileName; + } + + get dataUrl() { + return this.localFile.dataUrl; + } + + base64Content() { + return getBase64FromDataUrl(this.dataUrl); + } + + rawContent() { + return atob(getBase64FromDataUrl(this.dataUrl)); + } } export function getBase64FromDataUrl(dataUrl: string): string { @@ -29,7 +55,7 @@ export default class FileControl extends React.Component { const dataUrl = evt.target.result as string; onChange({ fileName: name, - content: dataUrl + dataUrl, }) }; reader.readAsDataURL(file); diff --git a/modules/workbenches/modeler/features/importModel/importModel.operation.ts b/modules/workbenches/modeler/features/importModel/importModel.operation.ts index c71631a8..06543361 100644 --- a/modules/workbenches/modeler/features/importModel/importModel.operation.ts +++ b/modules/workbenches/modeler/features/importModel/importModel.operation.ts @@ -1,19 +1,14 @@ -import { roundValueForPresentation as r } from 'cad/craft/operationHelper'; -import { ApplicationContext } from "context"; -import { EntityKind } from "cad/model/entities"; -import { BooleanDefinition } from "cad/craft/schema/common/BooleanDefinition"; -import { OperationDescriptor } from "cad/craft/operationPlugin"; -import { param } from 'cypress/types/jquery'; -import { MObject } from 'cad/model/mobject'; -import { LocalFile } from "ui/components/controls/FileControl"; +import {ApplicationContext} from "context"; +import {OperationDescriptor} from "cad/craft/operationPlugin"; +import {LocalFile, LocalFileAdapter} from "ui/components/controls/FileControl"; import CadError from "../../../../../web/app/utils/errors"; -import { parseStringPromise } from 'xml2js'; -import * as jszip from "jszip"; -import { FcElectricalSensor } from 'react-icons/fc'; - +import {parseStringPromise} from 'xml2js'; +import {importStepFile} from "cad/craft/e0/interact"; +import {clone} from "gems/objects"; +import JSZip from "jszip"; interface ImportModelParams { - file: LocalFile; + file: LocalFileAdapter; } export const ImportModelOpperation: OperationDescriptor = { @@ -31,35 +26,31 @@ export const ImportModelOpperation: OperationDescriptor = { const FileName = params.file.fileName.toUpperCase(); - let fileToRead = await atob(await params.file.content.slice(await params.file.content.indexOf(',') + 1)); + let rawContent = params.file.rawContent(); //console.log(params.file.content); - //console.log(fileToRead); + //console.log(rawContent); if (FileName.endsWith("BRP") || FileName.endsWith("BREP")) { //FreeCAD some times omits this text from the top of BRP files //as part of the brp files stored in the .FCStf file archive format - if (!fileToRead.startsWith("DBRep_DrawableShape")) { - fileToRead = `DBRep_DrawableShape\n` + fileToRead; + if (!rawContent.startsWith("DBRep_DrawableShape")) { + rawContent = `DBRep_DrawableShape\n` + rawContent; } - FS.writeFile("newBREPobject", (fileToRead)); + FS.writeFile("newBREPobject", rawContent); oci.readbrep("newBREPobject", "newBREPobject"); returnObject.created.push(occ.io.getShell("newBREPobject")); } else if (FileName.endsWith("FCSTD")) { - var JSZip = require("jszip"); - const zipContents = await (await JSZip.loadAsync(btoa(fileToRead), { base64: true })).files; - var xmlFreeCADData = await zipContents["Document.xml"].async("string"); - - let DecodedXmlFreeCADData = (JSON.parse(JSON.stringify(await parseStringPromise(xmlFreeCADData)))).Document.ObjectData[0].Object; - //console.log(DecodedXmlFreeCADData); + const zipContents = (await JSZip.loadAsync(params.file.base64Content(), { base64: true })).files; + const xmlFreeCADData = await zipContents["Document.xml"].async("string"); + let DecodedXmlFreeCADData = (clone(await parseStringPromise(xmlFreeCADData))).Document.ObjectData[0].Object; for (const itemToLookAt in DecodedXmlFreeCADData) { const flattenedObject = flattenJSON(DecodedXmlFreeCADData[itemToLookAt]); let importBrepFlag = false; let importBrepShapeName = ""; - let visiblePropertyName = ""; for (const propertyToLookAt in flattenedObject) { //console.log(propertyToLookAt + " = " + flattenedObject[propertyToLookAt]); importBrepFlag = false; @@ -71,12 +62,13 @@ export const ImportModelOpperation: OperationDescriptor = { console.log(shouldItImport, importBrepShapeName); if (shouldItImport == "true") { try { - await FS.writeFile(importBrepShapeName, `DBRep_DrawableShape\n` + await zipContents[importBrepShapeName].async("string")); + const zipContent = await zipContents[importBrepShapeName].async("string"); + await FS.writeFile(importBrepShapeName, `DBRep_DrawableShape\n` + zipContent); await oci.readbrep(importBrepShapeName, importBrepShapeName); returnObject.created.push(occ.io.getShell(importBrepShapeName)); - console.log(importBrepShapeName); + console.debug(importBrepShapeName); } catch (e) { - console.log(e) + console.warn(e) } } } @@ -87,14 +79,14 @@ export const ImportModelOpperation: OperationDescriptor = { } else if (FileName.endsWith("STEP") || FileName.endsWith("STP")) { //step Import - FS.writeFile("newStepObject", (params.file.content)); - oci.stepread("newStepObject", "newStepObject"); + FS.writeFile("newStepFile", rawContent); + importStepFile("newStepObject", "newStepFile", true); returnObject.created.push(occ.io.getShell("newStepObject")); } else if (FileName.endsWith("IGES") || FileName.endsWith("IGS")) { //IGES import - FS.writeFile("newIgesObject", (params.file.content)); + FS.writeFile("newIgesObject", rawContent); oci.igesread("newIgesObject", "newIgesObject"); returnObject.created.push(occ.io.getShell("newIgesObject")); @@ -103,14 +95,11 @@ export const ImportModelOpperation: OperationDescriptor = { kind: CadError.KIND.INVALID_INPUT, code: 'File type not supported at this time' }); - - } - return returnObject; - }, + form: [ { type: 'file', @@ -122,12 +111,12 @@ export const ImportModelOpperation: OperationDescriptor = { } const flattenJSON = (obj = {}, res = {}, extraKey = '') => { - for (key in obj) { + for (let key of Object.keys(obj)) { if (typeof obj[key] !== 'object') { res[extraKey + key] = obj[key]; } else { flattenJSON(obj[key], res, `${extraKey}${key}.`); - }; - }; + } + } return res; }; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 13488c0e..6891a2da 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ "earcut": "2.1.1", "font-awesome": "4.7.0", "immer": "^9.0.12", - "jsketcher-occ-engine": "1.0.1-055e9551d187d751bf4850255121de8971c1c7bc", + "jsketcher-occ-engine": "1.0.1-9e267e3f1d283ebaa7cb1d5d11b529823e00b360", "jszip": "^3.10.0", "less": "^3.11.1", "libtess": "1.2.2", @@ -9164,9 +9164,9 @@ } }, "node_modules/jsketcher-occ-engine": { - "version": "1.0.1-055e9551d187d751bf4850255121de8971c1c7bc", - "resolved": "https://registry.npmjs.org/jsketcher-occ-engine/-/jsketcher-occ-engine-1.0.1-055e9551d187d751bf4850255121de8971c1c7bc.tgz", - "integrity": "sha512-Uqf5cJovjdBXfoKuL+hquiWQAr7OP4AeVWeIdHYVMXkMpuZW01BjTkhKucyrwV7odWpDrzZsJrDtoYCjS6X2yw==" + "version": "1.0.1-9e267e3f1d283ebaa7cb1d5d11b529823e00b360", + "resolved": "https://registry.npmjs.org/jsketcher-occ-engine/-/jsketcher-occ-engine-1.0.1-9e267e3f1d283ebaa7cb1d5d11b529823e00b360.tgz", + "integrity": "sha512-Eyj1ooaO+BqWLtM9GHHJ7GP18r23p9pCcHc437L8XmCBSEEVXgvLG7Hwr0FZUYSaszdevmHmQVEkuYlTPXYLmg==" }, "node_modules/json-parse-better-errors": { "version": "1.0.2", @@ -23437,9 +23437,9 @@ "dev": true }, "jsketcher-occ-engine": { - "version": "1.0.1-055e9551d187d751bf4850255121de8971c1c7bc", - "resolved": "https://registry.npmjs.org/jsketcher-occ-engine/-/jsketcher-occ-engine-1.0.1-055e9551d187d751bf4850255121de8971c1c7bc.tgz", - "integrity": "sha512-Uqf5cJovjdBXfoKuL+hquiWQAr7OP4AeVWeIdHYVMXkMpuZW01BjTkhKucyrwV7odWpDrzZsJrDtoYCjS6X2yw==" + "version": "1.0.1-9e267e3f1d283ebaa7cb1d5d11b529823e00b360", + "resolved": "https://registry.npmjs.org/jsketcher-occ-engine/-/jsketcher-occ-engine-1.0.1-9e267e3f1d283ebaa7cb1d5d11b529823e00b360.tgz", + "integrity": "sha512-Eyj1ooaO+BqWLtM9GHHJ7GP18r23p9pCcHc437L8XmCBSEEVXgvLG7Hwr0FZUYSaszdevmHmQVEkuYlTPXYLmg==" }, "json-parse-better-errors": { "version": "1.0.2", diff --git a/package.json b/package.json index 665373c5..1764d2f3 100644 --- a/package.json +++ b/package.json @@ -71,7 +71,7 @@ "earcut": "2.1.1", "font-awesome": "4.7.0", "immer": "^9.0.12", - "jsketcher-occ-engine": "1.0.1-055e9551d187d751bf4850255121de8971c1c7bc", + "jsketcher-occ-engine": "1.0.1-9e267e3f1d283ebaa7cb1d5d11b529823e00b360", "jszip": "^3.10.0", "less": "^3.11.1", "libtess": "1.2.2", diff --git a/web/app/cad/craft/e0/OCI.d.ts b/web/app/cad/craft/e0/OCI.d.ts index 57cac4cf..cc63afce 100644 --- a/web/app/cad/craft/e0/OCI.d.ts +++ b/web/app/cad/craft/e0/OCI.d.ts @@ -1,4 +1,4 @@ -interface OCCCommands { +export interface OCCCommands { /* 2dbeziercurve name nbpole pole, [weight] */ diff --git a/web/app/cad/craft/e0/interact.d.ts b/web/app/cad/craft/e0/interact.d.ts index ba211b91..739fe773 100644 --- a/web/app/cad/craft/e0/interact.d.ts +++ b/web/app/cad/craft/e0/interact.d.ts @@ -19,3 +19,6 @@ export function UpdateTessellation(shapePtr: number, deflection: number): number export function SetLocation(shapeName: string, matrixArray: number[]); export function AddLocation(shapeName: string, matrixArray: number[]); + +export function importStepFile(shapeName: string, fileName: string, oneOnly: boolean): number; + diff --git a/web/app/cad/craft/e0/interact.js b/web/app/cad/craft/e0/interact.js index c187ab73..7695bc91 100644 --- a/web/app/cad/craft/e0/interact.js +++ b/web/app/cad/craft/e0/interact.js @@ -128,3 +128,11 @@ export function AddLocation(shapeName, matrixArray) { ); _free(shapeNamePtr); } + +export function importStepFile(shapeName, fileName, oneOnly) { + const shapeNamePtr = toCString(shapeName); + const fileNamePtr = toCString(fileName); + Module._ImportStepFile(shapeNamePtr, fileNamePtr, oneOnly); + _free(shapeNamePtr); + _free(fileName); +} diff --git a/web/app/cad/craft/e0/occCommandInterface.ts b/web/app/cad/craft/e0/occCommandInterface.ts index 77b72185..91c5e47f 100644 --- a/web/app/cad/craft/e0/occCommandInterface.ts +++ b/web/app/cad/craft/e0/occCommandInterface.ts @@ -1,5 +1,6 @@ import {CallCommand} from "cad/craft/e0/interact"; import {MObject} from "cad/model/mobject"; +import {OCCCommands} from "cad/craft/e0/OCI"; export type OCCCommandInterface = OCCCommands; diff --git a/web/app/cad/mdf/ui/FileWidget.tsx b/web/app/cad/mdf/ui/FileWidget.tsx index 353bf406..a13388b9 100644 --- a/web/app/cad/mdf/ui/FileWidget.tsx +++ b/web/app/cad/mdf/ui/FileWidget.tsx @@ -2,6 +2,8 @@ import {FileField} from "cad/craft/wizard/components/form/Fields"; import React from "react"; import {FieldBasicProps, fieldToSchemaGeneric} from "cad/mdf/ui/field"; import {Types} from "cad/craft/schema/types"; +import {LocalFile, LocalFileAdapter} from "ui/components/controls/FileControl"; +import {ValueResolver} from "cad/craft/schema/schema"; export interface FileWidgetProps extends FieldBasicProps { @@ -14,18 +16,35 @@ export function FileWidget(props: FileWidgetProps) { } FileWidget.propsToSchema = (props: FileWidgetProps) => { + return { type: Types.object, schema: { fileName: { type: 'string' }, - content: { + dataUrl: { type: 'string' } }, ...fieldToSchemaGeneric(props), + + resolve: chainResolver( + (ctx, localFile: LocalFile) => new LocalFileAdapter(localFile), + props.resolve + ) } }; +function chainResolver(...resolvers: ValueResolver[]): ValueResolver { + return (ctx, value, md, reportH) => { + let updatedValue = value; + resolvers.forEach(resolver => { + if (resolver) { + updatedValue = resolver(ctx, updatedValue, md, reportH); + } + }); + return updatedValue; + } +}