I'm working on some generative visuals using paper.js, and the plan is to define a random shape, subdivide it, and then randomize those points as well (to a different degree). Right now, the browser is crashing while attempting to subdivide a polygon.
I'm relatively new to doing these things in Javascript, so maybe I am expecting too much out of it, apparently crashes within intensive for loops are just something that happen. Does anyone have some tips?
var Cloud = function(point) {
this.origin = point;
this.initBound = new Size(600,200);
this.RoM = 40;
var numsides = 6;
var scalingFactor = new Point(1,this.initBound.height/this.initBound.width);
var cloud = new Path.RegularPolygon({
center: this.origin,
sides: numsides,
radius: this.initBound.width/2,
strokeColor: 'black'
});
cloud.scaling = scalingFactor;
var initBoundBox = new Path.Rectangle({
point: new Point(point.x-this.initBound.width/2,point.y-this.initBound.height/2),
size: this.initBound,
strokeColor: 'red'
});
for(var i=0;i<numsides;i++){
var px = cloud.segments[i].point.x;
var py = cloud.segments[i].point.y;
var x = Math.floor(Math.random()*( (px+this.RoM)-(px-this.RoM)+1 ) + (px-this.RoM) );
var y = Math.floor(Math.random()*( (py+this.RoM)-(py-this.RoM)+1 ) + (py-this.RoM) );
var tmpP = new Point(x,y);
cloud.segments[i].point = tmpP;
}
for(var i=0;i<cloud.segments.length-1;i++){
var mdPnt = new Point((cloud.segments[i].point.x+cloud.segments[i+1].point.x)/2,(cloud.segments[i].point.y+cloud.segments[i+1].point.y)/2);
cloud.add(i,mdPnt); //breaking here
}
//cloud.smooth();
}
new Cloud(new Point(500,300));
In your last for-loop, you're adding a segment in each iteration, and thereby increasing cloud.segments.length by one. Your loop never ends. You can mitigate this by incrementing by 2 instead of 1, or finding a better bisection routine.
In short, try this as a starting point:
for(var i=0;i<cloud.segments.length-1;i+=2){
var mdPnt = new Point((cloud.segments[i].point.x+cloud.segments[i+1].point.x)/2,(cloud.segments[i].point.y+cloud.segments[i+1].point.y)/2);
cloud.add(i,mdPnt); //breaking here
}
Related
I've got the below code, which takes in a number of scans. I know this to always be 2880. The canvas should split an entire 360° into 2880 sectors. The loop in the code below will always run from 0 to 2880, and in each loop, a bunch of (maybe several hundred) 2px coloured points are rendered in that sector, emanating from the centre of the canvas outward. The loop moves fast, before I upgraded the THREE package, this loop could render in c. 15 seconds.
The picture draws correctly, but what confuses me is the fact that the call to THREE's render message happens inside of the loop, yet the picture draws nothing until the last iteration of the loop is complete and then all 2880 sectors appear at once, which isn't the effect I'm going for.
Can anyone advise what I might be missing? It's a 2-D non-interactable image.
Stuff I've tried:-
setTimeout(null, 1000) after the .render() method to make it wait before executing the next iteration of the loop
Considered making it a recursive function with the next iteration of the loop inside of the above setTimeout
Reversing the THREE upgrade as an absolute last resort.
Stuff I've considered:-
Is the loop running two fast for the frame rate or not giving the screen enough time to update?
Limitation of THREEJs?
const drawReflectivityMap = (scans, reflectivityData, azimuthData, scene, renderer, totalScans, currentScan, cameraPosition, camera, reflectivityColours) => {
currentCamera = camera;
currentRenderer = renderer;
for (let i = 0; i < scans; i++) {
console.log('Drawing Reflectivity ' + i);
var reflectivity = reflectivityData[i];
var azimuth = utils.radians(azimuthData[i]);
var sinMultiplier = Math.sin(azimuth);
var cosMultiplier = Math.cos(azimuth);
var initialRange = mikeUtilities.addRange(mikeUtilities.multiplyRange(mikeUtilities.createRange(0, reflectivity.GateCount, 1), reflectivity.GateSize), reflectivity.FirstGate);
var x = utils.multiplyRange(initialRange, sinMultiplier);
var y = utils.multiplyRange(initialRange, cosMultiplier);
var dataSet = {
x: x,
y: y,
reflectivity: reflectivity
};
var reflectivityColourScale = d3.scaleQuantize().domain([-32.0, 94.5]).range(reflectivityColours);
var pointsMaterial = new THREE.PointsMaterial({
size: 2,
vertexColors: true,
sizeAttenuation: false
});
// x co-ordinate points for each point in this arc
var x = dataSet.x;
// y co-ordinate points for each point in this arc
var y = dataSet.y;
// Reflectivity (rainfall) intensity values for each point in this arc
var reflectivity = dataSet.reflectivity;
var geometry = new THREE.BufferGeometry();
var pointsGraph = [];
var coloursGraph = [];
x.forEach(function (index, i) {
if (reflectivity.MomentDataValues[i] > -33) {
geometry = new THREE.BufferGeometry();
var dataPointColour = new THREE.Color(reflectivityColourScale(reflectivity.MomentDataValues[i]));
pointsGraph.push(x[i], y[i], 0);
coloursGraph.push(dataPointColour.r, dataPointColour.g, dataPointColour.b);
}
});
var pointsGraphArray = new Float32Array(pointsGraph);
var coloursGraphArray = new Float32Array(coloursGraph);
geometry.setAttribute('position', new THREE.BufferAttribute(pointsGraphArray, 3));
geometry.setAttribute('color', new THREE.BufferAttribute(coloursGraphArray, 3));
var pointsMap = new THREE.Points(geometry, pointsMaterial);
scene.add(pointsMap);
renderScene(scene, cameraPosition, renderer);
}
}
function renderScene(scene, cameraPosition,renderer) {
currentCamera.position.z = cameraPosition;
currentRenderer.render(scene, currentCamera);
requestAnimationFrame(renderScene);
}
for (let i = 0; i < scans; i++) {
setTimeout(() => {
// Your code goes here
} i * 100)
}
I'm trying to do:
Using this code:
var p = new Path();
p.strokeColor = 'blue'
p.strokeWidth = 4
var size = 10
var o = new Point(100, 100)
p.moveTo (o.add ([-size, -size]))
p.lineTo (o.add ([size, size]))
p.moveTo (o.add ([size, -size]))
p.lineTo (o.add ([-size, size]))
But instead I get:
Apparently the second moveTo() is ignored. How could I make it work? I couldn't find anything in the documentation.
The solution in your case is to use CompoundPath instead of Path as you are intending to work on a group of paths.
There is a clue about that in the path.moveTo documentation:
On a normal empty Path, the point is simply added as the path’s first segment. If called on a CompoundPath, a new Path is created as a child and the point is added as its first segment.
Here is a sketch adapted from your initial code, demonstrating the solution.
var p = new CompoundPath();
p.strokeColor = 'blue';
p.strokeWidth = 4;
var size = 10;
var o = new Point(100, 100);
p.moveTo(o.add([-size, -size]));
p.lineTo(o.add([size, size]));
p.moveTo(o.add([size, -size]));
p.lineTo(o.add([-size, size]));
I ended up doing:
var s = 10 // size
var o = new Point(100, 100)
var p1 = new Path.Line (o.add ([-s, -s]), o.add ([s, s]))
var p2 = new Path.Line (o.add ([s, -s]), o.add ([-s, s]))
p2.strokeColor = p1.strokeColor = 'blue'
p2.strokeWidth = p1.strokeWidth = 4
Which is inelegant, but works. I have tried p1.unite (p2) but that also doesn't work.
I want to create a Pine using 2 meshes, 1 for the trunk and another one for the bush, this what I've done:
var pine_geometry = new THREE.Geometry();
var pine_texture_1 = THREE.ImageUtils.loadTexture('./res/textures/4.jpg');
var pine_geometry_1 = new THREE.CylinderGeometry(25, 25, 50, 6);
var pine_material_1 = new THREE.MeshBasicMaterial({
map : pine_texture_1
});
var pine_1 = new THREE.Mesh(pine_geometry_1);
pine_1.position.x = x;
pine_1.position.y = y + 25;
pine_1.position.z = z;
pine_1.updateMatrix();
pine_geometry.merge(pine_1.geometry, pine_1.matrix);
var pine_texture_2 = THREE.ImageUtils.loadTexture('./res/textures/5.jpg');
var pine_geometry_2 = new THREE.CylinderGeometry(0, 70, 250, 8);
var pine_material_2 = new THREE.MeshBasicMaterial({
map : pine_texture_2
});
var pine_2 = new THREE.Mesh(pine_geometry_2);
pine_2.position.x = x;
pine_2.position.y = y + 175;
pine_2.position.z = z;
pine_2.updateMatrix();
pine_geometry.merge(pine_2.geometry, pine_2.matrix);
var pine = new THREE.Mesh(pine_geometry, new THREE.MeshFaceMaterial([pine_material_1, pine_material_2]));
pine.geometry.computeFaceNormals();
pine.geometry.computeVertexNormals();
Game.scene.add(pine);
The Pine is correctly positioned as I want, however, the whole merged shape only uses 1 material instead of the 2 (the whole shape is covered by the 1st) and I want that each mesh has it's respective material when mergin both.
What I'm doing wrong? any idea?
After a long research I discovered that I was missing an extra parameter for the method 'merge' from the Geometry object, the last parameter is the index of the material that the mesh must have from the materials array, ex: 0 -> first material in 'materials' array... and so on.
So, my final piece of code looks like:
pine_geometry.merge(pine_1.geometry, pine_1.matrix, 0);
var pine_texture_2 = THREE.ImageUtils.loadTexture('./res/textures/5.jpg');
var pine_geometry_2 = new THREE.CylinderGeometry(0, 70, 250, 8);
var pine_material_2 = new THREE.MeshBasicMaterial({
map : pine_texture_2
});
var pine_2 = new THREE.Mesh(pine_geometry_2);
pine_2.position.x = x;
pine_2.position.y = y + 175;
pine_2.position.z = z;
pine_2.updateMatrix();
pine_geometry.merge(pine_2.geometry, pine_2.matrix, 1);
(Note the last numbers I add to each merge).
However, I want to clarify that this practice only works when we are dealing with various geometries that are from the same type, in this case, we're merging two CylinderGeometry, but if we wanted to merge for example a Cylinder with a Box AND add the MeshFaceMaterial, it wouldn't be recognized properly and the console will throw us 'Cannot read property map/attributes from undefined', nevertheless we can still merge both geometries but not providing multiple materials (that's a terrible mistake I made).
Hope this helps to anyone.
Here's a general function to merge meshes with materials, You can also specify if you want it to return it as a buffer geometry.
function _mergeMeshes(meshes, toBufferGeometry) {
var finalGeometry,
materials = [],
mergedGeometry = new THREE.Geometry(),
mergeMaterial,
mergedMesh;
meshes.forEach(function(mesh, index) {
mesh.updateMatrix();
mesh.geometry.faces.forEach(function(face) {face.materialIndex = 0;});
mergedGeometry.merge(mesh.geometry, mesh.matrix, index);
materials.push(mesh.material);
});
mergedGeometry.groupsNeedUpdate = true;
mergeMaterial = new THREE.MeshFaceMaterial(materials);
if (toBufferGeometry) {
finalGeometry = new THREE.BufferGeometry().fromGeometry(mergedGeometry);
} else {
finalGeometry = mergedGeometry;
}
mergedMesh = new THREE.Mesh(finalGeometry, mergeMaterial);
mergedMesh.geometry.computeFaceNormals();
mergedMesh.geometry.computeVertexNormals();
return mergedMesh;
}
var mergedMesh = _mergeMeshes([trunkMesh, treeTopMesh], true);
Three js ver67
Current code something like this -
var materials = [];
var totalGeom = new THREE.Geometry();
var cubeMat;
for (var i = 0; i < dataSetArray.length; i++) {
var ptColor = dataSetArray[i].color;
var value = dataSetArray[i].value;
var position = latLongToVector3(dataSetArray[i].y, dataSetArray[i].x, 600, 1);
var cubeGeom = new THREE.BoxGeometry(5, 5, 1 + value / 20);
cubeMat = new THREE.MeshLambertMaterial({
color: new THREE.Color(ptColor),
opacity: 0.6
});
materials.push(cubeMat);
// cubeGeom.updateMatrix();
var cubeMesh = new THREE.Mesh(cubeGeom, cubeMat);
cubeMesh.position = position;
cubeMesh.lookAt(scene.position);
// totalGeom.merge(cubeMesh.geometry, cubeMesh.geometry.matrix);
//THREE.GeometryUtils.setMaterialIndex(cubeMesh.geometry, i);
THREE.GeometryUtils.merge(totalGeom, cubeMesh);
}
var total = new THREE.Mesh(totalGeom, new THREE.MeshFaceMaterial(materials));
scene.add(total);
However I get the message
DEPRECATED: GeometryUtils's .merge() has been moved to Geometry. Use geometry.merge( geometry2, matrix, materialIndexOffset ) instead.
in chrome dev tools.
When I try something like -
totalGeom.merge(cubeMesh.geometry, cubeMesh.geometry.matrix);
instead of THREE.GeometryUtils.merge(totalGeom, cubeMesh); I get exceptions.
How will I do the above merge? Please help.
Note: This answer applies to legacy versions of three.js.
Do this:
cubeMesh.updateMatrix();
totalGeom.merge( cubeMesh.geometry, cubeMesh.matrix );
For a further understanding, see the source code of THREE.Geometry.merge().
three.js r.69
I work about one project in Three.js and I must solve one problem as fast as possible. I need to calculate few volumes of an 3D object.
First what I need is exactly the same like here:
How to calculate the volume of a 3D mesh object the surface of which is made up triangles
And I calculate it in the same way. This is an entire object volume.
That is enough if object doesn't need an additional material when he goes into 3D printing. For example if that will be a glass (an open cylinder) i need to calculate a volume of additional material, that will be used in printing of this model. That material will be "inside" the glass and will prevent a floor of the glass from falling inside the model.
I don't know how to calculate that, too less math knowledge I think. Is there a way to calulate this? Or maybe is there a way to calculate a "closed-mesh volume" even, when this is an open one (like glass)?
EDIT: I do not have a glass model, but I have a model of a box. The box will be printed with the open side on the bottom. I need to know the "inside" volume.
Sorry for my english, hope it's understandable.
Here is how I calculate volume of object (exactly like in link in 1st post):
function volumeOfT(p1, p2, p3){
var v321 = p3.x*p2.y*p1.z;
var v231 = p2.x*p3.y*p1.z;
var v312 = p3.x*p1.y*p2.z;
var v132 = p1.x*p3.y*p2.z;
var v213 = p2.x*p1.y*p3.z;
var v123 = p1.x*p2.y*p3.z;
return (-v321 + v231 + v312 - v132 - v213 + v123)/6.0;
}
function calculateVolume(object){
var volumes = 0.0;
for(var i = 0; i < object.geometry.faces.length; i++){
var Pi = object.geometry.faces[i].a;
var Qi = object.geometry.faces[i].b;
var Ri = object.geometry.faces[i].c;
var P = new THREE.Vector3(object.geometry.vertices[Pi].x, object.geometry.vertices[Pi].y, object.geometry.vertices[Pi].z);
var Q = new THREE.Vector3(object.geometry.vertices[Qi].x, object.geometry.vertices[Qi].y, object.geometry.vertices[Qi].z);
var R = new THREE.Vector3(object.geometry.vertices[Ri].x, object.geometry.vertices[Ri].y, object.geometry.vertices[Ri].z);
volumes += volumeOfT(P, Q, R);
}
loadedObjectVolume = Math.abs(volumes);
}
I checked in other software and Volume of object is correct.
Here is how I tried to create a ConvexGeometry:
var geometry = new THREE.STLLoader().parse( contents );
geometry.sourceType = "stl";
geometry.sourceFile = file.name;
geometry.computeFaceNormals();
geometry.computeVertexNormals();
var convexGeometry = THREE.ConvexGeometry(geometry.vertices);
var material = new THREE.MeshLambertMaterial({
color: 0xff0000,
emissive: 0x000000,
shading: THREE.FlatShading
});
var mesh = new THREE.Mesh( geometry, material );
On line var convexGeometry = THREE.ConvexGeometry(geometry.vertices); browser hangs. Only models with low verticies can go through this (cube for example).