React js, Raphael: canvas throw an Error: <path> attribute d: Expected number, "….68028259277344CNaN,NaN,NaN,NaN,…" - reactjs

Framwork: React js.
Library: "raphael": "^2.2.8",
Description: canvas throw an Error: attribute d: Expected number, "….68028259277344CNaN,NaN,NaN,NaN,…".
console errors screenshot
http://jsfiddle.net/fzjc81ym/
this.canvas = Raphael('grid', '100%', '100%');
drawLine(this.canvas, path1, duration, arrowAtrr, color, strokeDasharray, strokeWidth, arrowend).then(() => this.resolve(item, callback))
const drawLine = (canvas, pathStr, duration = 1000, attr = arrowAtrr, color = Color.GREEN, strokeDasharray = '-', strokeWidth = 4, arrowend = "block-wide-long") => {
return new Promise((resolve) => {
attr.stroke = color;
attr['stroke-dasharray'] = strokeDasharray;
attr['stroke-width'] = strokeWidth;
attr['arrow-end'] = arrowend
var guidePath = canvas.path(pathStr).attr({ stroke: 'none', fill: 'none' });
var path = canvas.path(pathStr).attr({ stroke: 'none', fill: 'none' });
var totalLength = guidePath.getTotalLength(guidePath);
var startTime = new Date().getTime();
var intervalLength = 25;
var intervalId = setInterval(function () {
var elapsedTime = new Date().getTime() - startTime;
var thisLength = elapsedTime / duration * totalLength;
var subPathStr = guidePath.getSubpath(0, thisLength);
attr.path = subPathStr;
path.attr(attr)
path.animate(attr, intervalLength);
if (elapsedTime >= duration) {
clearInterval(intervalId);
resolve();
}
}, intervalLength);
});
}
it seems to happen when I use the arrow-end attribute
I didn't find an answer in other places
Maybe someone has any idea how to solve that error?

A solutions:
in my case, the arrow line was smaller than the arrow-end triangle,
so I set the min size of the path to be 11 (the length of the arrow-end).
var subPathStr = guidePath.getSubpath(0, Math.max(11, thisLength));
here the around code:
var subPathStr = guidePath.getSubpath(0, Math.max(11, thisLength));
attr.path = subPathStr;
path.attr(attr)
path.animate(attr, intervalLength);

You didnt really include enough Information for your problem but this may help.
https://www.npmjs.com/package/react-raphael
It looks like you need to call Raphael.Paper(width={300} height={300}) in order to set the width of a canvas.

Related

why all MTL & OBJ files load in last array on pattern marker (Three.js - Augmented Reality)

here I have the pattern file hiro.patt & pattern-markerobj1.patt
let patternArray = ["hiro", "pattern-markerobj1"];
pattern hiro.patt represents mtl-obj Fish
pattern pattern-markerobj1.patt mtl-obj represents
let mtlobj = ['fish-2','test',];
file obj dan mtl
but when it is executed, all 3D models are only displayed on the 1st marker index or 2nd marker, namely pattern-markerobj1.
however when I use THREE.MeshBasicMaterial by using color in array
let colorArray = [0xff8800, 0xffff00, ]; they seemed no problem.
can be seen in the image below
Hiro without 3D Model without 3D Model :')
pattern-markerobj1 with 2 3D Models (stacked) pattern-markerobj1
then how to fix it?
hope
marker Hiro -> mtl obj fish
marker pattern-markerobj1 -> test mtl obj
let patternArray = ["hiro", "pattern-markerobj1"];
let colorArray = [0xff8800, 0xffff00, ];
let mtlobj = ['fish-2', 'percobaan', ];
let scale = [0.25, 0.0025, ];
for (let i = 0; i < 2; i++) {
markerRoot1 = new THREE.Group();
scene.add(markerRoot1);
let markerControls1 = new THREEx.ArMarkerControls(arToolkitContext, markerRoot1, {
type: 'pattern',
patternUrl: "data/" + patternArray[i] + ".patt",
})
let geometry1 = new THREE.PlaneBufferGeometry(1, 1, 4, 4);
// let texture = loader.load( 'images/earth.jpg', render );
let material1 = new THREE.MeshBasicMaterial({
color: colorArray[i],
opacity: 0.0005
});
mesh1 = new THREE.Mesh(geometry1, material1);
mesh1.rotation.x = -Math.PI / 2;
markerRoot1.add(mesh1);
function onProgress(xhr) {
console.log((xhr.loaded / xhr.total * 100) + '% loaded');
}
function onError(xhr) {
console.log('An error happened');
}
new THREE.MTLLoader()
.setPath('models/')
.load(mtlobj[i] + '.mtl', function (materials) {
materials.preload();
new THREE.OBJLoader()
.setMaterials(materials)
.setPath('models/')
.load(mtlobj[i] + '.obj', function (group) {
mesh0 = group.children[0];
mesh0.material.side = THREE.DoubleSide;
mesh0.position.y = 0.25;
mesh0.scale.set(scale[i], scale[i], scale[i]);
markerRoot1.add(mesh0);
}, onProgress, onError);
});
}

React threejs merge two tubes to create a tee-piece with CSG not working as expected

I am trying to create a tee-piece which is a fitting in the plumbing domain. It consist of 2 tubes that are merged together and has 3 openings as shown in this picture.
I have written some code in threejs where I am trying to create a tube mesh1 and another tube mesh2 and then try to union them into mesh3 with the library #enable3d/three-graphics/jsm/csg - thanks to #Marquizzo. After using the function CSG.union and adding the mesh to the scene I can see that I get one tee-piece but it has also created a hole in geometry 1, which was not expected. You can see a picture of the correct holes(green) and the wrongly created hole (red) here:
it should instead look like this and be as one geometry.
Can anyone tell me how CSG works and why I am getting an extra hole on the backside of the first geometry?
import React, { Component } from 'react';
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
import { CSG } from '#enable3d/three-graphics/jsm/csg';
export default class TubeViewer extends Component {
componentDidMount() {
//Add Scene
this.scene = new THREE.Scene();
//Add Renderer
this.renderer = new THREE.WebGLRenderer({ antialias: true });
this.renderer.setClearColor('#808080');
this.renderer.shadowMap.enabled = true;
this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;
this.renderer.setPixelRatio(window.devicePixelRatio);
this.renderer.setSize(window.innerWidth, window.innerHeight);
this.mount.appendChild(this.renderer.domElement);
//Add Camera
const fov = 60;
const aspect = window.innerWidth / window.innerHeight;
const near = 1.0;
const far = 1000.0;
this.camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
this.camera.position.set(1, aspect, 1, 1000);
//Tee-piece
const curve1 = new THREE.LineCurve(new THREE.Vector3(2, 0, 0), new THREE.Vector3(2, 0, 0.1));
const curve11 = new THREE.LineCurve(new THREE.Vector3(2.0, 0, 0.05), new THREE.Vector3(2.05, 0, 0.05));
const geometry1 = new THREE.TubeGeometry(curve1, 20, 0.025, 8, false);
const geometry2 = new THREE.TubeGeometry(curve2, 20, 0.025, 8, false);
const material = new THREE.MeshBasicMaterial({ color: '#C0C0C0' });
const mesh1 = new THREE.Mesh(geometry1, material);
const mesh2 = new THREE.Mesh(geometry2, material);
const mesh3 = CSG.union(mesh1, mesh2);
this.scene.add(mesh3);
//Add raycaster to for interactivity
this.raycaster = new THREE.Raycaster();
this.mouse = new THREE.Vector2();
this.renderer.domElement.addEventListener('click', onClick.bind(this), false);
function onClick(event) {
event.preventDefault();
this.mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
this.mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
this.raycaster.setFromCamera(this.mouse, this.camera);
var intersects = this.raycaster.intersectObjects(this.scene.children, true);
if (intersects.length > 0) {
console.log('Intersection:', intersects[0]);
//console.log(intersects[0].object.uuid);
// console.log(`GUID: ${intersects[0]}`);
let object = intersects[0].object;
object.material.color.set(Math.random() * 0xffffff);
}
}
//Settings
//Add Camera Controls
const controls = new OrbitControls(this.camera, this.renderer.domElement);
controls.addEventListener('change', this.render); // use if there is no animation loop
controls.minDistance = 2;
controls.maxDistance = 10;
controls.target.set(0, 0, -0.2);
controls.update();
///Add AMBIENT LIGHT
let light = new THREE.DirectionalLight(0xffffff, 1.0);
light.position.set(20, 100, 10);
light.target.position.set(0, 0, 0);
light.castShadow = true;
light.shadow.bias = -0.001;
light.shadow.mapSize.width = 2048;
light.shadow.mapSize.height = 2048;
light.shadow.camera.near = 0.1;
light.shadow.camera.far = 500.0;
light.shadow.camera.near = 0.5;
light.shadow.camera.far = 500.0;
light.shadow.camera.left = 100;
light.shadow.camera.right = -100;
light.shadow.camera.top = 100;
light.shadow.camera.bottom = -100;
this.scene.add(light);
light = new THREE.AmbientLight(0xffffff, 0.7);
this.scene.add(light);
//Start animation
this.start();
}
//Unmount when animation has stopped
componentWillUnmount() {
this.stop();
this.mount.removeChild(this.renderer.domElement);
}
//Function to start animation
start = () => {
//Rotate Models
if (!this.frameId) {
this.frameId = requestAnimationFrame(this.animate);
}
};
//Function to stop animation
stop = () => {
cancelAnimationFrame(this.frameId);
};
//Animate models here
animate = () => {
//ReDraw scene with camera and scene object
if (this.cubeMesh) this.cubeMesh.rotation.y += 0.01;
this.renderScene();
this.frameId = window.requestAnimationFrame(this.animate);
};
//Render the scene
renderScene = () => {
if (this.renderer) this.renderer.render(this.scene, this.camera);
};
render() {
return (
<div
style={{ width: '800px', height: '800px' }}
ref={(mount) => {
this.mount = mount;
}}
/>
);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
enter code here?
For CSG you'll need solid bodies. These tubes are open.
I created an example using cylinders (tubes are involved to cap) so you can test it.
These cylinders are open ended, so they fail in the same way as your tubes.
https://codepen.io/flatworldstudio/pen/bGBjmrP
const geometry1 = new THREE.CylinderGeometry(0.1, 0.1, 0.5, 20, 1, true);
These are closed, and CSG works as expected.
https://codepen.io/flatworldstudio/pen/VwmBRoL
const geometry1 = new THREE.CylinderGeometry(0.1, 0.1, 0.5, 20, 1, false);
(I'm using a different version of CSG, but they all seem to be built on the same code)

How to group GLTF element and drag and drop in 3js

Single GLTF element i can drag and drop. but group of element i can't drag. I am using following code
var loader = new THREE.GLTFLoader();
loader.load( 'W3030/W3030.gltf', ( gltf ) => {
gltf.scene.traverse( function( child ) {
if(child.type === "Group")
{
newObject = true;
GLTFobjects.push(child);
}
if ( child.isMesh ) {
child.receiveShadow = true;
child.castShadow = true;
child.material.transparent = true;
child.material.opacity = 1;
}
});
scene.add(GLTFobjects);
gltf.scene.scale.set(1, 1, 1);
});
I'm afraid instances of Group are not supported by DragControls since there is no Group.raycast() method.
You can implement a workaround by replacing groups with invisible meshes. However, instead of setting Object3D.visible to false, you do this for Material.visible. Otherwise the raycasting logic will not perform the intersection test. It's then necessary to use a geometry that is large enough to enclose the respective children.
three.js R110
Hi Thanks for your support. Now i drag.
Use following code to drag multi mesh GLTF. It is Work for me.
var dragobjects =[];
//add following code in init function
var gltfobject = addGLTFObjectIntoScene();
scene.add(gltfobject);
dragControls = new THREE.DragControls(dragobjects, camera, renderer.domElement);
dragControls.addEventListener('dragstart', onDragStart, false);
dragControls.addEventListener('drag', onDrag , false);
dragControls.addEventListener('dragend', onDragEnd, false);
//end init function code
//add following function after or before init function
function drawBox(objectwidth,objectheight,objectdepth){
var geometry, material, box;
geometry = new THREE.BoxGeometry(objectwidth,objectheight,objectdepth);
material = new THREE.MeshBasicMaterial({color: 0xffff00, transparent: true, opacity: 0,depthTest:false});
box = new THREE.Mesh(geometry, material);
dragobjects.push(box);
box.position.set(0, 0, 0);
return box;
};
function addGLTFObjectIntoScene(){
group = new THREE.Group();
var loader = new THREE.GLTFLoader();
loader.load( 'W1230/W1230.gltf', ( gltf ) => {
mesh = gltf.scene;
mesh.scale.set( 30, 30, 30);
var gltfbox = new THREE.Box3().setFromObject( mesh );
var objectwidth = Math.floor(gltfbox.getSize().x);
var objectheight = Math.floor(gltfbox.getSize().y);
var objectdepth = Math.floor(gltfbox.getSize().z);
objectwidth = objectwidth + parseInt(2);
objectheight = objectheight + parseInt(2);
objectdepth = objectdepth + parseInt(1);
mesh.position.set(0, -objectheight/2, 0);
box = drawBox(objectwidth,objectheight,objectdepth);
group.add(box);
group.name = "quadrant";
console.log(mesh);
box.add( mesh);
});
return group;
};

THREE.js Texture to Sphere with React

I am trying to load a flag (gif) texture to a sphere geometry in THREE.js, but the caveat is I am using React to do this.
const textureLoader = new THREE.TextureLoader();
const flag = getFlagForCountry(flags, x.id),
texture = textureLoader.load(require(`../assets/images/flags/${flag.name}.gif`));
const mat = new THREE.MeshLambertMaterial({
transparent: true,
opacity: .5,
map: texture
});
const sphere = new THREE.Mesh(new THREE.SphereGeometry(1, 10, 10), mat);
sphere.overdraw = true;
When I remove the map: texture property I am able to see the sphere in the scene, but then when I add back in the texture it is simply a black screen. I know the docs for TextureLoader say url is a string, but I am not getting any errors and in fact I am getting warnings that make it appear like something is working. Has anyone had success loading a texture onto a sphere using require() in React.
THREE.WebGLRenderer: image is not power of two (1181x788). Resized to 1024x512
<img crossorigin="anonymous" src="/static/media/Argentina.4c3ff3da.gif">
I would recommend passing the image path directly to the .load() method rather than passing it via require(). Also, I suggest using the TextureLoader callback, to ensure that your texture object is valid and fully loaded, before trying to make use of it.
You can make use of the callback in this way:
const textureLoader = new THREE.TextureLoader();
const flag = getFlagForCountry(flags, x.id)'
// Use the loaders callback
textureLoader.load(`../assets/images/flags/${flag.name}.gif`, function(texture) {
// The texture object has loaded and is now avalible to be used
const mat = new THREE.MeshLambertMaterial({
transparent: true,
opacity: .5,
map: texture
});
const sphere = new THREE.Mesh(new THREE.SphereGeometry(1, 10, 10), mat);
sphere.overdraw = true;
// Add sphere to your scene ... scene.add(sphere);
});
As a final note, consider adjusting your image filepath to an absolute path (by removing the ..) if your assets directory is located in the same directory that your webserver is running from.
Hope this helps!
import React, { Component } from "react";
import * as THREE from "three";
var earthMesh;
class ThreeScene extends Component {
componentDidMount() {
const width = this.mount.clientWidth;
const height = this.mount.clientHeight;
//ADD SCENE
this.scene = new THREE.Scene();
//ADD CAMERA
this.camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000);
this.camera.position.z = 8;
//ADD RENDERER
this.renderer = new THREE.WebGLRenderer({ antialias: true });
this.renderer.setClearColor("#263238");
this.renderer.setSize(width, height);
this.mount.appendChild(this.renderer.domElement);
//ADD CUBE
const geometry = new THREE.BoxGeometry(5, 5, 5);
const material = new THREE.MeshBasicMaterial({
color: "#0F0",
wireframe: true
});
this.cube = new THREE.Mesh(geometry, material);
this.scene.add(this.cube);
//Add SPHERE
//LOAD TEXTURE and on completion apply it on box
var loader = new THREE.TextureLoader();
loader.load(
"data:image/png;base64,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",
this.onLoad,
this.onProgress,
this.onError
);
//LIGHTS
var lights = [];
lights[0] = new THREE.PointLight(0x304ffe, 1, 0);
lights[1] = new THREE.PointLight(0xffffff, 1, 0);
lights[2] = new THREE.PointLight(0xffffff, 1, 0);
lights[0].position.set(0, 200, 0);
lights[1].position.set(100, 200, 100);
lights[2].position.set(-100, -200, -100);
this.scene.add(lights[0]);
this.scene.add(lights[1]);
this.scene.add(lights[2]);
}
componentWillUnmount() {
this.stop();
this.mount.removeChild(this.renderer.domElement);
}
start = () => {
if (!this.frameId) {
this.frameId = requestAnimationFrame(this.animate);
}
};
stop = () => {
cancelAnimationFrame(this.frameId);
};
animate = () => {
this.earthMesh.rotation.x += 0.01;
this.cube.rotation.y += 0.01;
this.renderScene();
this.frameId = window.requestAnimationFrame(this.animate);
};
renderScene = () => {
this.renderer.render(this.scene, this.camera);
};
onLoad = texture => {
var objGeometry = new THREE.SphereBufferGeometry(3, 35, 35);
var objMaterial = new THREE.MeshPhongMaterial({
map: texture,
shading: THREE.FlatShading
});
this.earthMesh = new THREE.Mesh(objGeometry, objMaterial);
this.scene.add(this.earthMesh);
this.renderScene();
//start animation
this.start();
};
onProgress = xhr => {
console.log((xhr.loaded / xhr.total) * 100 + "% loaded");
};
// Function called when download errors
onError = error => {
console.log("An error happened" + error);
};
render() {
return (
<div
style={{ width: "400px", height: "400px" }}
ref={mount => {
this.mount = mount;
}}
/>
);
}
}
export default ThreeScene;
https://codesandbox.io/embed/kw7l49nw1r

How to get the minimal required width for a dijit ComboBox or FilteringSelect?

I'm using ComboBox and FilteringSelect in a dialog and have yet been unable to make the controls have the minimal required width only, i.e. being just large enough to display the longest text from a drop-down list. Also the control must not be set to a fixed width since the actual content of the drop-down lists gets filled in from a translation database.
In plain html with a simple input of type text it works smooth just by default. However since even all examples at dojotoolkit.org show the very same behavior it seems to me that dojo introduces a minimum width for all those input controls. Thus I wonder if it can be done at all...
Thanks in advance!
I had the same problem; after some struggle, I decided to adapt this to my problem.
In my case, I was forced to use an old version of dojo, and the FilteringSelect were declarative, so I had to use a hack (the last three lines of the code below) to be sure my function would be executed at the right time.
So, the function below takes all dijit widgets, looks for those stored element is a select (getAllDropdowns), and for each it takes its options, copies the content in a new element moved outside of the visible screen and takes the width of that element, adjusted with padding (this may not be your case, so check getWidth); then it takes the max of those widths and compare it with the current length of the input element, and if the longest option is bigger, adjust the input and the outmost div widths.
This answer comes quite late, but since it was not easy for me to come to this solution, I thought it may be worth sharing.
// change dropdowns width to fit the largest option
function fixDropdownWidth() {
var getAllDropdowns = function() {
var dropdowns = [];
dijit.registry.forEach(function(widget, idx, hash) {
if (widget.store) {
var root = widget.store.root;
if (root && root.nodeName.toLowerCase() == 'select') {
dropdowns.push(widget);
}
}
});
return dropdowns;
};
var getTesterElement = function() {
var ret = dojo.query('tester');
if (ret.length) {
return ret;
}
else {
document.body.appendChild(document.createElement('tester'));
return dojo.query('tester');
}
};
var getWidth = function(el) {
var style = dojo.getComputedStyle(el);
return el.clientWidth + parseInt(style.paddingLeft) + parseInt(style.paddingRight);
};
var getOptionWidth = function(option) {
var testEl = getTesterElement();
testEl[0].innerHTML = option.innerHTML;
return getWidth(testEl[0]);
};
var dropdowns = getAllDropdowns();
var testEl = getTesterElement();
dojo.style(testEl[0], {
position: 'absolute',
top: -9999,
left: -9999,
width: 'auto',
whiteSpace: 'nowrap'
});
for (var i = 0; i < dropdowns.length; i++) {
var input = dropdowns[i].textbox;
dojo.style(testEl[0], {
fontSize: dojo.style(input, 'fontSize'),
fontFamily: dojo.style(input, 'fontFamily'),
fontWeight: dojo.style(input, 'fontWeight'),
letterSpacing: dojo.style(input, 'letterSpacing')
});
var max = 0;
var treshold = 5;
dojo.query('option', dropdowns[i].store.root).forEach(function(el, idx, list) {
max = Math.max(max, getOptionWidth(el) + treshold);
});
if (max > getWidth(dropdowns[i].textbox)) {
var icon = dojo.query('.dijitValidationIcon', dropdowns[i].domNode)[0];
dojo.style(dropdowns[i].textbox, {width: max + 'px'});
var width = max + getWidth(icon) + getWidth(dropdowns[i].downArrowNode) + treshold;
dojo.style(dropdowns[i].domNode, {
width: width + 'px'
});
}
}
}
dojo.addOnLoad(function() {
dojo._loaders.push(fixDropdownWidth);
});
var dropDowns = [];
var getAllDropdowns = function (dropDowns) {
array.forEach(dijit.registry.toArray(), function (widget) {
if (widget.store) {
if (widget.domNode.classList.contains("dijitComboBox")) {
dropDowns.push(widget);
}
}
});
};
getAllDropdowns(dropDowns);
var maxLength = 0;
array.forEach(dropDowns, function (dropDown) {
var opts = dropDown.get("store").data;
array.forEach(opts, function (option) {
var optionValue = option[dropDown.get("searchAttr")];
var dropDownCurrentStyle = window.getComputedStyle(dropDown.domNode);
var currentOptionWidth = getTextWidth(optionValue, dropDownCurrentStyle.fontStyle, dropDownCurrentStyle.fontVariant, dropDownCurrentStyle.fontWeight, dropDownCurrentStyle.fontSize, dropDownCurrentStyle.fontFamily);
if (currentOptionWidth > maxLength) {
maxLength = currentOptionWidth;
}
});
dropDown.domNode.style.width = maxLength + "px";
maxLength = 0;
});
function getTextWidth(text, fontStyle, fontVariant, fontWeight, fontSize, fontFamily) {
// re-use canvas object for better performance
var canvas = getTextWidth.canvas || (getTextWidth.canvas = document.createElement("canvas"));
var context = canvas.getContext("2d");
var font = fontStyle + " " + fontVariant + " " + fontWeight + " " + fontSize + " " + fontFamily;
context.font = font;
canvas.innerText = text;
var metrics = context.measureText(text);
return metrics.width + 25; //change this to what you need it to be
}

Resources