diff --git a/core/src/main/java/com/vzome/core/exporters/POVRayExporter.java b/core/src/main/java/com/vzome/core/exporters/POVRayExporter.java index ad8e80f75..1f0bb682c 100644 --- a/core/src/main/java/com/vzome/core/exporters/POVRayExporter.java +++ b/core/src/main/java/com/vzome/core/exporters/POVRayExporter.java @@ -23,6 +23,7 @@ import com.vzome.core.math.symmetry.Embedding; import com.vzome.core.render.RenderedManifestation; import com.vzome.core.viewing.CameraIntf; +import com.vzome.xml.ResourceLoader; /** * Renders out to POV-Ray using #declare statements to reuse geometry. @@ -67,18 +68,8 @@ public void doExport( File povFile, Writer writer, int height, int width ) throw output .println( "#declare parallel_proj = " + (mScene .isPerspective()?0:1) + ";" ); output .println(); - InputStream input = getClass() .getClassLoader() - .getResourceAsStream( PREAMBLE_FILE ); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - byte[] buf = new byte[1024]; - int num; - try { - while ( ( num = input .read( buf, 0, 1024 )) > 0 ) - out .write( buf, 0, num ); - } catch (IOException e) { - e.printStackTrace(); - } - output .println( new String( out .toByteArray() ) ); + String preamble = ResourceLoader.loadStringResource( PREAMBLE_FILE ); + output .println( preamble ); output .println(); for ( int i = 0; i<3; i++ ) { diff --git a/online/src/app/classic/menus/filemenu.jsx b/online/src/app/classic/menus/filemenu.jsx index 67ca6ff52..ef7f83f74 100644 --- a/online/src/app/classic/menus/filemenu.jsx +++ b/online/src/app/classic/menus/filemenu.jsx @@ -9,7 +9,7 @@ import { saveFileAs, openFile, saveTextFileAs, saveTextFile } from "../../../vie import { CommandAction, Divider, Menu, MenuAction, MenuItem, SubMenu } from "../../framework/menus.jsx"; import { UrlDialog } from '../dialogs/webloader.jsx' import { SvgPreviewDialog } from "../dialogs/svgpreview.jsx"; -import { useCamera } from "../../../viewer/context/camera.jsx"; +import { INITIAL_DISTANCE, useCamera } from "../../../viewer/context/camera.jsx"; import { useImageCapture } from "../../../viewer/context/export.jsx"; const queryParams = new URLSearchParams( window.location.search ); @@ -33,7 +33,7 @@ export const FileMenu = () => { const { rootController, state, setState, createDesign, openDesignFile, fetchDesignUrl, importMeshFile, guard, edited } = useEditor(); - const { state: cameraState } = useCamera(); + const { state: cameraState, mapViewToWorld } = useCamera(); const [ showDialog, setShowDialog ] = createSignal( false ); const fields = () => controllerProperty( rootController(), 'fields', 'fields', true ); @@ -98,7 +98,11 @@ export const FileMenu = () => const exportAs = ( extension, mimeType, format=extension, params={} ) => evt => { const camera = unwrap( cameraState.camera ); + camera .magnification = Math.log( camera.distance / INITIAL_DISTANCE ); + const lighting = unwrap( cameraState.lighting ); + lighting .directionalLights .forEach( light => light .worldDirection = mapViewToWorld( light.direction ) ); + controllerExportAction( rootController(), format, { camera, lighting, ...params } ) .then( text => { const name = (state.designName || 'untitled') .concat( "." + extension ); @@ -163,7 +167,7 @@ export const FileMenu = () => - + diff --git a/online/src/viewer/context/camera.jsx b/online/src/viewer/context/camera.jsx index 6feb4c231..553cf6c25 100644 --- a/online/src/viewer/context/camera.jsx +++ b/online/src/viewer/context/camera.jsx @@ -1,6 +1,6 @@ import { createContext, createEffect, useContext } from 'solid-js'; -import { PerspectiveCamera } from "three"; +import { PerspectiveCamera, Vector3 } from "three"; import { createStore } from 'solid-js/store'; @@ -154,6 +154,13 @@ const CameraProvider = ( props ) => } const trackballProps = { camera: trackballCamera, sync }; // no need (or desire) for reactivity here + const mapViewToWorld = ( [ x, y, z ] ) => + { + const vec = new Vector3( x, y, z ); + vec .transformDirection( trackballCamera.matrixWorldInverse ); + return [ vec.x, vec.y, vec.z ]; + } + const setCamera = loadedCamera => { setState( 'camera', loadedCamera ); @@ -179,7 +186,7 @@ const CameraProvider = ( props ) => const providerValue = { name: props.name, perspectiveProps, trackballProps, state, - resetCamera, setCamera, setLighting, togglePerspective, toggleOutlines, setDistance, + resetCamera, setCamera, setLighting, togglePerspective, toggleOutlines, setDistance, mapViewToWorld, }; // The perspectiveProps is used to initialize PerspectiveCamera in clients. diff --git a/online/src/worker/java/java/util/UUID.java b/online/src/worker/java/java/util/UUID.java index 578367f58..62cabc952 100644 --- a/online/src/worker/java/java/util/UUID.java +++ b/online/src/worker/java/java/util/UUID.java @@ -13,7 +13,7 @@ private UUID( String s ) public static UUID randomUUID() { - return new UUID( Double.toString( Math.random() ) ); + return new UUID( Double.toString( Math.random() ) .substring( 2 ) ); } public String toString() diff --git a/online/src/worker/legacy/core-java.js b/online/src/worker/legacy/core-java.js index 351fd68bd..e8211f115 100644 --- a/online/src/worker/legacy/core-java.js +++ b/online/src/worker/legacy/core-java.js @@ -11,7 +11,7 @@ import { java, javaemul } from "./candies/j4ts-2.1.0-SNAPSHOT/bundle.js" this.value = s; } static randomUUID() { - return new UUID(/* toString */ ('' + (Math.random()))); + return new UUID(/* toString */ ('' + (Math.random())).substring(2)); } toString() { return this.value; @@ -3952,10 +3952,6 @@ export var com; this.orbits = new com.vzome.core.math.symmetry.OrbitSet(symmetry); } /* Default method injected from com.vzome.core.editor.api.OrbitSource */ - getOrientations$() { - return this.getOrientations(false); - } - /* Default method injected from com.vzome.core.editor.api.OrbitSource */ getZone(orbit, orientation) { return this.getSymmetry().getDirection(orbit).getAxis(com.vzome.core.math.symmetry.Symmetry.PLUS, orientation); } @@ -3983,6 +3979,10 @@ export var com; return embedding; } /* Default method injected from com.vzome.core.editor.api.OrbitSource */ + getOrientations$() { + return this.getOrientations(false); + } + /* Default method injected from com.vzome.core.editor.api.OrbitSource */ getOrientations(rowMajor) { if (((typeof rowMajor === 'boolean') || rowMajor === null)) { let __args = arguments; @@ -16761,10 +16761,6 @@ export var com; this.setStyle(styleName); } /* Default method injected from com.vzome.core.editor.api.OrbitSource */ - getOrientations$() { - return this.getOrientations(false); - } - /* Default method injected from com.vzome.core.editor.api.OrbitSource */ getZone(orbit, orientation) { return this.getSymmetry().getDirection(orbit).getAxis(com.vzome.core.math.symmetry.Symmetry.PLUS, orientation); } @@ -16792,6 +16788,10 @@ export var com; return embedding; } /* Default method injected from com.vzome.core.editor.api.OrbitSource */ + getOrientations$() { + return this.getOrientations(false); + } + /* Default method injected from com.vzome.core.editor.api.OrbitSource */ getOrientations(rowMajor) { if (((typeof rowMajor === 'boolean') || rowMajor === null)) { let __args = arguments; @@ -36960,21 +36960,8 @@ export var com; this.output.println$(); this.output.println$java_lang_Object("#declare parallel_proj = " + (this.mScene.isPerspective() ? 0 : 1) + ";"); this.output.println$(); - const input = this.constructor.getClassLoader().getResourceAsStream(POVRayExporter.PREAMBLE_FILE); - const out = new java.io.ByteArrayOutputStream(); - const buf = (s => { let a = []; while (s-- > 0) - a.push(0); return a; })(1024); - let num; - try { - while (((num = input.read(buf, 0, 1024)) > 0)) { - out.write(buf, 0, num); - } - ; - } - catch (e) { - console.error(e.message, e); - } - this.output.println$java_lang_Object(new String(out.toByteArray())); + const preamble = com.vzome.xml.ResourceLoader.loadStringResource(POVRayExporter.PREAMBLE_FILE); + this.output.println$java_lang_Object(preamble); this.output.println$(); for (let i = 0; i < 3; i++) { { @@ -48051,10 +48038,6 @@ export var com; this.__parent = __parent; } /* Default method injected from com.vzome.core.editor.api.OrbitSource */ - getOrientations$() { - return this.getOrientations(false); - } - /* Default method injected from com.vzome.core.editor.api.OrbitSource */ getZone(orbit, orientation) { return this.getSymmetry().getDirection(orbit).getAxis(com.vzome.core.math.symmetry.Symmetry.PLUS, orientation); } @@ -48082,6 +48065,10 @@ export var com; return embedding; } /* Default method injected from com.vzome.core.editor.api.OrbitSource */ + getOrientations$() { + return this.getOrientations(false); + } + /* Default method injected from com.vzome.core.editor.api.OrbitSource */ getOrientations(rowMajor) { if (((typeof rowMajor === 'boolean') || rowMajor === null)) { let __args = arguments; diff --git a/online/src/worker/legacy/exporters.js b/online/src/worker/legacy/exporters.js index aeec13234..e7d158997 100644 --- a/online/src/worker/legacy/exporters.js +++ b/online/src/worker/legacy/exporters.js @@ -10,7 +10,7 @@ const exporterClasses = { 'off' : 'OffExporter', 'ply' : 'PlyExporter', 'vrml' : 'VRMLExporter', - 'pov' : 'PovRayExporter', + 'pov' : 'POVRayExporter', 'partgeom' : 'PartGeometryExporter', 'openscad' : 'OpenScadExporter', 'math' : 'MathTableExporter', @@ -29,44 +29,6 @@ const exporterClasses = { // 'FORMAT' : 'VefModelExporter', } -export const export3d = ( scene, configuration ) => -{ - const { format, height, width } = configuration; - const { renderedModel } = scene; - const exporter = new com.vzome.core.exporters[ exporterClasses[ format ] ](); - const out = new java.io.StringWriter(); - exporter .exportGeometry( renderedModel, null, out, height, width ); - return out.toString(); -} - -const createDocument = ( legacyDesign, camera, lighting ) => -{ - // TODO - return { - // CameraIntf getCameraModel(); - - // Lights getSceneLighting(); - - // RenderedModel getRenderedModel(); - - // ToolsModel getToolsModel(); - - // Element getDetailsXml( Document dom, boolean b ); - - // EditorModel getEditorModel(); - } -} - -export const export3dDocument = ( legacyDesign, camera, lighting, configuration ) => -{ - const { format, height, width } = configuration; - const exporter = new com.vzome.core.exporters[ exporterClasses[ format ] ](); - const out = new java.io.StringWriter(); - const document = createDocument( legacyDesign, camera, lighting ); - exporter .exportDocument( document, null, out, height, width ); - return out.toString(); -} - const parseColor = input => { const m = input .match( /^#([0-9a-f]{6})$/i )[1]; @@ -86,7 +48,9 @@ const createLights = lighting => const lights = new com.vzome.core.viewing.Lights(); lights .setBackgroundColor( parseColor( backgroundColor ) ); lights .setAmbientColor( parseColor( ambientColor ) ); - for ( const { direction: [x,y,z], color } of directionalLights ) { + for ( const { worldDirection: [x,y,z], color } of directionalLights ) { + // Because we are using worldDirection rather than direction, these vectors are in world coordinates already. + // Apparently, POVRayExporter is the only exporter that uses directional lights. lights .addDirectionLight( parseColor( color ), new com.vzome.core.math.RealVector( x, y, z ) ); } return lights; @@ -110,6 +74,49 @@ const createProjectionMatrix = ( camera, aspectRatio ) => return com.vzome.core.math.RealMatrix4.perspective( fovX, aspectRatio, near, far ); } +const createCamera = ( camera ) => +{ + const { distance, width, perspective, lookAt, up, lookDir, magnification } = camera; // This camera always comes from the client context + const halfX = width / 2; + const fov = 2 * Math.atan( halfX / distance ); + let [ x, y, z ] = lookAt; + const lookAtRV = new com.vzome.core.math.RealVector( x, y, z ); + [ x, y, z ] = up; + const upRV = new com.vzome.core.math.RealVector( x, y, z ); + [ x, y, z ] = lookDir; + const lookDirRV = new com.vzome.core.math.RealVector( x, y, z ); + return { + isPerspective: () => perspective, + getFieldOfView: () => fov, + getViewDistance: () => distance, + getMagnification: () => magnification, + getLookAtPointRV: () => lookAtRV, + getLookDirectionRV: () => lookDirRV, + getUpDirectionRV: () => upRV, + + // POVRayExporter will call this to map light directions to world coordinates, in the Java code, + // but here in Javascript our light directions are *already* in world coordinates. + mapViewToWorld: rv => rv, + } +} + +const createDocument = ( legacyDesign, camera, lighting ) => + { + const { renderedModel, editor, toolsModel } = legacyDesign; + const lights = createLights( lighting ); + const cameraModel = createCamera( camera ); + return { + getCameraModel: () => cameraModel, + getSceneLighting: () => lights, + getRenderedModel: () => renderedModel, + getToolsModel: () => toolsModel, + getEditorModel: () => editor, + getDetailsXml: ( dom, deep ) => null, // TODO: implement this so more exporters work + } + } + +////////////////////////////////////////////// main entry points: + export const export2d = ( scene, configuration ) => { const { format, height, width, useShapes, drawOutlines, monochrome, showBackground, useLighting } = configuration; @@ -123,4 +130,26 @@ export const export2d = ( scene, configuration ) => const out = new java.io.StringWriter(); exporter .export( snapshot, out, drawOutlines, monochrome, showBackground ); return out.toString(); -} \ No newline at end of file +} + +export const export3d = ( scene, configuration ) => + { + const { format, height, width } = configuration; + const { renderedModel } = scene; + const exporter = new com.vzome.core.exporters[ exporterClasses[ format ] ](); + const out = new java.io.StringWriter(); + exporter .exportGeometry( renderedModel, null, out, height, width ); + return out.toString(); + } + +export const export3dDocument = ( legacyDesign, camera, lighting, configuration ) => + { + const { format, height, width } = configuration; + const exporter = new com.vzome.core.exporters[ exporterClasses[ format ] ](); + const out = new java.io.StringWriter(); + // Satisfy the DocumentIntf contract required by DocumentExporter + const document = createDocument( legacyDesign, camera, lighting ); + exporter .exportDocument( document, null, out, height, width ); + return out.toString(); + } + \ No newline at end of file diff --git a/online/src/worker/legacy/ts/com/vzome/core/exporters/POVRayExporter.ts b/online/src/worker/legacy/ts/com/vzome/core/exporters/POVRayExporter.ts index a50f81d89..5d4b5fc31 100644 --- a/online/src/worker/legacy/ts/com/vzome/core/exporters/POVRayExporter.ts +++ b/online/src/worker/legacy/ts/com/vzome/core/exporters/POVRayExporter.ts @@ -48,16 +48,8 @@ namespace com.vzome.core.exporters { this.output.println$(); this.output.println$java_lang_Object("#declare parallel_proj = " + (this.mScene.isPerspective() ? 0 : 1) + ";"); this.output.println$(); - const input: java.io.InputStream = (this.constructor).getClassLoader().getResourceAsStream(POVRayExporter.PREAMBLE_FILE); - const out: java.io.ByteArrayOutputStream = new java.io.ByteArrayOutputStream(); - const buf: number[] = (s => { let a=[]; while(s-->0) a.push(0); return a; })(1024); - let num: number; - try { - while(((num = input.read(buf, 0, 1024)) > 0)) {out.write(buf, 0, num)}; - } catch(e) { - console.error(e.message, e); - } - this.output.println$java_lang_Object(new String(out.toByteArray())); + const preamble: string = com.vzome.xml.ResourceLoader.loadStringResource(POVRayExporter.PREAMBLE_FILE); + this.output.println$java_lang_Object(preamble); this.output.println$(); for(let i: number = 0; i < 3; i++) {{ const color: com.vzome.core.construction.Color = this.mLights.getDirectionalLightColor(i); diff --git a/online/src/worker/legacy/ts/core-java.ts b/online/src/worker/legacy/ts/core-java.ts index ffa35e8eb..43cd60a9e 100644 --- a/online/src/worker/legacy/ts/core-java.ts +++ b/online/src/worker/legacy/ts/core-java.ts @@ -57,7 +57,7 @@ namespace java.util { } public static randomUUID(): UUID { - return new UUID(/* toString */(''+(Math.random()))); + return new UUID(/* toString */(''+(Math.random())).substring(2)); } public toString(): string { @@ -4135,10 +4135,6 @@ namespace com.vzome.core.render { export namespace RenderedModel { export class SymmetryOrbitSource implements com.vzome.core.editor.api.OrbitSource { - /* Default method injected from com.vzome.core.editor.api.OrbitSource */ - getOrientations$(): number[][] { - return this.getOrientations(false); - } /* Default method injected from com.vzome.core.editor.api.OrbitSource */ getZone(orbit: string, orientation: number): com.vzome.core.math.symmetry.Axis { return this.getSymmetry().getDirection(orbit).getAxis(com.vzome.core.math.symmetry.Symmetry.PLUS, orientation); @@ -4163,6 +4159,10 @@ namespace com.vzome.core.render { return embedding; } /* Default method injected from com.vzome.core.editor.api.OrbitSource */ + getOrientations$(): number[][] { + return this.getOrientations(false); + } + /* Default method injected from com.vzome.core.editor.api.OrbitSource */ public getOrientations(rowMajor?: any): number[][] { if (((typeof rowMajor === 'boolean') || rowMajor === null)) { let __args = arguments; @@ -17037,10 +17037,6 @@ namespace com.vzome.core.editor { } namespace com.vzome.core.editor { export class SymmetrySystem implements com.vzome.core.editor.api.OrbitSource { - /* Default method injected from com.vzome.core.editor.api.OrbitSource */ - getOrientations$(): number[][] { - return this.getOrientations(false); - } /* Default method injected from com.vzome.core.editor.api.OrbitSource */ getZone(orbit: string, orientation: number): com.vzome.core.math.symmetry.Axis { return this.getSymmetry().getDirection(orbit).getAxis(com.vzome.core.math.symmetry.Symmetry.PLUS, orientation); @@ -17065,6 +17061,10 @@ namespace com.vzome.core.editor { return embedding; } /* Default method injected from com.vzome.core.editor.api.OrbitSource */ + getOrientations$(): number[][] { + return this.getOrientations(false); + } + /* Default method injected from com.vzome.core.editor.api.OrbitSource */ public getOrientations(rowMajor?: any): number[][] { if (((typeof rowMajor === 'boolean') || rowMajor === null)) { let __args = arguments; @@ -36209,16 +36209,8 @@ namespace com.vzome.core.exporters { this.output.println$(); this.output.println$java_lang_Object("#declare parallel_proj = " + (this.mScene.isPerspective() ? 0 : 1) + ";"); this.output.println$(); - const input: java.io.InputStream = (this.constructor).getClassLoader().getResourceAsStream(POVRayExporter.PREAMBLE_FILE); - const out: java.io.ByteArrayOutputStream = new java.io.ByteArrayOutputStream(); - const buf: number[] = (s => { let a=[]; while(s-->0) a.push(0); return a; })(1024); - let num: number; - try { - while(((num = input.read(buf, 0, 1024)) > 0)) {out.write(buf, 0, num)}; - } catch(e) { - console.error(e.message, e); - } - this.output.println$java_lang_Object(new String(out.toByteArray())); + const preamble: string = com.vzome.xml.ResourceLoader.loadStringResource(POVRayExporter.PREAMBLE_FILE); + this.output.println$java_lang_Object(preamble); this.output.println$(); for(let i: number = 0; i < 3; i++) {{ const color: com.vzome.core.construction.Color = this.mLights.getDirectionalLightColor(i); @@ -46085,10 +46077,6 @@ namespace com.vzome.core.edits { export class ReplaceWithShape$0 implements com.vzome.core.editor.api.OrbitSource { public __parent: any; /* Default method injected from com.vzome.core.editor.api.OrbitSource */ - getOrientations$(): number[][] { - return this.getOrientations(false); - } - /* Default method injected from com.vzome.core.editor.api.OrbitSource */ getZone(orbit: string, orientation: number): com.vzome.core.math.symmetry.Axis { return this.getSymmetry().getDirection(orbit).getAxis(com.vzome.core.math.symmetry.Symmetry.PLUS, orientation); } @@ -46112,6 +46100,10 @@ namespace com.vzome.core.edits { return embedding; } /* Default method injected from com.vzome.core.editor.api.OrbitSource */ + getOrientations$(): number[][] { + return this.getOrientations(false); + } + /* Default method injected from com.vzome.core.editor.api.OrbitSource */ public getOrientations(rowMajor?: any): number[][] { if (((typeof rowMajor === 'boolean') || rowMajor === null)) { let __args = arguments; diff --git a/online/src/worker/legacy/ts/java/util/UUID.ts b/online/src/worker/legacy/ts/java/util/UUID.ts index 1f0a0cc3d..7ad9fc4f6 100644 --- a/online/src/worker/legacy/ts/java/util/UUID.ts +++ b/online/src/worker/legacy/ts/java/util/UUID.ts @@ -9,7 +9,7 @@ namespace java.util { } public static randomUUID(): UUID { - return new UUID(/* toString */(''+(Math.random()))); + return new UUID(/* toString */(''+(Math.random())).substring(2)); } public toString(): string {