Create three.js components with map method - javascript

I've created a react-three-fiber functional component to load a glb file of a butterfly with animation and I am returning a primitive with the scene of the glb passed as an object prop. It is nested in a mesh, that is nested in a scene, that is nested in a group.
function Butterfly({ speed, factor, url, ...props }) {
const { scene, nodes, materials, animations } = useLoader(GLTFLoader, url);
const group = useRef()
const [mixer] = useState(() => new THREE.AnimationMixer())
useEffect(() => mixer.clipAction(animations[0], group.current).play(), [])
useFrame((state, delta) => {
group.current.rotation.y -= Math.sin((delta * factor) / 2) * Math.cos((delta * factor) / 2) * 2
mixer.update(delta * speed)
})
return (
<group ref={group} dispose={null}>
<scene name="Scene" {...props}>
<mesh
name="Object_0"
>
<primitive object={scene}/>
</mesh>
</scene>
</group>
)
}
This component is then returned in separate function in each iteration of an array.
function Butterflies() {
const copyArray = new Array(100).fill()
console.log(copyArray);
return copyArray.map((j, i) => {
const x = (15 + Math.random() * 30) * (Math.round(Math.random()) ? -1 : 1)
const y = -10 + Math.random() * 20
const z = -5 + Math.random() * 10
return <Butterfly key={i} position={[x, y, z]} rotation={[0, x > 0 ? Math.PI : 0, 0]} speed='5' factor='1.5' url='/blue_butterfly.glb' />
})
}
However, this function is only returning one butterfly instead of 100.
single butterfly
I think my issue has something to do with the return in the Butterfly component. I've tried only returning the primitive with props and a ref, but that doesn't render anything. I've tried going through the console.log of the the glb file and found the geometry and tried passing that as a prop to the mesh along with the materials, but that only rendered a white butterfly shape without the animation from the scene. Why is this only returning 1 butterfly instead of 100?

I assume Butterflies() is in Butterflies.js and you are trying return many <Butterfly> here, however react component can only have one parent.
try this in Butterflies():
function Butterflies() {
const copyArray = new Array(100).fill()
console.log(copyArray);
return(
<>
{copyArray.map((j, i) => {
const x = (15 + Math.random() * 30) * (Math.round(Math.random()) ? -1 : 1)
const y = -10 + Math.random() * 20
const z = -5 + Math.random() * 10
return <Butterfly key={i} position={[x, y, z]} rotation={[0, x > 0 ? Math.PI : 0, 0]} speed='5' factor='1.5' url='/blue_butterfly.glb' />
})}
</>)}
Or store it in a variable first, for the example:
function Butterflies() {
const copyArray = new Array(100).fill()
console.log(copyArray);
const items=copyArray.map((j, i) => {
const x = (15 + Math.random() * 30) * (Math.round(Math.random()) ? -1 : 1)
const y = -10 + Math.random() * 20
const z = -5 + Math.random() * 10
return <Butterfly key={i} position={[x, y, z]} rotation={[0, x > 0 ? Math.PI : 0, 0]} speed='5' factor='1.5' url='/blue_butterfly.glb' />
})
return(
<>
{items}
</>
)}

Related

Is it possible to avoid labels overlapping in Pie recharts js?

I used the Pie component from Recharts js and the problem is that I get labels overlapping for labels with the same value.
here is some of my code:
<PieChart>
<Pie dataKey="value"
data={data}
fill="#536A6D"
label nameKey="name"
>
<LabelList dataKey="name" position="insideStart" />
<Pie>
</PieChart>
Is it possible to arrange the labels so that they do not collide with each other?
Thank you in advance!
Yes, you will have to conditionally render the labelline and label attribute. In my case only the zero values overlap so when the value is zero I do not render the value. Other examples online will help with the custom label but there is nothing over the little label line left over, I had this problem and had to dig through the source code to come up with the custom code /:
<Pie
data={dataZ}
cx={150 + wid - pad / 2}
cy={150}
innerRadius={70 + scaler}
outerRadius={100 + scaler}
fill="#8884d8"
paddingAngle={1}
dataKey="value"
label={RenderLabel2}
labelLine={RenderCustomizedLabelLine}
>
let RenderCustomizedLabelLine = function (props: any) {
return (props.value != 0 ? <path stroke={props.stroke} d={`M${props.points[0].x},${props.points[0].y}L${props.points[1].x},${props.points[1].y}`} className="customized-label-line" /> : <polyline stroke={props.stroke} fill="none" />)
}
let RenderLabel2 = function (props: any) {
const RADIAN = Math.PI / 180;
const radius = 25 + props.innerRadius + (props.outerRadius - props.innerRadius);
const x = props.cx + radius * Math.cos(-props.midAngle * RADIAN);
const y = props.cy + radius * Math.sin(-props.midAngle * RADIAN);
return (props.value != 0 ? <text
className="recharts-text recharts-pie-label-text"
x={x}
y={y}
fontSize='16'
fontFamily='sans-serif'
dominantBaseline="central"
cy={props.cy}
cx={props.cx}
fill="#666"
textAnchor={props.x > props.cx ? 'start' : 'end'}
>{Number.isInteger(props.value) ? Number(props.value) : Number(props.value).toFixed(1)}%</text> : <g>
<text x={500} y={y} fill="#transparent" rotate="90"></text>
</g>)
}

React.js output shows as NaN

Hi as stated above my output keeps showing up as NaN and I'm really struggling to fix it.
There are ultimately three classes that will touch the code. App.js, Calculate.js, and Calculations.js where all of the functions are stored. In Calculate.js, I am importing two functions from Calculations.js with the hopes of producing their output after data is taken in from four input fields and a button press. The code for all three files is below.Please help, I truly am lost now.
Edit: it should be noted the calculations were given to us and are basically unmodifiable.
App.js
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import Calculate from './components/Calculate';
import Clear from './components/Clear';
//need to put clear underneath Calculate
export default function App() {
return (
<View style={styles.container}>
<Text> 'Please enter your coordinates you wish to calculate.' </Text>
<Calculate buttonTitle = 'Calculate'/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
Calculate.js
import React, { useState} from 'react';
import {StyleSheet, Text, View, TextInput} from 'react-native';
import {Button} from 'react-native-elements';
import {computeDistance, computeBearing} from './Calculations';
const Calculate = ({buttonTitle, lat1, lon1, lat2,lon2, distance, bearing}) => {
const [state, setState] = useState({lat1: '', lon1: '', lat2: '', lon2: '', distance: '', bearing: ''});
const updateStateObject = (vals) =>{
setState({
...state,
...vals,
});
};
return (
<View style={styles.container}>
<TextInput
placeholder = 'Starting latitude'
onChangeText = {(lat1) => updateStateObject({lat1: lat1})} //or you could do (val) => {setName(val);}
value = {state.lat1}/>
<TextInput
placeholder = 'Starting longitude'
onChangeText = {(lon1) => updateStateObject({lon1: lon1})} //or you could do (val) => {setName(val);}
value = {state.lon1}/>
<TextInput
placeholder = 'Ending latitude'
onChangeText = {(lat2) => updateStateObject({lat2: lat2})} //or you could do (val) => {setName(val);}
value = {state.lat2}/>
<TextInput
placeholder = 'Ending longitude'
onChangeText = {(lon2) => updateStateObject({lon2: lon2})} //or you could do (val) => {setName(val);}
value = {state.lon2}/>
<Button
title= {buttonTitle}
onPress = {() =>{
state.distance = computeDistance(lat1, lon1, lat2, lon2);
state.bearing = computeBearing(lat1, lon1, lat2, lon2);
updateStateObject({distance: `Distance: ${distance}`});
updateStateObject({bearing: `Bearing: ${bearing}`});
}} />
<Text> {state.distance}</Text>
<Text> {state.bearing}</Text>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
export default Calculate;
Calculations.js
// Converts from degrees to radians.
function toRadians(degrees) {
return (degrees * Math.PI) / 180;
}
// Converts from radians to degrees.
function toDegrees(radians) {
return (radians * 180) / Math.PI;
}
// Computes distance between two geo coordinates in kilometers.
export function computeDistance(lat1, lon1, lat2, lon2) {
console.log(`p1={${lat1},${lon1}} p2={${lat2},${lon2}}`);
var R = 6371; // km (change this constant to get miles)
var dLat = ((lat2 - lat1) * Math.PI) / 180;
var dLon = ((lon2 - lon1) * Math.PI) / 180;
var a =
Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos((lat1 * Math.PI) / 180) *
Math.cos((lat2 * Math.PI) / 180) *
Math.sin(dLon / 2) *
Math.sin(dLon / 2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
var d = R * c;
return `${round(d, 3)} km`;
}
// Computes bearing between two geo coordinates in degrees.
export function computeBearing(startLat, startLng, destLat, destLng) {
startLat = toRadians(startLat);
startLng = toRadians(startLng);
destLat = toRadians(destLat);
destLng = toRadians(destLng);
var y = Math.sin(destLng - startLng) * Math.cos(destLat);
var x =
Math.cos(startLat) * Math.sin(destLat) -
Math.sin(startLat) * Math.cos(destLat) * Math.cos(destLng - startLng);
var brng = Math.atan2(y, x);
brng = toDegrees(brng);
return (brng + 360) % 360;
}
function round(value, decimals) {
return Number(Math.round(value + "e" + decimals) + "e-" + decimals);
}
Output
So apparently I have had a misunderstanding on what state is and how it is used. The working button should read as the following:
<Button
title={buttonTitle}
onPress = {() =>{updateStateObject({
distance: `Distance: ${computeDistance( parseFloat(lat1), parseFloat(lon1), parseFloat(lat2), parseFloat(lon2))}`,
bearing: `Bearing: ${computeBearing( parseFloat(lat1), parseFloat(lon1), parseFloat(lat2), parseFloat(lon2))}`,
})
}}
/>

Calculate Wheel Data XY Position

I tried to create a lucky draw wheel using reactjs, first, I need to place all the input data to a certain XY position. Below is the expected output XY position example what I need.
var renderData = ["1","2","3","4","5","6","7","8","9","10","11","12"];
React.createElement('div', { className: '_data'},
renderData.map((index,item)=>{
var itemPosition = index / renderData.length * 360;
var itemX = itemPosition * Math.PI/180;
var itemY = itemPosition * Math.PI/180;
return React.createElement('div', { className: '_items',
style:{top:itemX,left:itemY}
},item);
})
)
So I use createElement to create div for each of the data, then using top and left for XY position.
How to calculate the XY position for each div
Update
After tried the #keikai answer
var renderData = ["1","2","3","4","5","6","7","8","9","10","11","12"];
const r = 350;
const len = renderData.length;
const radiusList = renderData.map(x => (360 / len) * (x - 1));
const positionPairList = radiusList.map(x => ({
x: Math.sin((Math.PI * x) / 180) * r,
y: Math.cos((Math.PI * x) / 180) * r
}));
React.createElement('div', { className: '_data'},
renderData.map((item, index) => {
return React.createElement('div', { className: `_items`,
style:{top:`${positionPairList[index].x.toFixed(2)}px`,left:`${positionPairList[index].y.toFixed(2)}px`}
},item);
})
)
all data are rotated 0deg
child div still not place to the right position inside parent div
for clockwise, it starts from 10?
Update: rotate display with clock styles
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
function App() {
const n = 12;
const r = 500;
const radiusList = Array.from(Array(n).keys()).map(x => (360 / n) * x);
const positionPairList = radiusList.map(item => ({
x: Math.sin((Math.PI * item) / 180) * r,
y: Math.cos((Math.PI * item) / 180) * r
}));
return (
<div className="App">
{positionPairList.map((item, index) => {
const offset = index === 0 ? n : 0;
return (
<div
className="Parts"
style={{
top: `${r - item.y.toFixed(0)}px`,
right: `${r + 200 - item.x.toFixed(0)}px`,
transform: `rotate(${radiusList[index]}deg)`
}}
>
{index + offset}
</div>
);
})}
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Correct Wheel Data XY Position

From the last question I asked Calculate Wheel Data XY Position , I have get the X,Y and Rotation for the div child.
Let say, the parent div width is 414px , so I divide by 2 is 207 for const r
var renderData = ["1","2","3","4","5","6","7","8","9","10","11","12"];
const r = 207;
const len = renderData.length;
const radiusList = Array.from(Array(len).keys()).map(x => (360 / len) * x);
const positionPairList = radiusList.map(x => ({
x: Math.sin((Math.PI * x) / 180) * r,
y: Math.cos((Math.PI * x) / 180) * r
}));
React.createElement('div', { className: '_data'},
renderData.map((item, index) => {
return React.createElement('div', { className: `_items`,
style:{
top:`${r - positionPairList[index].y.toFixed(0)}px`,
right:`${r - positionPairList[index].x.toFixed(0)}px`,
transform: `rotate(${radiusList[index]}deg)`},
},item);
})
)
Below is the output result
The child X and Y still no position in the right place, note that I cannot manually add the top or right position value, the value must use calculation.

React-konva double connected objects with arrow

I'm trying to expand the Connected Objects demo by allowing two nodes (shapes of Circle class) to be double referenced (A connects to B with Arrow1 and B connects to A with Arrow2). I work with react-konva package.
I have implemented a demo on Code Sandbox with some basic functionality.
On line 5, 6 you'll find the Nodes info, on line 21 there exists a high-order component that creates the Arrow based on the start Node and end Node position.
In the default example, the arrows are working as expected. If you try to set the value of redNode.x to 300 the arrows overlap. The same happens when blueNode.x is equal to -100. This has something to do with the way I calculate the arrows (I suspect the equations on line 38).
Also note that as redNode.x moves to value 300, the two arrows approach each other (this happens on other values too), which is something I do not want to happen. I expect the arrows to have the same shape when the two nodes change position and not to overlap or approach each other. Unfortunately, my lack of mathematics does not help me solve the problem. I also tried to create a custom shape using quadraticCurveTo method without success.
Thanks in advance for the help. I appreciate all the solutions.
There are many ways to make curved lines. Here is my attempt to make it better:
import React from "react";
import ReactDOM from "react-dom";
import { Stage, Layer, Circle, Arrow, Text } from "react-konva";
const BLUE_DEFAULTS = {
x: 100,
y: 100,
fill: "blue",
width: 30,
height: 30,
draggable: true
};
const RED_DEFAULTS = {
x: 100,
y: 300,
fill: "red",
width: 30,
height: 30,
draggable: true
};
const Edge = ({ node1, node2 }) => {
const dx = node1.x - node2.x;
const dy = node1.y - node2.y;
let angle = Math.atan2(-dy, dx);
const radius = 20;
const curvePower = 30;
const arrowStart = {
x: node2.x + -radius * Math.cos(angle + Math.PI),
y: node2.y + radius * Math.sin(angle + Math.PI)
};
const arrowEnd = {
x: node1.x + -radius * Math.cos(angle),
y: node1.y + radius * Math.sin(angle)
};
const arrowCurve = {
x:
(arrowStart.x + arrowEnd.x) / 2 +
curvePower * Math.cos(angle + Math.PI / 2),
y:
(arrowStart.y + arrowEnd.y) / 2 +
curvePower * Math.sin(angle - Math.PI / 2)
};
return (
<Arrow
tension={0.2}
points={[
arrowStart.x,
arrowStart.y,
arrowCurve.x,
arrowCurve.y,
arrowEnd.x,
arrowEnd.y
]}
stroke="#000"
fill="#000"
strokeWidth={3}
pointerWidth={6}
/>
);
};
const App = () => {
const [blueNode, updateBlueNode] = React.useState(BLUE_DEFAULTS);
const [redNode, updateRedNode] = React.useState(RED_DEFAULTS);
return (
<Stage width={window.innerWidth} height={window.innerHeight}>
<Layer>
<Text text="Drag any node to see connections change" />
<Edge node1={blueNode} node2={redNode} />
<Edge node1={redNode} node2={blueNode} />
<Circle
{...blueNode}
onDragMove={e => {
updateBlueNode({ ...blueNode, ...e.target.position() });
}}
/>
<Circle
{...redNode}
onDragMove={e => {
updateRedNode({ ...redNode, ...e.target.position() });
}}
/>
</Layer>
</Stage>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Demo: https://codesandbox.io/s/react-konva-double-connected-objects-m5g22

Categories