AmChart- After updating fieldMappings and dataSet,Graph is not plotting - amcharts

Problem Statement:
after updating fieldMappings and dataSet during runtime(After clicking on a button) for a stockgraph, validateNow() / validteData() is not plotting the graph.
Note: MACD0 is added from 25th element onward and expoSignalLine0 is added from 33rd element onward in the dataprovider and fieldMapping is also getting updated and can be verified same in console.enter code here
Following is the Code snippet:
(addMACD function is called on click of a button)
function addMACD() {
var chart = AmCharts.charts[ 0 ];
AmCharts.MACDGraphs = 0;
AmCharts.expoSignalLineGraphs = 0;
var MACDField = "MACD"+ AmCharts.MACDGraphs;
var expoSignalLineField = "expoSignalLine"+ AmCharts.expoSignalLineGraphs;
chart.dataSets[0].fieldMappings.push( {
fromField : MACDField,
toField : MACDField
},
{
fromField : expoSignalLineField,
toField : expoSignalLineField
});
var currClose;
var prevClose;
var twelveDayEMA =[];
var twentySixDayEMA =[];
var MACDarray = [];
var signalLineArray = [];
var MACDperiod = 9 ;// 9 day exponential average
for ( var i = 1; i < (chart.dataSets[0].dataProvider.length); i++) {
var dp = chart.dataSets[0].dataProvider[i - 1];
prevClose = parseFloat(dp["close"]);
var dp = chart.dataSets[0].dataProvider[i];
currClose = parseFloat(dp["close"]);
if( i==1){
twelveDayEMA[i] = (0.15*currClose) + (0.85*prevClose);
twentySixDayEMA[i] = (0.075*currClose) + (0.925*prevClose);
}
else{
twelveDayEMA[i] = (0.15*currClose) + (0.85*twelveDayEMA[i - 1]);
twentySixDayEMA[i] = (0.075*currClose) + (0.925*twentySixDayEMA[i - 1]);
}
if(i >= 25){
MACDarray[i] = twelveDayEMA[i] - twentySixDayEMA[i] ;
dp[MACDField] = MACDarray[i];
if(i == 25){
signalLineArray[i] = MACDarray[i];
}
else{
signalLineArray[i] = ( MACDarray[i]*(2/( MACDperiod + 1)) ) + ( signalLineArray[i - 1]*(1-(2/( MACDperiod + 1))) )
}
}
if(i >=33){
dp[expoSignalLineField] = signalLineArray[i];
}
}
console.log(chart);
if ( chart.panels.length == 1 || chart.panels.length == 2 || chart.panels.length == 3 || chart.panels.length == 4 || chart.panels.length == 5) {
var newPanel = new AmCharts.StockPanel();
newPanel.allowTurningOff = true;
newPanel.title = "MACD";
newPanel.showCategoryAxis = false;
graph1 = new AmCharts.StockGraph();
graph1.valueField = MACDField;
graph1.useDataSetColors = false;
graph1.lineColor="#6699FF";
graph1.title = "MACD";
newPanel.stockGraphs.push( graph1 );
graph2 = new AmCharts.StockGraph();
graph2.valueField =expoSignalLineField;
graph2.useDataSetColors = false;
graph2.lineColor = "#990000";
graph2.title = "MACD2";
newPanel.stockGraphs.push( graph2 );
var legend = new AmCharts.StockLegend();
legend.markerType = "none";
legend.markerSize = 0;
newPanel.stockLegend = legend;
chart.addPanelAt( newPanel, 1 );
chart.validateData();
chart.validateNow();
//chart.write("chartdiv");
}
}

You have to call validateNow first, then call validateData.
Alternatively, you can call validateNow(true, false) which has the same effect as calling the two functions separately.
Updated fiddle

Related

handling objects offscreen for a view frustum

I have a view frustum that works great when looking at stuff from a distance. But for example, when i stand in the middle of a square, my current system struggles to place vertices which are behind me.
For example the image below demonstrates looking at a tile from a distance vs standing on top of it ...
My proccess for putting things onto screen can be described in 5 steps
cross product the co-ordinates of the object with my camera matrix
cross product the result of 1 with my projection_matrix
normalize the result of 2 by dividing through the 4th dimension of my co-ordinates (my w co-ordinate)
cull results of 3 (its not causing the problem, i tried without culling)
cross product 4 with to_screen_matrix
Basically the problem i have, is this procces is great at putting things on the screen but sometimes an object isnt on the screen... what co-ordinates should be used then?
Below is a drawing of what i think the problem is
below is my screen_projection function
return_screen_projection(dont_cull = false){
var position = cross_product(this.position , player.camera_matrix())
position = cross_product(position , projection.projection_matrix) // does this just convert the position to cameras reference frame.
for (let i = 0; i < position.length; i++) {
position[i] = position[i]/position[3]
}
if (dont_cull == false){
for (let i = 0; i < position.length; i++) {
if (i != 1){
if (this.is_this_object_behind_player()){for (let ii = 0; ii < position.length; ii++) {position[ii] = -9999;} console.log("culling1")}
if (position[i] > 2){for (let ii = 0; ii < position.length; ii++) {position[ii] = -9999;} console.log("culling2")}
if (position[i] < -2){for (let ii = 0; ii < position.length; ii++) {position[ii] = -9999;} console.log("culling3")}
}
} // also all examples say set position = 0 if culling
}
position = cross_product(position , projection.to_screen_matrix)
return [position[0],position[1]]
}
how could i better handle the position of a vertex offscreen, when some vertexes of the object im dealing with are on screen?
################## extra info below
below is a blob of 300 lines of code (sorry , i cant make it more minimal and reproducable with just a copy n paste)
running the below in a web browser will give you an example of the problem (w,a,s,d) to move (you start mired in the middle of an object, you may wish to step back to see better the first time)
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjs/11.3.3/math.js"></script>
<script async src="https://unpkg.com/es-module-shims#1.3.6/dist/es-module-shims.js"></script>
</head>
<body>
<div id="canvas div" style = "position: relative; left: 0px; float:left; top: 0px;" >
<h1> first person below </h1>
<canvas id="mi_canvas" width="300" height="300" style="border-style: solid;"></canvas> <br>
</div>
<script>
var floor_y_pos = 9
canvas = document.getElementById("mi_canvas");
ctx = canvas.getContext("2d");
render_distance = 1000;
fov = math.pi / 2
class Projection{
constructor(){
var NEAR = player.near_plane
var FAR = player.far_plane
var RIGHT = Math.tan(player.h_fov/2)
var LEFT = - RIGHT
var TOP = Math.tan(player.v_fov /2)
var BOTTOM = -TOP
var m00 = 2*NEAR / (RIGHT - LEFT)
var m02 = (RIGHT + LEFT)/(RIGHT - LEFT)
var m11 = 2*NEAR / (TOP - BOTTOM)
var m12 = (TOP + BOTTOM) /(TOP - BOTTOM)
var m22 = (FAR * NEAR) / (FAR - NEAR)
var m23 = -2 * NEAR * FAR / (FAR-NEAR)
this.projection_matrix = [
[-m00,0,m02,0],
[0,m11,0,0],
[m02,m12,-m22,-1],
[0,0,m23,0]
]
var HW=player.H_WIDTH
var HH = player.H_HEIGHT
this.to_screen_matrix = [
[HW,0,0,0],
[0,HH,0,0],
[0,0,1,0],
[HW,HH,0,1]
]
}
}
function multiply(a, b) {
var aNumRows = a.length, aNumCols = a[0].length,
bNumRows = b.length, bNumCols = b[0].length,
m = new Array(aNumRows); // initialize array of rows
for (var r = 0; r < aNumRows; ++r) {
m[r] = new Array(bNumCols); // initialize the current row
for (var c = 0; c < bNumCols; ++c) {
m[r][c] = 0; // initialize the current cell
for (var i = 0; i < aNumCols; ++i) {
m[r][c] += a[r][i] * b[i][c];
}
}
}
return m;
}
function mi_position_matrix_multiplier(A, B)
{
var new_matrix = []
for (var new_num_ind = 0; new_num_ind < A.length; ++new_num_ind)
{
this_num = 0;
for (var a_ind = 0; a_ind < A.length; ++a_ind)
{
this_num += (A[a_ind] * B[a_ind][new_num_ind])
}
new_matrix.push(this_num)
}
return new_matrix;
}
function pythagoras(thing1, thing2)
{
dist = (((thing1[0]-thing2[0])**2)+((thing1[1]-thing2[1])**2))**0.5
return dist
}
class vertex{
constructor(x, y,z , id){
this.id = id
this.position = [x,y,z,1]
this.min_dist = 1.5 // minimum possible distance between player and object
}
is_this_object_behind_player(){
var arrow_length = 0.0001;
var pointing_position = [player.position[0]+(player.forward[0]*arrow_length) , player.position[2]-(player.forward[2]*arrow_length)]
var dist1 = pythagoras([this.position[0],this.position[2]], pointing_position)
var dist2 = pythagoras([this.position[0],this.position[2]], [player.position[0],player.position[2]])
if (dist1 < dist2){
return true;}
else if (dist1 > dist2){
return false;}
else{}
}
return_screen_projection(dont_cull = false){
var position = mi_position_matrix_multiplier(this.position , player.camera_matrix())
position = mi_position_matrix_multiplier(position , projection.projection_matrix) // does this just convert the position to cameras reference frame.
for (let i = 0; i < position.length; i++) {
position[i] = position[i]/position[3]
}
if (dont_cull == false){
for (let i = 0; i < position.length; i++) {
if (i != 1){
if (this.is_this_object_behind_player()){for (let ii = 0; ii < position.length; ii++) {position[ii] = -9999;} console.log("culling1")}
if (position[i] > 2){for (let ii = 0; ii < position.length; ii++) {position[ii] = -9999;} console.log("culling2")}
if (position[i] < -2){for (let ii = 0; ii < position.length; ii++) {position[ii] = -9999;} console.log("culling3")}
}
} // also all examples say set position = 0 if culling
}
position = mi_position_matrix_multiplier(position , projection.to_screen_matrix)
return [position[0],position[1]]
}
}
class player{
constructor(){
this.position =[0,0,0,1.0]
this.forward = [0,0,1,1]
this.up = [0,1,0,1]
this.right =[1,0,0,1]
this.h_fov = 3.1415926535/3
this.v_fov = this.h_fov * (canvas.height / canvas.width)
this.near_plane = 1
this.far_plane = 100
this.moving_speed = 0.2
this.rotation_speed = 0.1
this.H_WIDTH = canvas.width/2
this.H_HEIGHT = canvas.height/2
this.anglePitch = 0
this.angleYaw = 0
}
set_camera_angle(){
var rotate = multiply(rotate_x(this.anglePitch) , rotate_y(this.angleYaw))
this.forward = [0, 0, 1, 1]
this.up = [0, 1, 0, 1]
this.right = [1, 0, 0, 1]
this.forward = mi_position_matrix_multiplier(this.forward , rotate)
this.right = mi_position_matrix_multiplier(this.right , rotate)
this.up = mi_position_matrix_multiplier(this.up , rotate)
}
camera_yaw(angle){
this.angleYaw += angle}
translate_matrix(self){
var x = this.position[0];
var y = this.position[1];
var z = this.position[2];
var w = this.position[3];
return [
[1,0,0,0],
[0,1,0,1],
[0,0,1,0],
[-x,-y,z, 1]
]}
rotate_matrix(){
var rx = this.right[0]
var ry = this.right[1]
var rz = this.right[2]
var w = this.right[3]
var fx = this.forward[0]
var fy = this.forward[1]
var fz = this.forward[2]
var w = this.forward[3]
var ux = this.up[0]
var uy = this.up[1]
var uz = this.up[2]
var w = this.up[3]
return [
[rx,ux,fx,0],
[ry,uy,fy,0],
[rz,uz,fz,0],
[0,0,0,1]
]
}
camera_matrix(){
return multiply(this.translate_matrix(), this.rotate_matrix());
}
move(event)
{
var key_code = parseInt(event.keyCode)
if (key_code == 37 || key_code == 39 || key_code == 83 || key_code == 87 || key_code == 119|| key_code == 115)
{
var dx = Math.cos(this.angleYaw)*this.moving_speed
var dy = Math.sin(this.angleYaw)*this.moving_speed
// console.log("that were moving = dx , dy = "+dx.toString()+" , "+dy.toString())
if ( key_code == 37 || key_code == 87 || key_code == 119) {
this.position[0] += -dy
this.position[2] += dx
}
if (key_code == 39 || key_code == 83 || key_code == 115) {
for (let i = 0; i < this.position.length; i++) {
this.position[0] += dy
this.position[2] += -dx
}
}
}
else {
if ( key_code == 38 || key_code == 65 || key_code == 97) {
this.camera_yaw(-this.rotation_speed)
}
if (key_code == 40 || key_code == 68 || key_code == 100) {
this.camera_yaw(this.rotation_speed)
}
this.set_camera_angle()
}
}
}
function translate(pos){
tx,ty,tz=pos
return np.array([
[1,0,0,0],
[0,1,0,0],
[0,0,1,0],
[tx,ty,tz,1]
])}
function rotate_x(angle){
return [
[1,0,0,0],
[0,Math.cos(angle),Math.sin(angle),0],
[0,-Math.sin(angle),Math.cos(angle),0],
[0,0,0,1]
]
}
function rotate_y(a){
return [
[math.cos(a),0, -math.sin(a),0],
[0,1,0,0],
[math.sin(a), 0 , math.cos(a),0],
[0,0,0,1]
]
}
function update_matrix_info_debug(matrix_name, matrix){
if (matrix[0].length > 1)
{
for (let x = 1; x < matrix.length+1; x++) {
for (let y = 1; y < matrix.length+1; y++) {
document.getElementById(matrix_name.toString()+"_"+x.toString()+y.toString()).innerHTML = matrix[x-1][y-1]
}
}
}
else {
for (let x = 1; x < matrix.length+1; x++) {document.getElementById(matrix_name.toString()+"_"+"1"+x.toString()).innerHTML = matrix[x-1]}
}
}
class two_d_surdace {
constructor(verex1,verex2,verex3,verex4 , colour){
this.vertices = [verex1,verex2,verex3,verex4]
this.colour = colour
}
draw_all_faces(){
var each_point = []
for (let i = 0; i < this.vertices.length; i++) {
each_point.push(this.vertices[i].return_screen_projection(true))
}
ctx.fillStyle = this.colour;
var moved_to_first_yet = false
for (let vertex = 0; vertex < this.vertices.length; vertex++)
{
if (moved_to_first_yet == false)
{
moved_to_first_yet = true
ctx.moveTo( each_point[vertex][0],each_point[vertex][1]);
}
else{ctx.lineTo( each_point[vertex][0],each_point[vertex][1]);}
}
ctx.closePath();
ctx.fill();
}
}
function if_off_screen(x, y)
{
if (x> canvas.width || x < 0){ return true;}
if (y > canvas.height || y < 0){ return true;}
return false;
}
function if_most_of_these_numbers_are_off_screen(numbers){
var threshold = 1; //Math.floor(numbers.length*0.49)
var counter = 0
for (let i = 0; i < numbers.length; i++) { if (if_off_screen(numbers[i][0], numbers[i][1])){ counter +=1} else{} }
if (counter >= threshold){return true}
return false;
}
player = new player();
projection = new Projection()
floor = new two_d_surdace(new vertex(50,floor_y_pos,50) , new vertex(-50,floor_y_pos,50) , new vertex(-50,floor_y_pos,-50) , new vertex(50,floor_y_pos,-50) , '#F90' )
$(document).on("keypress", function (event) {
player.move(event)
ctx.beginPath();
ctx.clearRect(0, 0, canvas.width, canvas.height);
floor.draw_all_faces()
});
</script>
</body>

Data Validation with possible loops

This is a functioning script for dynamic data validation set based on some old youtube videos
It's slow and I'm not sure where there might be a loop that I can remove
Had never used scripts until 4 hours ago; can anyone please make this run faster
Any advice appreciated!
function onEdit() {
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var datass = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Reference Sheet");
if(ss.getName() == "Test Sheet" && ss.getActiveCell().getColumn() == 10 && ss.getActiveCell().getRow() > 7){
ss.getActiveCell().offset(0, 1).clearContent().clearDataValidations();
var materials = datass.getRange(1, 1, 1, datass.getLastColumn()).getValues();
var materialIndex = materials[0].indexOf(ss.getActiveCell().getValue()) + 1;
if(materialIndex != 0){
var validationRangeMU = datass.getRange(2, materialIndex, datass.getLastRow());
var validationRuleMU = SpreadsheetApp.newDataValidation().requireValueInRange(validationRangeMU).build();
ss.getActiveCell().offset(0, 1).setDataValidation(validationRuleMU);
}
}
if(ss.getName() == "Test Sheet" && ss.getActiveCell().getColumn() == 15 && ss.getActiveCell().getRow() > 7){
ss.getActiveCell().offset(0, 1).clearContent().clearDataValidations();
var materials = datass.getRange(1, 1, 1, datass.getLastColumn()).getValues();
var materialIndex = materials[0].indexOf(ss.getActiveCell().getValue()) + 1;
if(materialIndex != 0){
var validationRangeMU = datass.getRange(2, materialIndex, datass.getLastRow());
var validationRuleMU = SpreadsheetApp.newDataValidation().requireValueInRange(validationRangeMU).build();
ss.getActiveCell().offset(0, 1).setDataValidation(validationRuleMU);
}
}
if(ss.getName() == "Test Sheet" && ss.getActiveCell().getColumn() == 20 && ss.getActiveCell().getRow() > 7){
ss.getActiveCell().offset(0, 1).clearContent().clearDataValidations();
var materials = datass.getRange(1, 1, 1, datass.getLastColumn()).getValues();
var materialIndex = materials[0].indexOf(ss.getActiveCell().getValue()) + 1;
if(materialIndex != 0){
var validationRangeMU = datass.getRange(2, materialIndex, datass.getLastRow());
var validationRuleMU = SpreadsheetApp.newDataValidation().requireValueInRange(validationRangeMU).build();
ss.getActiveCell().offset(0, 1).setDataValidation(validationRuleMU);
}
}
Try this:
Just taking maximum advantage of the event object
function onEdit(e) {
var idx = [10, 15, 20].indexOf(e.range.columnStart)
if (e.range.getSheet().getName() == "Test Sheet" && ~idx && e.range.rowStart > 7) {
var rsh = e.source.getSheetByName("Reference Sheet");
e.range.offset(0, 1).clearContent().clearDataValidations();
var materials = rsh.getRange(1, 1, 1, rsh.getLastColumn()).getValues();
var materialIndex = materials[0].indexOf(e.value) + 1;
if (materialIndex != 0) {
var validationRangeMU = rsh.getRange(2, materialIndex, rsh.getLastRow());
var validationRuleMU = SpreadsheetApp.newDataValidation().requireValueInRange(validationRangeMU).build();
e.range.offset(0, 1).setDataValidation(validationRuleMU);
}
}
}

I want to Group questions and answers LINQ and have them display properly

I'm trying to group properly to have my cell distribute eveningly. The printout is coming very odd and uneven.
is it the table i'm creating or group or both? I think the group is correct. My results are as shown in the image below.
Gold is the heading,
Green are the questions,
Red are the answers
Mt table is is below
var Sections = new OncologySection().SelectSections(projectID.ToString());
int iSection = 0;
int igroups = 0;
int ianswer = 0;
tb.CssClass = "";
tb.BorderWidth = 1;
tb.Width = new Unit("780px");
tb.Attributes.Add("runat", "server");
foreach (OncologySection section in Sections)
{
TableRow row1 = new TableRow();
iSection++;
// var getDistinctQuestion = getVoterAnswerstoList.Select(s => s.QuestionText ,s.Id).Distinct().ToList();
var getVoterAnswerstoList = new OncologyGeneratePDFDAL().DataforPDFCreation(Convert.ToInt32(projectID), Convert.ToInt32(voterid), Convert.ToInt32(caseId), Convert.ToInt32(voteSurveyId), Convert.ToInt32(section.SectionID)).OrderBy(os => os.SortOrder);
//var groupedCustomerList = getVoterAnswerstoList
// .GroupBy(u => u.QuestionText, u.QuestionText)
// .Select(grp => grp.ToList())
// .ToList();
var groupedCustomerList = getVoterAnswerstoList.GroupBy(x => new { x.QuestionText, x.DynamicValue }).ToList();
TableCell cell1 = new TableCell();
cell1.BorderWidth = 1;
cell1.Text = section.SectionName;
cell1.BorderColor = System.Drawing.Color.Goldenrod;
cell1.ColumnSpan = groupedCustomerList.Count();
row1.Cells.Add(cell1);
tb.Rows.Add(row1);
TableRow row2 = new TableRow();
foreach (var groups in groupedCustomerList)
{
igroups++;
TableCell cell2 = new TableCell();
var q = (from s in groups select s.QuestionText).FirstOrDefault();
cell2.BorderWidth = 1;
cell2.Text = q;
cell2.BorderColor = System.Drawing.Color.Green;
cell2.ColumnSpan = groupedCustomerList.Count();
row2.Cells.Add(cell2);
if (igroups == groupedCustomerList.Count())
{
tb.Rows.Add(row2);
}
else
{
row2.Cells.Add(cell2);
}
TableRow row3 = new TableRow();
foreach (var answers in groups)
{
ianswer++;
TableCell cell3 = new TableCell();
cell3.BorderWidth = 1;
cell3.BorderColor = System.Drawing.Color.DarkRed;
if (answers.DataTypeId == 7)
{
cell3.Text = answers.DynamicValue.ToString();
}
else if ((answers.DataTypeId == 5) || (answers.DataTypeId == 6) || (answers.DataTypeId == 8))
{
if (answers.VotingValue != 0)
{
cell3.Text = answers.VotingValue.ToString();
}
else
{
cell3.Text = " ";
}
}
else
{
cell3.Text = " ";
}
row3.Cells.Add(cell3);
tb.Rows.Add(row3);
}
}
}
}

Geometry intersection converting from direct geometry to buffergeometry

I am using Three.js. Found a really good Decal library written by Benpurdy. It's very easily modifiable and also used the techniques described here
However, the technique uses Geometry. The project I am on, uses BufferGeometry. I traced the code which does the geometry intersects and can't figure out the conversion from faces and vertices to attributes.
this.createGeometry = function(matrix, mesh) {
var geom = mesh.geometry;
var decalGeometry = new THREE.Geometry();
var projectorInverse = matrix.clone().getInverse(matrix);
var meshInverse = mesh.matrixWorld.clone().getInverse(mesh.matrixWorld);
var faces = [];
for(var i = 0; i < geom.faces.length; i++){
var verts = [geom.faces[i].a, geom.faces[i].b, geom.faces[i].c];
var pts = [];
var valid = false;
for(var v = 0; v < 3; v++) {
var vec = geom.vertices[verts[v]].clone();
vec.applyMatrix4(mesh.matrixWorld);
vec.applyMatrix4(matrix);
if((vec.z > 1) || (vec.z < -1) || (vec.x > 1) || (vec.x < -1) || (vec.y > 1) || (vec.y < -1)) {
} else {
valid = true;
}
pts.push(vec);
}
if(valid) {
var uv = [];
for(var n = 0; n < 3; n++){
uv.push(new THREE.Vector2( (pts[n].x + 1) / 2, (pts[n].y + 1) / 2));
pts[n].applyMatrix4(projectorInverse);
pts[n].applyMatrix4(meshInverse);
decalGeometry.vertices.push( pts[n] );
}
// update UV's
decalGeometry.faceVertexUvs[0].push(uv);
var newFace = geom.faces[i].clone();
newFace.a = decalGeometry.vertices.length - 3;
newFace.b = decalGeometry.vertices.length - 2;
newFace.c = decalGeometry.vertices.length - 1;
decalGeometry.faces.push(newFace);
}
}
return decalGeometry;
}
Appreciate if anyone could shed some light on how to go about pursuing this? Thanks.
I ended up solving the problem by writing another function to compute intersections with buffergeometry. Took me a while trying to understand the original buffer geometry code.
this.createGeometryFromBufferGeometry = function(matrix, mesh) {
var geom = mesh.geometry;
var decalGeometry = new THREE.Geometry();
var projectorInverse = matrix.clone().getInverse(matrix);
var meshInverse = mesh.matrixWorld.clone().getInverse(mesh.matrixWorld);
var faces = [];
for(var i = 0; i < geom.attributes.position.array.length; i+=9){
var pts = [];
var valid = false;
for(var v = 0; v < 9; v+=3) {
var vec = new THREE.Vector3(geom.attributes.position.array[i+v],geom.attributes.position.array[i+v+1],geom.attributes.position.array[i+v+2]);
console.log((i+v) + " " + (i+v+1) + " " + (i+v+2) );
console.log(vec);
vec.applyMatrix4(mesh.matrixWorld);
vec.applyMatrix4(matrix);
if((vec.z > 1) || (vec.z < -1) || (vec.x > 1) || (vec.x < -1) || (vec.y > 1) || (vec.y < -1)) {
} else {
valid = true;
}
pts.push(vec);
}
if(valid) {
var uv = [];
for(var n = 0; n < 3; n++){
uv.push(new THREE.Vector2( (pts[n].x + 1) / 2, (pts[n].y + 1) / 2));
pts[n].applyMatrix4(projectorInverse);
pts[n].applyMatrix4(meshInverse);
decalGeometry.vertices.push( pts[n] );
}
decalGeometry.faceVertexUvs[0].push(uv);
var newFace = new THREE.Face3()
newFace.a = decalGeometry.vertices.length - 3;
newFace.b = decalGeometry.vertices.length - 2;
newFace.c = decalGeometry.vertices.length - 1;
decalGeometry.faces.push(newFace);
}
}
return decalGeometry;
}
BufferGeometry() has a method .fromGeometry(). Populates this BufferGeometry with data from a Geometry object.
var geom = new THREE.BoxGeometry(1,1,1);
var bufGeom = new THREE.BufferGeometry().fromGeometry(geom);
UPD. You can use the other way round.
var bufGeom = new THREE.BoxBufferGeometry(1,1,1);
var geom = new THREE.Geometry().fromBufferGeometry(bufGeom);
Quick and dirty solution is to create geometry from bufferGeometry and after calculating dispose created geometry
this.compute = function()
{
this.geometry = mesh.geometry
if(this.geometry.attributes)
{
this.geometry = new THREE.Geometry().fromBufferGeometry(this.geometry);
this.computeDecal();
this.geometry.dispose();
}
else
{
this.computeDecal();
}
}

Finding path from cell x to cell y in a grid so that all cells are parsed once

I am trying to code an algorithm so that it can start from any "start" cell of a grid ( eg.cell no. 4 in the pic) and parse through each cell of a grid once. The grid can be of any size 3x3, 4x4, 8x8 etc.
The following codes generates such paths. And works fine for many cells in 8x8 grid. But for many cells, it takes forever to search a path.
I was wondering if there is some better solution that can be referenced, so that the solution can be optimized.
package
{
import flash.display.MovieClip;
public class Main extends MovieClip
{
private var rand_Num;
public var node0_Mc:MovieClip ,node1_Mc:MovieClip,node2_Mc:MovieClip,node3_Mc:MovieClip,node4_Mc:MovieClip,node5_Mc:MovieClip,node6_Mc:MovieClip,node7_Mc:MovieClip,node8_Mc:MovieClip,node9_Mc:MovieClip,
node10_Mc:MovieClip,node11_Mc:MovieClip,node12_Mc:MovieClip,node13_Mc:MovieClip,node14_Mc:MovieClip,node15_Mc:MovieClip,node16_Mc:MovieClip,node17_Mc:MovieClip,node18_Mc:MovieClip,node19_Mc:MovieClip,
node20_Mc:MovieClip,node21_Mc:MovieClip,node22_Mc:MovieClip,node23_Mc:MovieClip,node24_Mc:MovieClip,node25_Mc:MovieClip,node26_Mc:MovieClip,node27_Mc:MovieClip,node28_Mc:MovieClip,node29_Mc:MovieClip,
node30_Mc:MovieClip,node31_Mc:MovieClip,node32_Mc:MovieClip,node33_Mc:MovieClip,node34_Mc:MovieClip,node35_Mc:MovieClip,node36_Mc:MovieClip,node37_Mc:MovieClip,node38_Mc:MovieClip,node39_Mc:MovieClip,
node40_Mc:MovieClip,node41_Mc:MovieClip,node42_Mc:MovieClip,node43_Mc:MovieClip,node44_Mc:MovieClip,node45_Mc:MovieClip,node46_Mc:MovieClip,node47_Mc:MovieClip,node48_Mc:MovieClip,node49_Mc:MovieClip,
node50_Mc:MovieClip,node51_Mc:MovieClip,node52_Mc:MovieClip,node53_Mc:MovieClip,node54_Mc:MovieClip,node55_Mc:MovieClip,node56_Mc:MovieClip,node57_Mc:MovieClip,node58_Mc:MovieClip,node59_Mc:MovieClip,
node60_Mc:MovieClip, node61_Mc:MovieClip, node62_Mc:MovieClip, node63_Mc:MovieClip;
public const NUM_COLS:Number = 8;
// 3 ;// 4 ;
public const NUM_ROWS:Number = 8;// 3 ;// 4 ;
var chain_Arr:Array = [];
var blockIndex_Arr:Array = [];
var nodearr:Array;
var adjacentNodeArray_Arr:Array = [];
var parsedNodeIndex_Arr:Array = [];
var validNextAdjacentNodeIndexArray_Arr:Array = [];
var validPreviousAdjacentNodeIndexArray_Arr:Array = [];
var savePair_Arr:Array = [];
var countChain_Num:Number = 0;
var saveParent_Arr:Array = [];
public function Main()
{
// constructor code
nodearr = [node0_Mc,node1_Mc,node2_Mc,node3_Mc,node4_Mc,node5_Mc,node6_Mc,node7_Mc,node8_Mc,node9_Mc,node10_Mc,node11_Mc,node12_Mc,node13_Mc,node14_Mc,node15_Mc,node16_Mc,node17_Mc,node18_Mc,node19_Mc,node20_Mc,node21_Mc,node22_Mc,node23_Mc,node24_Mc,node25_Mc,node26_Mc,node27_Mc,node28_Mc,node29_Mc,node30_Mc,node31_Mc,node32_Mc,node33_Mc,node34_Mc,node35_Mc,node36_Mc,node37_Mc,node38_Mc,node39_Mc,node40_Mc,node41_Mc,node42_Mc,node43_Mc,node44_Mc,node45_Mc,node46_Mc,node47_Mc,node48_Mc,node49_Mc,node50_Mc,node51_Mc,node52_Mc,node53_Mc,node54_Mc,node55_Mc,node56_Mc,node57_Mc,node58_Mc,node59_Mc,node60_Mc,node61_Mc,node62_Mc,node63_Mc];
var possibleAdjacentNodeIndex_Arr:Array = [];
initValidNextAdjacentNodeIndexArray();
initValidPreviousAdjacentNodeIndexArray();
savePair_Arr = [];
var startIndex_num:Number = 45;// 0 ;
rand_Num = 62;// randomRange(10, (NUM_COLS * NUM_ROWS) - 1);
getAllChainsFromParamNodeIndexParamParentChain(startIndex_num,[startIndex_num]);
}
function initValidNextAdjacentNodeIndexArray():void
{
for (var index_num = 0; index_num < NUM_COLS*NUM_ROWS; index_num++)
{
validNextAdjacentNodeIndexArray_Arr[index_num] = getValidNodeIndexArrayAdjacentToParamNodeIndex(index_num);
//trace(validNextAdjacentNodeIndexArray_Arr[index_num]);
}
}
function initValidPreviousAdjacentNodeIndexArray():void
{
for (var index_num = 0; index_num < NUM_COLS*NUM_ROWS; index_num++)
{
validPreviousAdjacentNodeIndexArray_Arr[index_num] = getValidNodeIndexArrayAdjacentToParamNodeIndex(index_num);
//trace(validNextAdjacentNodeIndexArray_Arr[index_num]);
}
}
//function getAllChainsFromParamNodeIndexParamParentChain( path_arr:Array,index_param_num:Number ):void
function getAllChainsFromParamNodeIndexParamParentChain( index_param_num:Number, parent_arr:Array ):void
{
var i;
if ( countChain_Num > 15 )
{
return;
}
reinitPath();
var adjacent_arr = getValidNodeIndexArrayAdjacentToParamNodeIndex(index_param_num);
for (i = 0; i < adjacent_arr.length; i++)
{
reinitPath();
if ( ( checkRepeat(chain_Arr)) )
{
continue;
}
chain_Arr.push(adjacent_arr[i]);
//trace("chain starts from", chain_Arr);
var nodeIndex_num:Number = adjacent_arr[i];
var nodeIndexExist_num = 0;
var parentNode_num:Number = parent_arr[parent_arr.length - 1];
var bool:Boolean;
while ( isNaN(nodeIndex_num) == false)
{
//var childNodeIndex_num = manageValidAdjacentIndexArray(parent_arr[parent_arr.length-1], parentNode_num , nodeIndex_num );
var childNodeIndex_num = manageValidAdjacentIndexArray(adjacent_arr[i],parentNode_num,nodeIndex_num);
//var childNodeIndex_num = manageValidAdjacentIndexArray(parentNode_num, parentNode_num , nodeIndex_num );
parentNode_num = nodeIndex_num;
nodeIndex_num = childNodeIndex_num;
if ( ( checkRepeat(chain_Arr)) )
{
break;
}
if ( isNaN(nodeIndex_num) == false)
{
chain_Arr.push(nodeIndex_num);
}
}
if (chain_Arr.length > NUM_COLS * NUM_ROWS - 1)
{
//if ( !( checkRepeat(chain_Arr)) )
{
trace(chain_Arr);
saveParent_Arr[saveParent_Arr.length ] = new Array();
for (var k = 0; k < rand_Num; k++)
{
saveParent_Arr[saveParent_Arr.length - 1].push( chain_Arr[k]);
}
countChain_Num++;
}
}
};
for (i = 0; i < adjacent_arr.length; i++)
{
var arr2:Array = parent_arr.slice();
arr2.push(adjacent_arr[i]);
getAllChainsFromParamNodeIndexParamParentChain( adjacent_arr[i], arr2 );
}
function reinitPath():void
{
chain_Arr = [];
chain_Arr = chain_Arr.concat(parent_arr);
}
}
private function checkRepeat(chain_arr:Array):Boolean
{
var bool:Boolean;
var num = rand_Num;
if (chain_arr.length >= num - 1)
{
for (var i = 0; i < saveParent_Arr.length; i++)
{
//trace( saveParent_Arr[i][0], saveParent_Arr[i][1]);
if (saveParent_Arr[i] != undefined)
{
var z = 0;
for (var j = 0; j < num; j++)
{
if (saveParent_Arr[i][j] == chain_arr[j])
{
z++;
if ( z >= num)
{
bool = true;
break;
}
}
else
{
break;
}
}
if (bool)
{
break;
}
}
}
}
return bool;
}
function randomRange( minNum:Number, maxNum:Number):Number
{
return (Math.floor(Math.random() * (maxNum - minNum + 1)) + minNum);
}
function randomizeArray(arr:Array ):void
{
for (var i = 0; i < arr.length; i++)
{
var random = randomRange(0,arr.length - 1);
var temp = arr[i];
arr[i] = arr[random];
arr[random] = temp;
}
}
//will send NaN if no valid adjacent index array is possible
private function manageValidAdjacentIndexArray(chainStartIndex_num:Number, parentNodeIndex_param_num:Number, nodeIndex_num:Number):Number
{
var num_nan:Number;
var ret_num:Number;
var j;
//var tot:Number = validNextAdjacentNodeIndexArray_Arr[nodeIndex_num].length ;
j = 0;
var arr:Array = validNextAdjacentNodeIndexArray_Arr[nodeIndex_num];// getValidNodeIndexArrayAdjacentToParamNodeIndex(nodeIndex_num) ;
randomizeArray(arr);
while (arr.length > 0 )// && isNaN(ret_num))
{
ret_num = arr[j];//validNextAdjacentNodeIndexArray_Arr[nodeIndex_num][j] ;
//if this index is present in chain then remove it off
if (chain_Arr.indexOf(ret_num) >= 0)
{
//j++ ;
ret_num = num_nan;
}
j++;
if ( j >= arr.length )
{
ret_num = num_nan;
break;
}
}
return ret_num;
}
private function getValidAdjacentIndexToParamNodeIndex(nodeIndex_param_num:Number):Number
{
var adjacentNode_arr:Array = [];
var adjacentRow_arr:Array = [];
var adjacentCol_arr:Array = [];
var allIndex_arr:Array = [];
var r:Number;
var c:Number;
var row_num:Number = int(nodeIndex_param_num / NUM_COLS);
var col_num:Number = (nodeIndex_param_num % NUM_COLS);
allIndex_arr = getAllAdjacentNodeIndexArray(row_num,col_num);
validateIndices(allIndex_arr);
var ret_num:Number;
ret_num = allIndex_arr[0];
return ret_num;
}
function getValidNodeIndexArrayAdjacentToParamNodeIndex(nodeIndex_param_num:Number ):Array
{
var adjacentNode_arr = [];
for (var positionId_num = 0; positionId_num < 8; positionId_num++)
{
var num:Number = getNodeIndexAtParamPositionIdOfParamIndex(positionId_num,nodeIndex_param_num);
if (isNaN(num))
{
}
else
{
adjacentNode_arr.push(num);
}
}
return adjacentNode_arr;
}
private function getAllAdjacentNodeIndexArray(row_num:Number, col_num:Number,within_arr:Array=null):Array
{
var num:Number;
var index_arr:Array = [num,num,num,num,num,num,num,num];
var r:Number;
var c:Number;
if ( row_num > 0 )
{
r = row_num - 1;
c = col_num;
index_arr[0] = r * NUM_COLS + c;
}
if ( col_num < NUM_COLS-1)
{
r = row_num;
c = col_num + 1;
index_arr[1] = r * NUM_COLS + c;
}
if ( row_num < NUM_ROWS-1)
{
r = row_num + 1;
c = col_num;
index_arr[2] = r * NUM_COLS + c;
}
if ( col_num > 0 )
{
r = row_num;
c = col_num - 1;
index_arr[3] = r * NUM_COLS + c;
}
///////////////////////////////////////////
if ( row_num > 0 && col_num > 0)
{
r = row_num - 1;
c = col_num - 1;
index_arr[4] = r * NUM_COLS + c;
}
if ( row_num > 0 && col_num < NUM_COLS-1)
{
r = row_num - 1;
c = col_num + 1;
index_arr[5] = r * NUM_COLS + c;
}
if ( row_num < NUM_ROWS-1 && col_num < NUM_COLS-1)
{
r = row_num + 1;
c = col_num + 1;
index_arr[6] = r * NUM_COLS + c;
}
if ( row_num < NUM_ROWS-1 && col_num > 0 )
{
r = row_num + 1;
c = col_num - 1;
index_arr[7] = r * NUM_COLS + c;
}
return index_arr;
}
//the adjacent node must be present in within_arr, which we splice, one time for variation
function getNodeIndexAtParamPositionIdOfParamIndex( n:Number , nodeIndex_param_num:Number):Number
{
var adjacentNode_arr:Array = [];
var adjacentRow_arr:Array = [];
var adjacentCol_arr:Array = [];
var index_arr:Array = [];
var r:Number;
var c:Number;
var index_num:Number;
var row_num:Number = int(nodeIndex_param_num / NUM_COLS);
var col_num:Number = (nodeIndex_param_num % NUM_COLS);
index_arr = getAllAdjacentNodeIndexArray(row_num,col_num);
validateIndices(index_arr);
var ret_num:Number;
ret_num = index_arr[n];
return ret_num;
}
private function validateIndices(index_arr:Array):void
{
for (var i = 0; i < index_arr.length; i++)
{
if (chain_Arr.indexOf(index_arr[i]) == -1)
{
}
else
{
var num:Number;
index_arr[i] = num;//pushing NaN
}
}
}
}
}
It's a Hamiltonian path problem. It's NP complete and afaik there is still no efficient solution.
check this out: hamilton paths in grid graphs (pdf)
short randomized algorithm explanation: Princeton Hamiltonian path problem
or wikipedia: Hamiltonian path problem
edit:
I did some more research and for your special case of strongly connected square grids (nxn chessboards) a variation of the warndoffs rule for finding a knights tour might give you a solution in near linear time. See here: A method for finding hamiltonian paths and knight tours (pdf)

Resources