New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@ae-studio/dxf-viewer

Package Overview
Dependencies
Maintainers
0
Versions
23
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@ae-studio/dxf-viewer - npm Package Compare versions

Comparing version 1.1.1 to 1.1.2

2

package.json
{
"name": "@ae-studio/dxf-viewer",
"version": "1.1.1",
"version": "1.1.2",
"description": "JavaScript DXF file viewer",

@@ -5,0 +5,0 @@ "main": "src/index.js",

@@ -1,229 +0,236 @@

import * as three from "three"
import {BatchingKey} from "./BatchingKey"
import {DxfWorker} from "./DxfWorker"
import {MaterialKey} from "./MaterialKey"
import {ColorCode, DxfScene} from "./DxfScene"
import {OrbitControls} from "./OrbitControls"
import {RBTree} from "./RBTree"
import * as three from 'three';
import { BatchingKey } from './BatchingKey';
import { DxfWorker } from './DxfWorker';
import { MaterialKey } from './MaterialKey';
import { ColorCode, DxfScene } from './DxfScene';
import { OrbitControls } from './OrbitControls';
import { RBTree } from './RBTree';
/** Level in "message" events. */
const MessageLevel = Object.freeze({
INFO: "info",
WARN: "warn",
ERROR: "error"
})
INFO: 'info',
WARN: 'warn',
ERROR: 'error',
});
/** The representation class for the viewer, based on Three.js WebGL renderer. */
export class DxfViewer {
/** @param domContainer Container element to create the canvas in. Usually empty div. Should not
* have padding if auto-resize feature is used.
* @param options Some options can be overridden if specified. See DxfViewer.DefaultOptions.
*/
constructor(domContainer, options = null) {
this.sceneData = null;
this.n = 0;
this.domContainer = domContainer;
this.options = Object.create(DxfViewer.DefaultOptions);
this.objects = new Map();
if (options) {
Object.assign(this.options, options);
}
options = this.options;
/** @param domContainer Container element to create the canvas in. Usually empty div. Should not
* have padding if auto-resize feature is used.
* @param options Some options can be overridden if specified. See DxfViewer.DefaultOptions.
*/
constructor(domContainer, options = null) {
this.sceneData = null
this.n = 0;
this.domContainer = domContainer
this.options = Object.create(DxfViewer.DefaultOptions)
this.objects = new Map()
if (options) {
Object.assign(this.options, options)
}
options = this.options
this.clearColor = this.options.clearColor.getHex();
this.clearColor = this.options.clearColor.getHex()
this.scene = new three.Scene();
this.scene = new three.Scene()
try {
this.renderer = new three.WebGLRenderer({
alpha: options.canvasAlpha,
premultipliedAlpha: options.canvasPremultipliedAlpha,
antialias: options.antialias,
depth: false,
preserveDrawingBuffer: options.preserveDrawingBuffer,
});
try {
this.renderer = new three.WebGLRenderer({
alpha: options.canvasAlpha,
premultipliedAlpha: options.canvasPremultipliedAlpha,
antialias: options.antialias,
depth: false,
preserveDrawingBuffer: options.preserveDrawingBuffer
})
globalThis.renderer = this.renderer
} catch (e) {
console.log("Failed to create renderer: " + e)
this.renderer = null
return
}
const renderer = this.renderer
/* Prevent bounding spheres calculations which fails due to non-conventional geometry
* buffers layout. Also do not waste CPU on sorting which we do not need anyway.
*/
renderer.sortObjects = false
renderer.setPixelRatio(window.devicePixelRatio)
globalThis.renderer = this.renderer;
} catch (e) {
console.log('Failed to create renderer: ' + e);
this.renderer = null;
return;
}
const renderer = this.renderer;
/* Prevent bounding spheres calculations which fails due to non-conventional geometry
* buffers layout. Also do not waste CPU on sorting which we do not need anyway.
*/
renderer.sortObjects = false;
renderer.setPixelRatio(window.devicePixelRatio);
const camera = this.camera = new three.OrthographicCamera(-1, 1, 1, -1, 0.1, 2);
camera.position.z = 1
camera.position.x = 0
camera.position.y = 0
const camera = (this.camera = new three.OrthographicCamera(-1, 1, 1, -1, 0.1, 2));
camera.position.z = 1;
camera.position.x = 0;
camera.position.y = 0;
globalThis.camera = camera
globalThis.scene = this.scene
globalThis.camera = camera;
globalThis.scene = this.scene;
this.simpleColorMaterial = []
this.simplePointMaterial = []
for (let i = 0; i < InstanceType.MAX; i++) {
this.simpleColorMaterial[i] = this._CreateSimpleColorMaterial(i)
this.simplePointMaterial[i] = this._CreateSimplePointMaterial(i)
}
this.simpleColorMaterial = [];
this.simplePointMaterial = [];
for (let i = 0; i < InstanceType.MAX; i++) {
this.simpleColorMaterial[i] = this._CreateSimpleColorMaterial(i);
this.simplePointMaterial[i] = this._CreateSimplePointMaterial(i);
}
renderer.setClearColor(options.clearColor, options.clearAlpha)
renderer.setClearColor(options.clearColor, options.clearAlpha);
if (options.autoResize) {
this.canvasWidth = domContainer.clientWidth
this.canvasHeight = domContainer.clientHeight
domContainer.style.position = "relative"
} else {
this.canvasWidth = options.canvasWidth
this.canvasHeight = options.canvasHeight
this.resizeObserver = null
}
renderer.setSize(this.canvasWidth, this.canvasHeight)
if (options.autoResize) {
this.canvasWidth = domContainer.clientWidth;
this.canvasHeight = domContainer.clientHeight;
domContainer.style.position = 'relative';
} else {
this.canvasWidth = options.canvasWidth;
this.canvasHeight = options.canvasHeight;
this.resizeObserver = null;
}
renderer.setSize(this.canvasWidth, this.canvasHeight);
this.canvas = renderer.domElement
domContainer.style.display = "block"
if (options.autoResize) {
this.canvas.style.position = "absolute"
this.resizeObserver = new ResizeObserver(entries => this._OnResize(entries[0]))
this.resizeObserver.observe(domContainer)
}
domContainer.appendChild(this.canvas)
this.canvas = renderer.domElement;
domContainer.style.display = 'block';
if (options.autoResize) {
this.canvas.style.position = 'absolute';
this.resizeObserver = new ResizeObserver((entries) => this._OnResize(entries[0]));
this.resizeObserver.observe(domContainer);
}
domContainer.appendChild(this.canvas);
this.canvas.addEventListener("click", this._OnPointerEvent.bind(this))
// this.canvas.addEventListener("pointerup", this._OnPointerEvent.bind(this))
this.canvas.addEventListener('click', this._OnPointerEvent.bind(this));
// this.canvas.addEventListener("pointerup", this._OnPointerEvent.bind(this))
this.Render()
this.Render();
/* Indexed by MaterialKey, value is {key, material}. */
this.materials = new RBTree((m1, m2) => m1.key.Compare(m2.key))
/* Indexed by layer name, value is Layer instance. */
this.layers = new Map()
/* Indexed by block name, value is Block instance. */
this.blocks = new Map()
/* Indexed by MaterialKey, value is {key, material}. */
this.materials = new RBTree((m1, m2) => m1.key.Compare(m2.key));
/* Indexed by layer name, value is Layer instance. */
this.layers = new Map();
/* Indexed by block name, value is Block instance. */
this.blocks = new Map();
/** Set during data loading. */
this.worker = null
}
/** Set during data loading. */
this.worker = null;
}
/** @return {boolean} True if renderer exists. May be false in case when WebGL context is lost
* (e.g. after wake up from sleep). In such case page should be reloaded.
*/
HasRenderer() {
return Boolean(this.renderer)
}
/** @return {boolean} True if renderer exists. May be false in case when WebGL context is lost
* (e.g. after wake up from sleep). In such case page should be reloaded.
*/
HasRenderer() {
return Boolean(this.renderer);
}
/**
* @returns {three.WebGLRenderer | null} Returns the created Three.js renderer.
*/
GetRenderer(){
return this.renderer;
}
/**
* @returns {three.WebGLRenderer | null} Returns the created Three.js renderer.
*/
GetRenderer() {
return this.renderer;
}
GetCanvas() {
return this.canvas
}
GetCanvas() {
return this.canvas;
}
GetDxf() {
return this.parsedDxf
}
GetDxf() {
return this.parsedDxf;
}
GetControls() {
return this.controls
}
GetControls() {
return this.controls;
}
SetSize(width, height) {
this._EnsureRenderer()
SetSize(width, height) {
this._EnsureRenderer();
const hScale = width / this.canvasWidth
const vScale = height / this.canvasHeight
const hScale = width / this.canvasWidth;
const vScale = height / this.canvasHeight;
const cam = this.camera
const centerX = (cam.left + cam.right) / 2
const centerY = (cam.bottom + cam.top) / 2
const camWidth = cam.right - cam.left
const camHeight = cam.top - cam.bottom
cam.left = centerX - hScale * camWidth / 2
cam.right = centerX + hScale * camWidth / 2
cam.bottom = centerY - vScale * camHeight / 2
cam.top = centerY + vScale * camHeight / 2
cam.updateProjectionMatrix()
const cam = this.camera;
const centerX = (cam.left + cam.right) / 2;
const centerY = (cam.bottom + cam.top) / 2;
const camWidth = cam.right - cam.left;
const camHeight = cam.top - cam.bottom;
cam.left = centerX - (hScale * camWidth) / 2;
cam.right = centerX + (hScale * camWidth) / 2;
cam.bottom = centerY - (vScale * camHeight) / 2;
cam.top = centerY + (vScale * camHeight) / 2;
cam.updateProjectionMatrix();
this.canvasWidth = width
this.canvasHeight = height
this.renderer.setSize(width, height)
if (this.controls) {
this.controls.update()
}
this._Emit("resized", {width, height})
this._Emit("viewChanged")
this.Render()
this.canvasWidth = width;
this.canvasHeight = height;
this.renderer.setSize(width, height);
if (this.controls) {
this.controls.update();
}
this._Emit('resized', { width, height });
this._Emit('viewChanged');
this.Render();
}
/** Load DXF into the viewer. Old content is discarded, state is reset.
* @param url {string} DXF file URL.
* @param fonts {?string[]} List of font URLs. Files should have typeface.js format. Fonts are
* used in the specified order, each one is checked until necessary glyph is found. Text is not
* rendered if fonts are not specified.
* @param progressCbk {?Function} (phase, processedSize, totalSize)
* Possible phase values:
* * "font"
* * "fetch"
* * "parse"
* * "prepare"
* @param workerFactory {?Function} Factory for worker creation. The worker script should
* invoke DxfViewer.SetupWorker() function.
*/
async Load({url, fonts = null, progressCbk = null, workerFactory = null}) {
if (url === null || url === undefined) {
throw new Error("`url` parameter is not specified")
}
/** Load DXF into the viewer. Old content is discarded, state is reset.
* @param url {string} DXF file URL.
* @param fonts {?string[]} List of font URLs. Files should have typeface.js format. Fonts are
* used in the specified order, each one is checked until necessary glyph is found. Text is not
* rendered if fonts are not specified.
* @param progressCbk {?Function} (phase, processedSize, totalSize)
* Possible phase values:
* * "font"
* * "fetch"
* * "parse"
* * "prepare"
* @param workerFactory {?Function} Factory for worker creation. The worker script should
* invoke DxfViewer.SetupWorker() function.
*/
async Load({ url, fonts = null, progressCbk = null, workerFactory = null }) {
if (url === null || url === undefined) {
throw new Error('`url` parameter is not specified');
}
this._EnsureRenderer()
this._EnsureRenderer();
this.Clear()
this.Clear();
this.worker = new DxfWorker(workerFactory ? workerFactory() : null)
const {scene, sceneData, dxf} = await this.worker.Load(url, fonts, this.options, progressCbk)
await this.worker.Destroy()
this.worker = null
this.parsedDxf = dxf
this.worker = new DxfWorker(workerFactory ? workerFactory() : null);
const { scene, sceneData, dxf } = await this.worker.Load(url, fonts, this.options, progressCbk);
await this.worker.Destroy();
this.worker = null;
this.parsedDxf = dxf;
this.origin = scene.origin
this.bounds = scene.bounds
this.hasMissingChars = scene.hasMissingChars
this.origin = scene.origin;
this.bounds = scene.bounds;
this.hasMissingChars = scene.hasMissingChars;
this.sceneData = sceneData
this.sceneData = sceneData;
for (const layer of scene.layers) {
this.layers.set(layer.name, new Layer(layer.name, layer.displayName, layer.color))
}
for (const layer of scene.layers) {
this.layers.set(layer.name, new Layer(layer.name, layer.displayName, layer.color));
}
/* Load all blocks on the first pass. */
for (const batch of scene.batches) {
if (batch.key.blockName !== null &&
batch.key.geometryType !== BatchingKey.GeometryType.BLOCK_INSTANCE &&
batch.key.geometryType !== BatchingKey.GeometryType.POINT_INSTANCE) {
/* Load all blocks on the first pass. */
for (const batch of scene.batches) {
if (
batch.key.blockName !== null &&
batch.key.geometryType !== BatchingKey.GeometryType.BLOCK_INSTANCE &&
batch.key.geometryType !== BatchingKey.GeometryType.POINT_INSTANCE
) {
let block = this.blocks.get(batch.key.blockName);
let block = this.blocks.get(batch.key.blockName)
if (!block) {
block = new Block()
this.blocks.set(batch.key.blockName, block)
}
if (!block) {
block = new Block();
this.blocks.set(batch.key.blockName, block);
}
const positions = this.GetBatchPositions(batch.key.blockName, scene)
const positions = this.GetBatchPositions(batch.key.blockName, scene);
block.PushBatch(new Batch(this, scene, {
...batch,
positions,
}, batch.key.blockName))
}
}
block.PushBatch(
new Batch(
this,
scene,
{
...batch,
positions,
},
batch.key.blockName,
),
);
}
}
console.log(`DXF scene:
console.log(`DXF scene:
${scene.batches.length} batches,

@@ -234,609 +241,622 @@ ${this.layers.size} layers,

indices ${scene.indices.byteLength} B
transforms ${scene.transforms.byteLength} B`)
transforms ${scene.transforms.byteLength} B`);
/* Instantiate all entities. */
for (const batch of scene.batches) {
this._LoadBatch(scene, batch)
}
/* Instantiate all entities. */
for (const batch of scene.batches) {
this._LoadBatch(scene, batch);
}
const bypass = true
const bypass = true;
// console.log('sceneData.insertBlocks', )
// console.log('sceneData.insertBlocks', )
//XXX line type
//XXX line type
for (const [key, { batches }] of sceneData.insertBlocks.entries()) {
const handledObjects = [];
for (const [key, { batches }] of sceneData.insertBlocks.entries()) {
const handledObjects = []
for (const batch of batches) {
if (batch.chunks) {
for (const chunk of batch.chunks) {
const object = this.CreateViewerObject({
batch,
indices: chunk.indices,
vertices: chunk.vertices,
});
for (const batch of batches) {
if (batch.chunks) {
for (const chunk of batch.chunks) {
const object = this.CreateViewerObject({
batch,
indices: chunk.indices,
vertices: chunk.vertices,
})
handledObjects.push(object);
}
} else {
const object = this.CreateViewerObject({
batch,
indices: null,
vertices: batch.vertices,
});
handledObjects.push(object)
}
} else {
const object = this.CreateViewerObject({
batch,
indices: null,
vertices: batch.vertices,
})
handledObjects.push(object)
}
}
this.objects.set(key, handledObjects)
handledObjects.push(object);
}
for (const [key, { batches }] of this.blocks.entries()) {
if (this.objects.has(key)) {
continue
}
}
const block = sceneData.blocks.get(key);
this.objects.set(key, handledObjects);
}
const handledObjects = []
for (const [key, { batches }] of this.blocks.entries()) {
if (this.objects.has(key)) {
continue;
}
for (const batch of batches) {
if (batch.chunks) {
for (const chunk of batch.chunks) {
const object = this.CreateViewerObject({
batch,
indices: chunk.indices,
vertices: chunk.vertices,
})
const block = sceneData.blocks ? sceneData.blocks.get(key) : undefined;
const handledObjects = [];
object.userData.block = block;
for (const batch of batches) {
if (batch.chunks) {
for (const chunk of batch.chunks) {
const object = this.CreateViewerObject({
batch,
indices: chunk.indices,
vertices: chunk.vertices,
});
handledObjects.push(object)
}
} else {
if (batch.positions.size === 0) {
const object = this.CreateViewerObject({
batch,
indices: null,
vertices: batch.vertices,
})
object.userData.block = block;
object.userData.block = block;
handledObjects.push(object);
}
} else {
if (batch.positions.size === 0) {
const object = this.CreateViewerObject({
batch,
indices: null,
vertices: batch.vertices,
});
handledObjects.push(object)
object.userData.block = block;
continue;
}
handledObjects.push(object);
const [key, { block: positionBlock }] = Array.from(batch.positions)[0];
const blockBatch = positionBlock.batches[0];
continue;
}
if (blockBatch.chunks) {
for (const chunk of blockBatch.chunks) {
const object = this.CreateViewerObject({
batch,
indices: chunk.indices,
vertices: chunk.vertices,
})
const [_key, { block: positionBlock }] = Array.from(batch.positions)[0];
const blockBatch = positionBlock.batches[0];
object.userData.block = block;
if (blockBatch.chunks) {
for (const chunk of blockBatch.chunks) {
const object = this.CreateViewerObject({
batch,
indices: chunk.indices,
vertices: chunk.vertices,
});
handledObjects.push(object)
}
} else {
const object = this.CreateViewerObject({
batch,
indices: null,
vertices: blockBatch.vertices,
})
object.userData.block = block;
object.userData.block = block;
handledObjects.push(object)
}
}
handledObjects.push(object);
}
} else {
const object = this.CreateViewerObject({
batch,
indices: null,
vertices: blockBatch.vertices,
});
this.objects.set(key, handledObjects)
object.userData.block = block;
handledObjects.push(object);
}
}
}
this._Emit("loaded")
this.objects.set(key, handledObjects);
}
if (scene.bounds) {
this.FitView(scene.bounds.minX - scene.origin.x, scene.bounds.maxX - scene.origin.x,
scene.bounds.minY - scene.origin.y, scene.bounds.maxY - scene.origin.y)
} else {
this._Message("Empty document", MessageLevel.WARN)
}
this._Emit('loaded');
if (this.hasMissingChars) {
this._Message("Some characters cannot be properly displayed due to missing fonts",
MessageLevel.WARN)
}
if (scene.bounds) {
this.FitView(
scene.bounds.minX - scene.origin.x,
scene.bounds.maxX - scene.origin.x,
scene.bounds.minY - scene.origin.y,
scene.bounds.maxY - scene.origin.y,
);
} else {
this._Message('Empty document', MessageLevel.WARN);
}
this._CreateControls()
this.Render()
if (this.hasMissingChars) {
this._Message(
'Some characters cannot be properly displayed due to missing fonts',
MessageLevel.WARN,
);
}
CreateViewerObject({
batch,
indices,
vertices,
}) {
const color = batch.key.color
const materialFactory =
batch.key.geometryType === BatchingKey.GeometryType.POINTS ||
batch.key.geometryType === BatchingKey.GeometryType.POINT_INSTANCE ?
this._GetSimplePointMaterial : this._GetSimpleColorMaterial
this._CreateControls();
this.Render();
}
const material = materialFactory.call(this, this._TransformColor(color), InstanceType.NONE)
CreateViewerObject({ batch, indices, vertices }) {
const color = batch.key.color;
const materialFactory =
batch.key.geometryType === BatchingKey.GeometryType.POINTS ||
batch.key.geometryType === BatchingKey.GeometryType.POINT_INSTANCE
? this._GetSimplePointMaterial
: this._GetSimpleColorMaterial;
let objConstructor
switch (batch.key.geometryType) {
case BatchingKey.GeometryType.POINTS:
/* This method also called for creating dots for shaped point instances. */
case BatchingKey.GeometryType.POINT_INSTANCE:
objConstructor = three.Points
break
case BatchingKey.GeometryType.LINES:
case BatchingKey.GeometryType.INDEXED_LINES:
objConstructor = three.LineSegments
break
case BatchingKey.GeometryType.TRIANGLES:
case BatchingKey.GeometryType.INDEXED_TRIANGLES:
objConstructor = three.Mesh
break
default:
throw new Error("Unexpected geometry type:" + batch.key.geometryType)
}
const material = materialFactory.call(this, this._TransformColor(color), InstanceType.NONE);
const geometry = new three.BufferGeometry()
const positionAttribute = new three.Float32BufferAttribute(vertices.buffer, 2);
let objConstructor;
switch (batch.key.geometryType) {
case BatchingKey.GeometryType.POINTS:
/* This method also called for creating dots for shaped point instances. */
case BatchingKey.GeometryType.POINT_INSTANCE:
objConstructor = three.Points;
break;
case BatchingKey.GeometryType.LINES:
case BatchingKey.GeometryType.INDEXED_LINES:
objConstructor = three.LineSegments;
break;
case BatchingKey.GeometryType.TRIANGLES:
case BatchingKey.GeometryType.INDEXED_TRIANGLES:
objConstructor = three.Mesh;
break;
default:
throw new Error('Unexpected geometry type:' + batch.key.geometryType);
}
geometry.setAttribute('position', positionAttribute);
const geometry = new three.BufferGeometry();
const positionAttribute = new three.Float32BufferAttribute(vertices.buffer, 2);
if (indices) {
const handledIndices = new three.Uint16BufferAttribute(indices.buffer, 1)
geometry.setIndex(handledIndices)
}
geometry.setAttribute('position', positionAttribute);
return new objConstructor(geometry, material);
if (indices) {
const handledIndices = new three.Uint16BufferAttribute(indices.buffer, 1);
geometry.setIndex(handledIndices);
}
Render() {
this._EnsureRenderer()
this.renderer.render(this.scene, this.camera)
}
return new objConstructor(geometry, material);
}
/** @return {Iterable<{name:String, color:number}>} List of layer names. */
GetLayers() {
const result = []
for (const lyr of this.layers.values()) {
result.push({
name: lyr.name,
displayName: lyr.displayName,
color: this._TransformColor(lyr.color)
})
}
return result
}
Render() {
this._EnsureRenderer();
this.renderer.render(this.scene, this.camera);
}
ShowLayer(name, show) {
this._EnsureRenderer()
const layer = this.layers.get(name)
if (!layer) {
return
}
for (const obj of layer.objects) {
obj.visible = show
}
this.Render()
/** @return {Iterable<{name:String, color:number}>} List of layer names. */
GetLayers() {
const result = [];
for (const lyr of this.layers.values()) {
result.push({
name: lyr.name,
displayName: lyr.displayName,
color: this._TransformColor(lyr.color),
});
}
return result;
}
/** Reset the viewer state. */
Clear() {
this._EnsureRenderer()
if (this.worker) {
this.worker.Destroy(true)
this.worker = null
}
if (this.controls) {
this.controls.dispose()
this.controls = null
}
this.scene.clear()
for (const layer of this.layers.values()) {
layer.Dispose()
}
this.layers.clear()
this.blocks.clear()
this.materials.each(e => e.material.dispose())
this.materials.clear()
this.SetView({x: 0, y: 0}, 2)
this._Emit("cleared")
this.Render()
ShowLayer(name, show) {
this._EnsureRenderer();
const layer = this.layers.get(name);
if (!layer) {
return;
}
/** Free all resources. The viewer object should not be used after this method was called. */
Destroy() {
if (!this.HasRenderer()) {
return
}
if (this.resizeObserver) {
this.resizeObserver.disconnect()
}
this.Clear()
this._Emit("destroyed")
for (const m of this.simplePointMaterial) {
m.dispose()
}
for (const m of this.simpleColorMaterial) {
m.dispose()
}
this.simplePointMaterial = null
this.simpleColorMaterial = null
this.renderer.dispose()
this.renderer = null
for (const obj of layer.objects) {
obj.visible = show;
}
this.Render();
}
SetView(center, width) {
const aspect = this.canvasWidth / this.canvasHeight
const height = width / aspect
const cam = this.camera
cam.left = -width / 2
cam.right = width / 2
cam.top = height / 2
cam.bottom = -height / 2
cam.zoom = 1
cam.position.set(center.x, center.y, 1)
cam.rotation.set(0, 0, 0)
cam.updateMatrix()
cam.updateProjectionMatrix()
this._Emit("viewChanged")
/** Reset the viewer state. */
Clear() {
this._EnsureRenderer();
if (this.worker) {
this.worker.Destroy(true);
this.worker = null;
}
/** Set view to fit the specified bounds. */
FitView(minX, maxX, minY, maxY, padding = 0.1) {
const aspect = this.canvasWidth / this.canvasHeight
let width = maxX - minX
const height = maxY - minY
const center = {x: minX + width / 2, y: minY + height / 2}
if (height * aspect > width) {
width = height * aspect
}
if (width <= Number.MIN_VALUE * 2) {
width = 1
}
this.SetView(center, width * (1 + padding))
if (this.controls) {
this.controls.dispose();
this.controls = null;
}
/** @return {Scene} three.js scene for the viewer. Can be used to add custom entities on the
* scene. Remember to apply scene origin available via GetOrigin() method.
*/
GetScene() {
return this.scene
this.scene.clear();
for (const layer of this.layers.values()) {
layer.Dispose();
}
this.layers.clear();
this.blocks.clear();
this.materials.each((e) => e.material.dispose());
this.materials.clear();
this.SetView({ x: 0, y: 0 }, 2);
this._Emit('cleared');
this.Render();
}
/** @return {OrthographicCamera} three.js camera for the viewer. */
GetCamera() {
return this.camera
/** Free all resources. The viewer object should not be used after this method was called. */
Destroy() {
if (!this.HasRenderer()) {
return;
}
/** @return {Vector2} Scene origin in global drawing coordinates. */
GetOrigin() {
return this.origin
if (this.resizeObserver) {
this.resizeObserver.disconnect();
}
/**
* @return {?{maxX: number, maxY: number, minX: number, minY: number}} Scene bounds in model
* space coordinates. Null if empty scene.
*/
GetBounds() {
return this.bounds
this.Clear();
this._Emit('destroyed');
for (const m of this.simplePointMaterial) {
m.dispose();
}
GetTexts() {
return this.sceneData.texts;
for (const m of this.simpleColorMaterial) {
m.dispose();
}
this.simplePointMaterial = null;
this.simpleColorMaterial = null;
this.renderer.dispose();
this.renderer = null;
}
/** Subscribe to the specified event. The following events are defined:
* * "loaded" - new scene loaded.
* * "cleared" - current scene cleared.
* * "destroyed" - viewer instance destroyed.
* * "resized" - viewport size changed. Details: {width, height}
* * "pointerdown" - Details: {domEvent, position:{x,y}}, position is in scene coordinates.
* * "pointerup"
* * "viewChanged"
* * "message" - Some message from the viewer. {message: string, level: string}.
*
* @param eventName {string}
* @param eventHandler {function} Accepts event object.
*/
Subscribe(eventName, eventHandler) {
this._EnsureRenderer()
this.canvas.addEventListener(EVENT_NAME_PREFIX + eventName, eventHandler)
}
SetView(center, width) {
const aspect = this.canvasWidth / this.canvasHeight;
const height = width / aspect;
const cam = this.camera;
cam.left = -width / 2;
cam.right = width / 2;
cam.top = height / 2;
cam.bottom = -height / 2;
cam.zoom = 1;
cam.position.set(center.x, center.y, 1);
cam.rotation.set(0, 0, 0);
cam.updateMatrix();
cam.updateProjectionMatrix();
this._Emit('viewChanged');
}
/** Unsubscribe from previously subscribed event. The arguments should match previous
* Subscribe() call.
*
* @param eventName {string}
* @param eventHandler {function}
*/
Unsubscribe(eventName, eventHandler) {
this._EnsureRenderer()
this.canvas.removeEventListener(EVENT_NAME_PREFIX + eventName, eventHandler)
/** Set view to fit the specified bounds. */
FitView(minX, maxX, minY, maxY, padding = 0.1) {
const aspect = this.canvasWidth / this.canvasHeight;
let width = maxX - minX;
const height = maxY - minY;
const center = { x: minX + width / 2, y: minY + height / 2 };
if (height * aspect > width) {
width = height * aspect;
}
if (width <= Number.MIN_VALUE * 2) {
width = 1;
}
this.SetView(center, width * (1 + padding));
}
// /////////////////////////////////////////////////////////////////////////////////////////////
/** @return {Scene} three.js scene for the viewer. Can be used to add custom entities on the
* scene. Remember to apply scene origin available via GetOrigin() method.
*/
GetScene() {
return this.scene;
}
_EnsureRenderer() {
if (!this.HasRenderer()) {
throw new Error("WebGL renderer not available. " +
"Probable WebGL context loss, try refreshing the page.")
}
}
/** @return {OrthographicCamera} three.js camera for the viewer. */
GetCamera() {
return this.camera;
}
_CreateControls() {
const controls = this.controls = new OrbitControls(this.camera, this.canvas)
controls.enableRotate = false
controls.mouseButtons = {
LEFT: three.MOUSE.PAN,
MIDDLE: three.MOUSE.DOLLY
}
controls.touches = {
ONE: three.TOUCH.PAN,
TWO: three.TOUCH.DOLLY_PAN
}
controls.zoomSpeed = 3
controls.mouseZoomSpeedFactor = 0.05
controls.target = new three.Vector3(this.camera.position.x, this.camera.position.y, 0)
controls.addEventListener("change", () => {
this._Emit("viewChanged")
this.Render()
})
controls.update()
}
/** @return {Vector2} Scene origin in global drawing coordinates. */
GetOrigin() {
return this.origin;
}
_Emit(eventName, data = null) {
this.canvas.dispatchEvent(new CustomEvent(EVENT_NAME_PREFIX + eventName, { detail: data }))
}
/**
* @return {?{maxX: number, maxY: number, minX: number, minY: number}} Scene bounds in model
* space coordinates. Null if empty scene.
*/
GetBounds() {
return this.bounds;
}
_Message(message, level = MessageLevel.INFO) {
this._Emit("message", {message, level})
GetTexts() {
return this.sceneData.texts;
}
/** Subscribe to the specified event. The following events are defined:
* * "loaded" - new scene loaded.
* * "cleared" - current scene cleared.
* * "destroyed" - viewer instance destroyed.
* * "resized" - viewport size changed. Details: {width, height}
* * "pointerdown" - Details: {domEvent, position:{x,y}}, position is in scene coordinates.
* * "pointerup"
* * "viewChanged"
* * "message" - Some message from the viewer. {message: string, level: string}.
*
* @param eventName {string}
* @param eventHandler {function} Accepts event object.
*/
Subscribe(eventName, eventHandler) {
this._EnsureRenderer();
this.canvas.addEventListener(EVENT_NAME_PREFIX + eventName, eventHandler);
}
/** Unsubscribe from previously subscribed event. The arguments should match previous
* Subscribe() call.
*
* @param eventName {string}
* @param eventHandler {function}
*/
Unsubscribe(eventName, eventHandler) {
this._EnsureRenderer();
this.canvas.removeEventListener(EVENT_NAME_PREFIX + eventName, eventHandler);
}
// /////////////////////////////////////////////////////////////////////////////////////////////
_EnsureRenderer() {
if (!this.HasRenderer()) {
throw new Error(
'WebGL renderer not available. ' + 'Probable WebGL context loss, try refreshing the page.',
);
}
}
_OnPointerEvent(e) {
const canvasRect = e.target.getBoundingClientRect()
const canvasCoord = {x: e.clientX - canvasRect.left, y: e.clientY - canvasRect.top}
this._Emit(e.type, {
domEvent: e,
canvasCoord,
position: this.CanvasToSceneCoord(canvasCoord.x, canvasCoord.y)
})
const position = this.CanvasToSceneCoord(canvasCoord.x, canvasCoord.y)
_CreateControls() {
const controls = (this.controls = new OrbitControls(this.camera, this.canvas));
controls.enableRotate = false;
controls.mouseButtons = {
LEFT: three.MOUSE.PAN,
MIDDLE: three.MOUSE.DOLLY,
};
controls.touches = {
ONE: three.TOUCH.PAN,
TWO: three.TOUCH.DOLLY_PAN,
};
controls.zoomSpeed = 3;
controls.mouseZoomSpeedFactor = 0.05;
controls.target = new three.Vector3(this.camera.position.x, this.camera.position.y, 0);
controls.addEventListener('change', () => {
this._Emit('viewChanged');
this.Render();
});
controls.update();
}
// function convertVerticesToArray(vertices) {
// const coordinates = [];
// for (let i = 0; i < vertices.length; i += 3) {
// coordinates.push({
// x: vertices[i],
// y: vertices[i + 1],
// z: vertices[i + 2]
// });
// }
// return coordinates;
// }
_Emit(eventName, data = null) {
this.canvas.dispatchEvent(new CustomEvent(EVENT_NAME_PREFIX + eventName, { detail: data }));
}
// const senOusby = this.blocks.get('1 FURN L1$0$SEN OUSBY CH 1')
_Message(message, level = MessageLevel.INFO) {
this._Emit('message', { message, level });
}
// console.log('blocks', senOusby)
_OnPointerEvent(e) {
const canvasRect = e.target.getBoundingClientRect();
const canvasCoord = { x: e.clientX - canvasRect.left, y: e.clientY - canvasRect.top };
this._Emit(e.type, {
domEvent: e,
canvasCoord,
position: this.CanvasToSceneCoord(canvasCoord.x, canvasCoord.y),
});
const position = this.CanvasToSceneCoord(canvasCoord.x, canvasCoord.y);
// console.log('position', this._CanvasToSceneCoord(canvasCoord.x, canvasCoord.y))
}
// function convertVerticesToArray(vertices) {
// const coordinates = [];
// for (let i = 0; i < vertices.length; i += 3) {
// coordinates.push({
// x: vertices[i],
// y: vertices[i + 1],
// z: vertices[i + 2]
// });
// }
// return coordinates;
// }
/** @return {{x,y}} Scene coordinate corresponding to the specified canvas pixel coordinates. */
CanvasToSceneCoord(x, y) {
const v = new three.Vector3(x * 2 / this.canvasWidth - 1,
-y * 2 / this.canvasHeight + 1,
1).unproject(this.camera)
return {x: v.x, y: v.y}
}
// const senOusby = this.blocks.get('1 FURN L1$0$SEN OUSBY CH 1')
_OnResize(entry) {
this.SetSize(Math.floor(entry.contentRect.width), Math.floor(entry.contentRect.height))
}
// console.log('blocks', senOusby)
GetBatchPositions(blockName, scene) {
const positions = new Map()
// console.log('position', this._CanvasToSceneCoord(canvasCoord.x, canvasCoord.y))
}
const block = this.sceneData?.blocks?.get?.(blockName)
const factor = this.sceneData?.vars?.get?.('DIMALTF')
/** @return {{x,y}} Scene coordinate corresponding to the specified canvas pixel coordinates. */
CanvasToSceneCoord(x, y) {
const v = new three.Vector3(
(x * 2) / this.canvasWidth - 1,
(-y * 2) / this.canvasHeight + 1,
1,
).unproject(this.camera);
// console.log('factor', factor)
return { x: v.x, y: v.y };
}
if (block) {
const entities = block?.data?.entities ?? []
for (let i = 0; i < entities.length; i++) {
const entity = entities[i];
_OnResize(entry) {
this.SetSize(Math.floor(entry.contentRect.width), Math.floor(entry.contentRect.height));
}
// console.log('!!! insert', this.sceneData.unmappedInserts.get(`${blockName}${i + 1}`))
// console.log('*** ousby', entities[0].vertices)
// if (entity.position) {
// console.log('there is a position!')
// // console.log('*** position', entity.position)
// }
GetBatchPositions(blockName, scene) {
const positions = new Map();
const name = `${blockName}__${i + 1}`;
const block = this.sceneData?.blocks?.get?.(blockName);
const factor = this.sceneData?.vars?.get?.('DIMALTF');
if (entity.vertices) {
positions.set(name, {
block,
entity: this.sceneData.unmappedInserts.get(name),
factor,
})
}
}
}
// const entities = block?.data?.entities
// console.log('factor', factor)
// if (!entities) {
// return null
// }
if (block) {
const entities = block?.data?.entities ?? [];
// for (let i = 0; i < entities.length; i++) {
// const entity = entities[i]
for (let i = 0; i < entities.length; i++) {
const entity = entities[i];
// positions.push(entity)
// console.log('!!! insert', this.sceneData.unmappedInserts.get(`${blockName}${i + 1}`))
// console.log('*** ousby', entities[0].vertices)
// if (entity.position) {
// console.log('there is a position!')
// // console.log('*** position', entity.position)
// }
return positions
const name = `${blockName}__${i + 1}`;
if (entity.vertices) {
positions.set(name, {
block,
entity: this.sceneData.unmappedInserts.get(name),
factor,
});
}
}
}
// const entities = block?.data?.entities
_LoadBatch(scene, batch) {
// console.log('keys', batch.key.blockName)
const positions = this.GetBatchPositions(batch.key.blockName, scene)
// if (batch.key.insertName) {
// const objects = new Batch(this, scene, {
// ...batch,
// positions,
// }, batch.key.insertName).CreateObjects()
// if (!entities) {
// return null
// }
// for (const obj of objects) {
// obj.insertName = batch.key.insertName
// for (let i = 0; i < entities.length; i++) {
// const entity = entities[i]
// for (const [key] of this.sceneData.unmappedInserts.entries()) {
// if (key.startsWith(`${batch.key.insertName}__`) || key === batch.key.insertName) {
// // console.log('key', key)
// this.objects.set(key, {
// obj,
// })
// }
// }
// }
// }
// positions.push(entity)
// }
if (batch.key.blockName !== null &&
batch.key.geometryType !== BatchingKey.GeometryType.BLOCK_INSTANCE &&
batch.key.geometryType !== BatchingKey.GeometryType.POINT_INSTANCE) {
/* Block definition. */
return
}
const objects = new Batch(this, scene, {
...batch,
positions,
}, batch.key.blockName).CreateObjects()
return positions;
}
const layer = this.layers.get(batch.key.layerName)
_LoadBatch(scene, batch) {
// console.log('keys', batch.key.blockName)
const positions = this.GetBatchPositions(batch.key.blockName, scene);
// if (batch.key.insertName) {
// const objects = new Batch(this, scene, {
// ...batch,
// positions,
// }, batch.key.insertName).CreateObjects()
for (const obj of objects) {
obj.insertName = batch.key.blockName
// for (const obj of objects) {
// obj.insertName = batch.key.insertName
this.scene.add(obj)
if (layer) {
layer.PushObject(obj)
}
}
}
// for (const [key] of this.sceneData.unmappedInserts.entries()) {
// if (key.startsWith(`${batch.key.insertName}__`) || key === batch.key.insertName) {
// // console.log('key', key)
// this.objects.set(key, {
// obj,
// })
// }
// }
// }
// }
_GetSimpleColorMaterial(color, instanceType = InstanceType.NONE) {
const key = new MaterialKey(instanceType, null, color, 0)
let entry = this.materials.find({key})
if (entry !== null) {
return entry.material
}
entry = {
key,
material: this._CreateSimpleColorMaterialInstance(color, instanceType)
}
this.materials.insert(entry)
return entry.material
if (
batch.key.blockName !== null &&
batch.key.geometryType !== BatchingKey.GeometryType.BLOCK_INSTANCE &&
batch.key.geometryType !== BatchingKey.GeometryType.POINT_INSTANCE
) {
/* Block definition. */
return;
}
_CreateSimpleColorMaterial(instanceType = InstanceType.NONE) {
const shaders = this._GenerateShaders(instanceType, false)
return new three.RawShaderMaterial({
uniforms: {
color: {
value: new three.Color(0xff00ff)
}
},
vertexShader: shaders.vertex,
fragmentShader: shaders.fragment,
depthTest: false,
depthWrite: false,
glslVersion: three.GLSL3,
side: three.DoubleSide
})
}
const objects = new Batch(
this,
scene,
{
...batch,
positions,
},
batch.key.blockName,
).CreateObjects();
/** @param color {number} Color RGB numeric value.
* @param instanceType {number}
*/
_CreateSimpleColorMaterialInstance(color, instanceType = InstanceType.NONE) {
const src = this.simpleColorMaterial[instanceType]
/* Should reuse compiled shaders. */
const m = src.clone()
m.uniforms.color = { value: new three.Color(color) }
return m
}
const layer = this.layers.get(batch.key.layerName);
_GetSimplePointMaterial(color, instanceType = InstanceType.NONE) {
const key = new MaterialKey(instanceType, BatchingKey.GeometryType.POINTS, color, 0)
let entry = this.materials.find({key})
if (entry !== null) {
return entry.material
}
entry = {
key,
material: this._CreateSimplePointMaterialInstance(color, this.options.pointSize,
instanceType)
}
this.materials.insert(entry)
return entry.material
for (const obj of objects) {
obj.insertName = batch.key.blockName;
this.scene.add(obj);
if (layer) {
layer.PushObject(obj);
}
}
}
_CreateSimplePointMaterial(instanceType = InstanceType.NONE) {
const shaders = this._GenerateShaders(instanceType, true)
return new three.RawShaderMaterial({
uniforms: {
color: {
value: new three.Color(0xff00ff)
},
pointSize: {
value: 2
}
},
vertexShader: shaders.vertex,
fragmentShader: shaders.fragment,
depthTest: false,
depthWrite: false,
glslVersion: three.GLSL3
})
_GetSimpleColorMaterial(color, instanceType = InstanceType.NONE) {
const key = new MaterialKey(instanceType, null, color, 0);
let entry = this.materials.find({ key });
if (entry !== null) {
return entry.material;
}
entry = {
key,
material: this._CreateSimpleColorMaterialInstance(color, instanceType),
};
this.materials.insert(entry);
return entry.material;
}
/** @param color {number} Color RGB numeric value.
* @param size {number} Rasterized point size in pixels.
* @param instanceType {number}
*/
_CreateSimplePointMaterialInstance(color, size = 2, instanceType = InstanceType.NONE) {
const src = this.simplePointMaterial[instanceType]
/* Should reuse compiled shaders. */
const m = src.clone()
m.uniforms.color = { value: new three.Color(color) }
m.uniforms.size = { value: size }
return m
_CreateSimpleColorMaterial(instanceType = InstanceType.NONE) {
const shaders = this._GenerateShaders(instanceType, false);
return new three.RawShaderMaterial({
uniforms: {
color: {
value: new three.Color(0xff00ff),
},
},
vertexShader: shaders.vertex,
fragmentShader: shaders.fragment,
depthTest: false,
depthWrite: false,
glslVersion: three.GLSL3,
side: three.DoubleSide,
});
}
/** @param color {number} Color RGB numeric value.
* @param instanceType {number}
*/
_CreateSimpleColorMaterialInstance(color, instanceType = InstanceType.NONE) {
const src = this.simpleColorMaterial[instanceType];
/* Should reuse compiled shaders. */
const m = src.clone();
m.uniforms.color = { value: new three.Color(color) };
return m;
}
_GetSimplePointMaterial(color, instanceType = InstanceType.NONE) {
const key = new MaterialKey(instanceType, BatchingKey.GeometryType.POINTS, color, 0);
let entry = this.materials.find({ key });
if (entry !== null) {
return entry.material;
}
entry = {
key,
material: this._CreateSimplePointMaterialInstance(
color,
this.options.pointSize,
instanceType,
),
};
this.materials.insert(entry);
return entry.material;
}
_GenerateShaders(instanceType, pointSize) {
const fullInstanceAttr = instanceType === InstanceType.FULL ?
`
_CreateSimplePointMaterial(instanceType = InstanceType.NONE) {
const shaders = this._GenerateShaders(instanceType, true);
return new three.RawShaderMaterial({
uniforms: {
color: {
value: new three.Color(0xff00ff),
},
pointSize: {
value: 2,
},
},
vertexShader: shaders.vertex,
fragmentShader: shaders.fragment,
depthTest: false,
depthWrite: false,
glslVersion: three.GLSL3,
});
}
/** @param color {number} Color RGB numeric value.
* @param size {number} Rasterized point size in pixels.
* @param instanceType {number}
*/
_CreateSimplePointMaterialInstance(color, size = 2, instanceType = InstanceType.NONE) {
const src = this.simplePointMaterial[instanceType];
/* Should reuse compiled shaders. */
const m = src.clone();
m.uniforms.color = { value: new three.Color(color) };
m.uniforms.size = { value: size };
return m;
}
_GenerateShaders(instanceType, pointSize) {
const fullInstanceAttr =
instanceType === InstanceType.FULL
? `
/* First row. */

@@ -846,24 +866,31 @@ in vec3 instanceTransform0;

in vec3 instanceTransform1;
` : ""
const fullInstanceTransform = instanceType === InstanceType.FULL ?
`
: '';
const fullInstanceTransform =
instanceType === InstanceType.FULL
? `
pos.xy = mat2(instanceTransform0[0], instanceTransform1[0],
instanceTransform0[1], instanceTransform1[1]) * pos.xy +
vec2(instanceTransform0[2], instanceTransform1[2]);
` : ""
`
: '';
const pointInstanceAttr = instanceType === InstanceType.POINT ?
`
const pointInstanceAttr =
instanceType === InstanceType.POINT
? `
in vec2 instanceTransform;
` : ""
const pointInstanceTransform = instanceType === InstanceType.POINT ?
`
: '';
const pointInstanceTransform =
instanceType === InstanceType.POINT
? `
pos.xy += instanceTransform;
` : ""
`
: '';
const pointSizeUniform = pointSize ? "uniform float pointSize;" : ""
const pointSizeAssigment = pointSize ? "gl_PointSize = pointSize;" : ""
const pointSizeUniform = pointSize ? 'uniform float pointSize;' : '';
const pointSizeAssigment = pointSize ? 'gl_PointSize = pointSize;' : '';
return {
vertex: `
return {
vertex: `

@@ -887,3 +914,3 @@ precision highp float;

`,
fragment: `
fragment: `

@@ -898,354 +925,364 @@ precision highp float;

}
`
}
}
`,
};
}
/** Ensure the color is contrast enough with current background color.
* @param color {number} RGB value.
* @return {number} RGB value to use for rendering.
*/
_TransformColor(color) {
if (!this.options.colorCorrection && !this.options.blackWhiteInversion) {
return color
}
/* Just black and white inversion. */
const bkgLum = Luminance(this.clearColor)
if (color === 0xffffff && bkgLum >= 0.8) {
return 0
}
if (color === 0 && bkgLum <= 0.2) {
return 0xffffff
}
if (!this.options.colorCorrection) {
return color
}
const fgLum = Luminance(color)
const MIN_TARGET_RATIO = 1.5
const contrast = ContrastRatio(color, this.clearColor)
const diff = contrast >= 1 ? contrast : 1 / contrast
if (diff < MIN_TARGET_RATIO) {
let targetLum
if (bkgLum > 0.5) {
targetLum = bkgLum / 2
} else {
targetLum = bkgLum * 2
}
if (targetLum > fgLum) {
color = Lighten(color, targetLum / fgLum)
} else {
color = Darken(color, fgLum / targetLum)
}
}
return color
/** Ensure the color is contrast enough with current background color.
* @param color {number} RGB value.
* @return {number} RGB value to use for rendering.
*/
_TransformColor(color) {
if (!this.options.colorCorrection && !this.options.blackWhiteInversion) {
return color;
}
/* Just black and white inversion. */
const bkgLum = Luminance(this.clearColor);
if (color === 0xffffff && bkgLum >= 0.8) {
return 0;
}
if (color === 0 && bkgLum <= 0.2) {
return 0xffffff;
}
if (!this.options.colorCorrection) {
return color;
}
const fgLum = Luminance(color);
const MIN_TARGET_RATIO = 1.5;
const contrast = ContrastRatio(color, this.clearColor);
const diff = contrast >= 1 ? contrast : 1 / contrast;
if (diff < MIN_TARGET_RATIO) {
let targetLum;
if (bkgLum > 0.5) {
targetLum = bkgLum / 2;
} else {
targetLum = bkgLum * 2;
}
if (targetLum > fgLum) {
color = Lighten(color, targetLum / fgLum);
} else {
color = Darken(color, fgLum / targetLum);
}
}
return color;
}
}
DxfViewer.MessageLevel = MessageLevel
DxfViewer.MessageLevel = MessageLevel;
DxfViewer.DefaultOptions = {
canvasWidth: 400,
canvasHeight: 300,
/** Automatically resize canvas when the container is resized. This options utilizes
* ResizeObserver API which is still not fully standardized. The specified canvas size is
* ignored if the option is enabled.
*/
autoResize: false,
/** Frame buffer clear color. */
clearColor: new three.Color("#000"),
/** Frame buffer clear color alpha value. */
clearAlpha: 1.0,
/** Use alpha channel in a framebuffer. */
canvasAlpha: false,
/** Assume premultiplied alpha in a framebuffer. */
canvasPremultipliedAlpha: true,
/** Use antialiasing. May degrade performance on poor hardware. */
antialias: true,
/** Correct entities colors to ensure that they are always visible with the current background
* color.
*/
colorCorrection: false,
/** Simpler version of colorCorrection - just invert pure white or black entities if they are
* invisible on current background color.
*/
blackWhiteInversion: true,
/** Size in pixels for rasterized points (dot mark). */
pointSize: 2,
/** Scene generation options. */
sceneOptions: DxfScene.DefaultOptions,
/** Retain the simple object representing the parsed DXF - will consume a lot of additional
* memory.
*/
retainParsedDxf: false,
/** Whether to preserve the buffers until manually cleared or overwritten. */
preserveDrawingBuffer: false,
/** Encoding to use for decoding DXF file text content. DXF files newer than DXF R2004 (AC1018)
* use UTF-8 encoding. Older files use some code page which is specified in $DWGCODEPAGE header
* variable. Currently parser is implemented in such a way that encoding must be specified
* before the content is parsed so there is no chance to use this variable dynamically. This may
* be a subject for future changes. The specified value should be suitable for passing as
* `TextDecoder` constructor `label` parameter.
*/
fileEncoding: "utf-8"
}
canvasWidth: 400,
canvasHeight: 300,
/** Automatically resize canvas when the container is resized. This options utilizes
* ResizeObserver API which is still not fully standardized. The specified canvas size is
* ignored if the option is enabled.
*/
autoResize: false,
/** Frame buffer clear color. */
clearColor: new three.Color('#000'),
/** Frame buffer clear color alpha value. */
clearAlpha: 1.0,
/** Use alpha channel in a framebuffer. */
canvasAlpha: false,
/** Assume premultiplied alpha in a framebuffer. */
canvasPremultipliedAlpha: true,
/** Use antialiasing. May degrade performance on poor hardware. */
antialias: true,
/** Correct entities colors to ensure that they are always visible with the current background
* color.
*/
colorCorrection: false,
/** Simpler version of colorCorrection - just invert pure white or black entities if they are
* invisible on current background color.
*/
blackWhiteInversion: true,
/** Size in pixels for rasterized points (dot mark). */
pointSize: 2,
/** Scene generation options. */
sceneOptions: DxfScene.DefaultOptions,
/** Retain the simple object representing the parsed DXF - will consume a lot of additional
* memory.
*/
retainParsedDxf: false,
/** Whether to preserve the buffers until manually cleared or overwritten. */
preserveDrawingBuffer: false,
/** Encoding to use for decoding DXF file text content. DXF files newer than DXF R2004 (AC1018)
* use UTF-8 encoding. Older files use some code page which is specified in $DWGCODEPAGE header
* variable. Currently parser is implemented in such a way that encoding must be specified
* before the content is parsed so there is no chance to use this variable dynamically. This may
* be a subject for future changes. The specified value should be suitable for passing as
* `TextDecoder` constructor `label` parameter.
*/
fileEncoding: 'utf-8',
};
DxfViewer.SetupWorker = function () {
new DxfWorker(self, true)
}
new DxfWorker(self, true);
};
const InstanceType = Object.freeze({
/** Not instanced. */
NONE: 0,
/** Full affine transform per instance. */
FULL: 1,
/** Point instances, 2D-translation vector per instance. */
POINT: 2,
/** Not instanced. */
NONE: 0,
/** Full affine transform per instance. */
FULL: 1,
/** Point instances, 2D-translation vector per instance. */
POINT: 2,
/** Number of types. */
MAX: 3
})
/** Number of types. */
MAX: 3,
});
class Batch {
/**
* @param viewer {DxfViewer}
* @param scene Serialized scene.
* @param batch Serialized scene batch.
*/
constructor(viewer, scene, batch, blockName) {
this.blockName = blockName
this.viewer = viewer
this.key = batch.key
this.positions = batch.positions
this.position = batch.position
this.name = blockName
this.transformsOffset = batch.transformsOffset
this.verticesOffset = batch.verticesOffset
/**
* @param viewer {DxfViewer}
* @param scene Serialized scene.
* @param batch Serialized scene batch.
*/
constructor(viewer, scene, batch, blockName) {
this.blockName = blockName;
this.viewer = viewer;
this.key = batch.key;
this.positions = batch.positions;
this.position = batch.position;
this.name = blockName;
this.transformsOffset = batch.transformsOffset;
this.verticesOffset = batch.verticesOffset;
if (batch.hasOwnProperty("verticesOffset")) {
const verticesArray =
new Float32Array(scene.vertices,
batch.verticesOffset * Float32Array.BYTES_PER_ELEMENT,
batch.verticesSize)
if (this.key.geometryType !== BatchingKey.GeometryType.POINT_INSTANCE ||
scene.pointShapeHasDot) {
this.vertices = new three.BufferAttribute(verticesArray, 2)
}
if (this.key.geometryType === BatchingKey.GeometryType.POINT_INSTANCE) {
this.transforms = new three.InstancedBufferAttribute(verticesArray, 2)
}
}
if (batch.hasOwnProperty('verticesOffset')) {
const verticesArray = new Float32Array(
scene.vertices,
batch.verticesOffset * Float32Array.BYTES_PER_ELEMENT,
batch.verticesSize,
);
if (
this.key.geometryType !== BatchingKey.GeometryType.POINT_INSTANCE ||
scene.pointShapeHasDot
) {
this.vertices = new three.BufferAttribute(verticesArray, 2);
}
if (this.key.geometryType === BatchingKey.GeometryType.POINT_INSTANCE) {
this.transforms = new three.InstancedBufferAttribute(verticesArray, 2);
}
}
if (batch.hasOwnProperty("chunks")) {
this.chunks = []
for (const rawChunk of batch.chunks) {
if (batch.hasOwnProperty('chunks')) {
this.chunks = [];
for (const rawChunk of batch.chunks) {
const verticesArray = new Float32Array(
scene.vertices,
rawChunk.verticesOffset * Float32Array.BYTES_PER_ELEMENT,
rawChunk.verticesSize,
);
const indicesArray = new Uint16Array(
scene.indices,
rawChunk.indicesOffset * Uint16Array.BYTES_PER_ELEMENT,
rawChunk.indicesSize,
);
this.chunks.push({
vertices: new three.BufferAttribute(verticesArray, 2),
indices: new three.BufferAttribute(indicesArray, 1),
});
}
}
const verticesArray =
new Float32Array(scene.vertices,
rawChunk.verticesOffset * Float32Array.BYTES_PER_ELEMENT,
rawChunk.verticesSize)
const indicesArray =
new Uint16Array(scene.indices,
rawChunk.indicesOffset * Uint16Array.BYTES_PER_ELEMENT,
rawChunk.indicesSize)
this.chunks.push({
vertices: new three.BufferAttribute(verticesArray, 2),
indices: new three.BufferAttribute(indicesArray, 1)
})
}
}
if (batch.hasOwnProperty('transformsOffset')) {
const transformsArray = new Float32Array(
scene.transforms,
batch.transformsOffset * Float32Array.BYTES_PER_ELEMENT,
batch.transformsSize,
);
/* Each transform is 3x2 matrix which is split into two 3D vectors which will occupy two
* attribute slots.
*/
const buf = new three.InstancedInterleavedBuffer(transformsArray, 6);
this.transforms0 = new three.InterleavedBufferAttribute(buf, 3, 0);
this.transforms1 = new three.InterleavedBufferAttribute(buf, 3, 3);
}
if (batch.hasOwnProperty("transformsOffset")) {
const transformsArray =
new Float32Array(scene.transforms,
batch.transformsOffset * Float32Array.BYTES_PER_ELEMENT,
batch.transformsSize)
/* Each transform is 3x2 matrix which is split into two 3D vectors which will occupy two
* attribute slots.
*/
const buf = new three.InstancedInterleavedBuffer(transformsArray, 6)
this.transforms0 = new three.InterleavedBufferAttribute(buf, 3, 0)
this.transforms1 = new three.InterleavedBufferAttribute(buf, 3, 3)
}
if (
this.key.geometryType === BatchingKey.GeometryType.BLOCK_INSTANCE ||
this.key.geometryType === BatchingKey.GeometryType.POINT_INSTANCE
) {
const layer = this.viewer.layers.get(this.key.layerName);
if (layer) {
this.layerColor = layer.color;
} else {
this.layerColor = 0;
}
}
}
if (this.key.geometryType === BatchingKey.GeometryType.BLOCK_INSTANCE ||
this.key.geometryType === BatchingKey.GeometryType.POINT_INSTANCE) {
const layer = this.viewer.layers.get(this.key.layerName)
if (layer) {
this.layerColor = layer.color
} else {
this.layerColor = 0
}
}
GetInstanceType() {
switch (this.key.geometryType) {
case BatchingKey.GeometryType.BLOCK_INSTANCE:
return InstanceType.FULL;
case BatchingKey.GeometryType.POINT_INSTANCE:
return InstanceType.POINT;
default:
return InstanceType.NONE;
}
}
GetInstanceType() {
switch (this.key.geometryType) {
case BatchingKey.GeometryType.BLOCK_INSTANCE:
return InstanceType.FULL
case BatchingKey.GeometryType.POINT_INSTANCE:
return InstanceType.POINT
default:
return InstanceType.NONE
}
/** Create scene objects corresponding to batch data.
* @param instanceBatch {?Batch} Batch with instance transform. Null for non-instanced object.
*/
*CreateObjects(instanceBatch = null, bypassBlock) {
if (bypassBlock) {
yield* this._CreateObjects(instanceBatch);
return;
}
/** Create scene objects corresponding to batch data.
* @param instanceBatch {?Batch} Batch with instance transform. Null for non-instanced object.
*/
*CreateObjects(instanceBatch = null, bypassBlock) {
if (bypassBlock) {
yield* this._CreateObjects(instanceBatch)
return
}
if (this.key.geometryType === BatchingKey.GeometryType.BLOCK_INSTANCE ||
this.key.geometryType === BatchingKey.GeometryType.POINT_INSTANCE) {
if (instanceBatch !== null) {
throw new Error("Unexpected instance batch specified for instance batch")
}
yield* this._CreateBlockInstanceObjects()
return
}
yield* this._CreateObjects(instanceBatch)
if (
this.key.geometryType === BatchingKey.GeometryType.BLOCK_INSTANCE ||
this.key.geometryType === BatchingKey.GeometryType.POINT_INSTANCE
) {
if (instanceBatch !== null) {
throw new Error('Unexpected instance batch specified for instance batch');
}
yield* this._CreateBlockInstanceObjects();
return;
}
yield* this._CreateObjects(instanceBatch);
}
*_CreateObjects(instanceBatch) {
const color = instanceBatch ?
instanceBatch._GetInstanceColor(this.key.color) : this.key.color
*_CreateObjects(instanceBatch) {
const color = instanceBatch ? instanceBatch._GetInstanceColor(this.key.color) : this.key.color;
//XXX line type
const materialFactory =
this.key.geometryType === BatchingKey.GeometryType.POINTS ||
this.key.geometryType === BatchingKey.GeometryType.POINT_INSTANCE ?
this.viewer._GetSimplePointMaterial : this.viewer._GetSimpleColorMaterial
//XXX line type
const materialFactory =
this.key.geometryType === BatchingKey.GeometryType.POINTS ||
this.key.geometryType === BatchingKey.GeometryType.POINT_INSTANCE
? this.viewer._GetSimplePointMaterial
: this.viewer._GetSimpleColorMaterial;
const material = materialFactory.call(this.viewer, this.viewer._TransformColor(color),
instanceBatch?.GetInstanceType() ?? InstanceType.NONE)
const material = materialFactory.call(
this.viewer,
this.viewer._TransformColor(color),
instanceBatch?.GetInstanceType() ?? InstanceType.NONE,
);
let objConstructor
switch (this.key.geometryType) {
case BatchingKey.GeometryType.POINTS:
/* This method also called for creating dots for shaped point instances. */
case BatchingKey.GeometryType.POINT_INSTANCE:
objConstructor = three.Points
break
case BatchingKey.GeometryType.LINES:
case BatchingKey.GeometryType.INDEXED_LINES:
objConstructor = three.LineSegments
break
case BatchingKey.GeometryType.TRIANGLES:
case BatchingKey.GeometryType.INDEXED_TRIANGLES:
objConstructor = three.Mesh
break
default:
throw new Error("Unexpected geometry type:" + this.key.geometryType)
}
let objConstructor;
switch (this.key.geometryType) {
case BatchingKey.GeometryType.POINTS:
/* This method also called for creating dots for shaped point instances. */
case BatchingKey.GeometryType.POINT_INSTANCE:
objConstructor = three.Points;
break;
case BatchingKey.GeometryType.LINES:
case BatchingKey.GeometryType.INDEXED_LINES:
objConstructor = three.LineSegments;
break;
case BatchingKey.GeometryType.TRIANGLES:
case BatchingKey.GeometryType.INDEXED_TRIANGLES:
objConstructor = three.Mesh;
break;
default:
throw new Error('Unexpected geometry type:' + this.key.geometryType);
}
function CreateObject(vertices, indices) {
const geometry = instanceBatch ?
new three.InstancedBufferGeometry() : new three.BufferGeometry()
geometry.setAttribute("position", vertices)
instanceBatch?._SetInstanceTransformAttribute(geometry)
if (indices) {
geometry.setIndex(indices)
}
const obj = new objConstructor(geometry, material)
obj.frustumCulled = false
obj.matrixAutoUpdate = false
obj.vertices = vertices
return obj
}
function CreateObject(vertices, indices) {
const geometry = instanceBatch
? new three.InstancedBufferGeometry()
: new three.BufferGeometry();
geometry.setAttribute('position', vertices);
instanceBatch?._SetInstanceTransformAttribute(geometry);
if (indices) {
geometry.setIndex(indices);
}
const obj = new objConstructor(geometry, material);
obj.frustumCulled = false;
obj.matrixAutoUpdate = false;
obj.vertices = vertices;
return obj;
}
if (this.chunks) {
for (const chunk of this.chunks) {
yield CreateObject(chunk.vertices, chunk.indices)
}
} else {
yield CreateObject(this.vertices, null)
}
if (this.chunks) {
for (const chunk of this.chunks) {
yield CreateObject(chunk.vertices, chunk.indices);
}
} else {
yield CreateObject(this.vertices, null);
}
}
/**
* @param geometry {InstancedBufferGeometry}
*/
_SetInstanceTransformAttribute(geometry) {
if (!geometry.isInstancedBufferGeometry) {
throw new Error("InstancedBufferGeometry expected")
}
if (this.key.geometryType === BatchingKey.GeometryType.POINT_INSTANCE) {
geometry.setAttribute("instanceTransform", this.transforms)
} else {
geometry.setAttribute("instanceTransform0", this.transforms0)
geometry.setAttribute("instanceTransform1", this.transforms1)
}
/**
* @param geometry {InstancedBufferGeometry}
*/
_SetInstanceTransformAttribute(geometry) {
if (!geometry.isInstancedBufferGeometry) {
throw new Error('InstancedBufferGeometry expected');
}
if (this.key.geometryType === BatchingKey.GeometryType.POINT_INSTANCE) {
geometry.setAttribute('instanceTransform', this.transforms);
} else {
geometry.setAttribute('instanceTransform0', this.transforms0);
geometry.setAttribute('instanceTransform1', this.transforms1);
}
}
*_CreateBlockInstanceObjects() {
const block = this.viewer.blocks.get(this.key.blockName)
if (!block) {
return
}
for (const batch of block.batches) {
yield* batch.CreateObjects(this)
}
if (this.hasOwnProperty("vertices")) {
/* Dots for point shapes. */
yield* this._CreateObjects()
}
*_CreateBlockInstanceObjects() {
const block = this.viewer.blocks.get(this.key.blockName);
if (!block) {
return;
}
for (const batch of block.batches) {
yield* batch.CreateObjects(this);
}
if (this.hasOwnProperty('vertices')) {
/* Dots for point shapes. */
yield* this._CreateObjects();
}
}
/**
* @param defColor {number} Color value for block definition batch.
* @return {number} RGB color value for a block instance.
*/
_GetInstanceColor(defColor) {
if (defColor === ColorCode.BY_BLOCK) {
return this.key.color
} else if (defColor === ColorCode.BY_LAYER) {
return this.layerColor
} else {
return defColor
}
/**
* @param defColor {number} Color value for block definition batch.
* @return {number} RGB color value for a block instance.
*/
_GetInstanceColor(defColor) {
if (defColor === ColorCode.BY_BLOCK) {
return this.key.color;
} else if (defColor === ColorCode.BY_LAYER) {
return this.layerColor;
} else {
return defColor;
}
}
}
class Layer {
constructor(name, displayName, color) {
this.name = name
this.displayName = displayName
this.color = color
this.objects = []
}
constructor(name, displayName, color) {
this.name = name;
this.displayName = displayName;
this.color = color;
this.objects = [];
}
PushObject(obj) {
this.objects.push(obj)
}
PushObject(obj) {
this.objects.push(obj);
}
Dispose() {
for (const obj of this.objects) {
obj.geometry.dispose()
}
this.objects = null
Dispose() {
for (const obj of this.objects) {
obj.geometry.dispose();
}
this.objects = null;
}
}
class Block {
constructor() {
this.batches = []
}
constructor() {
this.batches = [];
}
/** @param batch {Batch} */
PushBatch(batch) {
this.batches.push(batch)
}
/** @param batch {Batch} */
PushBatch(batch) {
this.batches.push(batch);
}
}
/** Custom viewer event names are prefixed with this string. */
const EVENT_NAME_PREFIX = "__dxf_"
const EVENT_NAME_PREFIX = '__dxf_';
/** Transform sRGB color component to linear color space. */
function LinearColor(c) {
return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4)
return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
}

@@ -1255,3 +1292,3 @@

function SRgbColor(c) {
return c < 0.003 ? c * 12.92 : Math.pow(c, 1 / 2.4) * 1.055 - 0.055
return c < 0.003 ? c * 12.92 : Math.pow(c, 1 / 2.4) * 1.055 - 0.055;
}

@@ -1265,7 +1302,7 @@

function Luminance(color) {
const r = LinearColor(((color & 0xff0000) >>> 16) / 255)
const g = LinearColor(((color & 0xff00) >>> 8) / 255)
const b = LinearColor((color & 0xff) / 255)
const r = LinearColor(((color & 0xff0000) >>> 16) / 255);
const g = LinearColor(((color & 0xff00) >>> 8) / 255);
const b = LinearColor((color & 0xff) / 255);
return r * 0.2126 + g * 0.7152 + b * 0.0722
return r * 0.2126 + g * 0.7152 + b * 0.0722;
}

@@ -1282,88 +1319,90 @@

function ContrastRatio(c1, c2) {
return (Luminance(c1) + 0.05) / (Luminance(c2) + 0.05)
return (Luminance(c1) + 0.05) / (Luminance(c2) + 0.05);
}
function HlsToRgb({h, l, s}) {
let r, g, b
if (s === 0) {
/* Achromatic */
r = g = b = l
} else {
function hue2rgb(p, q, t) {
if (t < 0) {
t += 1
}
if (t > 1) {
t -= 1
}
if (t < 1/6) {
return p + (q - p) * 6 * t
}
if (t < 1/2) {
return q
}
if (t < 2/3) {
return p + (q - p) * (2/3 - t) * 6
}
return p
}
const q = l < 0.5 ? l * (1 + s) : l + s - l * s
const p = 2 * l - q
r = hue2rgb(p, q, h + 1/3)
g = hue2rgb(p, q, h)
b = hue2rgb(p, q, h - 1/3)
function HlsToRgb({ h, l, s }) {
let r, g, b;
if (s === 0) {
/* Achromatic */
r = g = b = l;
} else {
function hue2rgb(p, q, t) {
if (t < 0) {
t += 1;
}
if (t > 1) {
t -= 1;
}
if (t < 1 / 6) {
return p + (q - p) * 6 * t;
}
if (t < 1 / 2) {
return q;
}
if (t < 2 / 3) {
return p + (q - p) * (2 / 3 - t) * 6;
}
return p;
}
return (Math.min(Math.floor(SRgbColor(r) * 256), 255) << 16) |
(Math.min(Math.floor(SRgbColor(g) * 256), 255) << 8) |
Math.min(Math.floor(SRgbColor(b) * 256), 255)
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
const p = 2 * l - q;
r = hue2rgb(p, q, h + 1 / 3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1 / 3);
}
return (
(Math.min(Math.floor(SRgbColor(r) * 256), 255) << 16) |
(Math.min(Math.floor(SRgbColor(g) * 256), 255) << 8) |
Math.min(Math.floor(SRgbColor(b) * 256), 255)
);
}
function RgbToHls(color) {
const r = LinearColor(((color & 0xff0000) >>> 16) / 255)
const g = LinearColor(((color & 0xff00) >>> 8) / 255)
const b = LinearColor((color & 0xff) / 255)
const r = LinearColor(((color & 0xff0000) >>> 16) / 255);
const g = LinearColor(((color & 0xff00) >>> 8) / 255);
const b = LinearColor((color & 0xff) / 255);
const max = Math.max(r, g, b)
const min = Math.min(r, g, b)
let h, s
const l = (max + min) / 2
const max = Math.max(r, g, b);
const min = Math.min(r, g, b);
let h, s;
const l = (max + min) / 2;
if (max === min) {
/* Achromatic */
h = s = 0
} else {
const d = max - min
s = l > 0.5 ? d / (2 - max - min) : d / (max + min)
switch (max) {
case r:
h = (g - b) / d + (g < b ? 6 : 0)
break;
case g:
h = (b - r) / d + 2
break
case b:
h = (r - g) / d + 4
break
}
h /= 6
if (max === min) {
/* Achromatic */
h = s = 0;
} else {
const d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch (max) {
case r:
h = (g - b) / d + (g < b ? 6 : 0);
break;
case g:
h = (b - r) / d + 2;
break;
case b:
h = (r - g) / d + 4;
break;
}
h /= 6;
}
return {h, l, s}
return { h, l, s };
}
function Lighten(color, factor) {
const hls = RgbToHls(color)
hls.l *= factor
if (hls.l > 1) {
hls.l = 1
}
return HlsToRgb(hls)
const hls = RgbToHls(color);
hls.l *= factor;
if (hls.l > 1) {
hls.l = 1;
}
return HlsToRgb(hls);
}
function Darken(color, factor) {
const hls = RgbToHls(color)
hls.l /= factor
return HlsToRgb(hls)
const hls = RgbToHls(color);
hls.l /= factor;
return HlsToRgb(hls);
}
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc