<commit_before>//@EliasHasle

/*
Draft for new version. More modularized, and interacts with a ship state.
Uses an additional coordinate system for motions.
The position.xy and rotation.z of the Ship3D object plae the ship in the 3D world.
(Not geographically)
position.z is the (negative) draft.
fluctCont is a "fluctuations container" to be used for dynamically
changing motions like heave, pitch, roll.
cmContainer centers the motion on the metacenter.
normalizer nulls out the metacenter height before the draft is applied.


THREE.js Object3D constructed from Vessel.js Ship object.

There are some serious limitations to this:
1. null values encountered are assumed to be either at the top or bottom of the given station.
2. The end caps and bulkheads are sometimes corrected with zeros where they should perhaps have been clipped because of null values.

TODO: Use calculated draft for position.z, and place the ship model in a motion container centered at the calculated metacenter.
*/

//var hMat; //global for debugging

function Ship3D(ship, {shipState, stlPath, deckOpacity=0.2, objectOpacity=0.5}) {
	THREE.Group.call(this);
	
	this.normalizer = new THREE.Group();
	this.fluctCont = new THREE.Group();
	this.fluctCont.rotation.order = "ZYX"; //right?
	this.cmContainer = new THREE.Group();
	this.fluctCont.add(this.cmContainer);
	this.normalizer.add(this.fluctCont);
	this.add(this.normalizer);
	
	Object.defineProperty(this, "draft", {
		get: function() {
			return -this.position.z;
		}/*,
		set: function(value) {
			this.position.z = -value;
		}*/
	});
	Object.defineProperty(this, "heave", {
		get: function() {
			return this.fluctCont.position.z;
		},
		set: function(value) {
			this.fluctCont.position.z = value;
			//this.shipState.motion.heave = value;
		}
	});
	Object.defineProperty(this, "pitch", {
		get: function() {
			return this.fluctCont.rotation.y;
		},
		set: function(value) {
			this.fluctCont.rotation.y = value;
			//this.shipState.motion.pitch = value;
		}
	});
	Object.defineProperty(this, "roll", {
		get: function() {
			return this.fluctCont.rotation.x;
		},
		set: function(value) {
			this.fluctCont.rotation.x = value;
			//this.shipState.motion.roll = value;
		}
	});	
	
	this.objectOpacity = objectOpacity;
	
	this.ship = ship;
	this.shipState = shipState || ship.designState.clone();
	
	let hull = ship.structure.hull;

	let LOA = hull.attributes.LOA;
	let BOA = hull.attributes.BOA;
	let Depth = hull.attributes.Depth;

	//console.log("LOA:%.1f, BOA:%.1f, Depth:%.1f",LOA,BOA,Depth);
	let {w: {cg,mass}, T, GMt, GMl} = ship.calculateStability(this.shipState);
	
	this.cmContainer.position.set(-cg.x, -cg.y, -cg.z-GMt);
	this.normalizer.position.z = cg.z+GMt;
	this.position.z = -T;
	
	let designDraft = ship.designState.calculationParameters.Draft_design;
	this.hull3D = new Hull3D(hull, designDraft);
	this.cmContainer.add(this.hull3D);
	
	//DEBUG, to show only hull:
	//return;
	
	let stations = hull.halfBreadths.stations;
	//Decks:
	var decks = new THREE.Group();
	let deckMat = new THREE.MeshPhongMaterial({color: 0xcccccc/*this.randomColor()*/, transparent: true, opacity: deckOpacity, side: THREE.DoubleSide});
	//deckGeom.translate(0,0,-0.5);
	let ds = ship.structure.decks;
	//let dk = Object.keys(ds);
	let stss = stations.map(st=>LOA*st); //use scaled stations for now
	//console.log(dk);
	//for (let i = 0; i < dk.length; i++) {
	for (let dk in ds) {
		//let d = ds[dk[i]]; //deck in ship structure
		let d = ds[dk];
		
		//Will eventually use BoxBufferGeometry, but that is harder, because vertices are duplicated in the face planes.
		let deckGeom = new THREE.PlaneBufferGeometry(1, 1, stss.length, 1);//new THREE.BoxBufferGeometry(1,1,1,sts.length,1,1);
		//console.log("d.zFloor=%.1f", d.zFloor); //DEBUG
		let zHigh = d.zFloor;
		let zLow = d.zFloor-d.thickness;
		let wlHigh = hull.getWaterline(zHigh);
		let wlLow = hull.getWaterline(zLow);
		let pos = deckGeom.getAttribute("position");
		let pa = pos.array;
		for (let j = 0; j < stss.length+1; j++) {
			let x = d.xAft+(j/stss.length)*(d.xFwd-d.xAft);
			let y1 = Vessel.f.linearFromArrays(stss,wlHigh,x);
			let y2 = Vessel.f.linearFromArrays(stss,wlLow,x);
			let y = Math.min(0.5*d.breadth, y1, y2);
			pa[3*j] = x;
			pa[3*j+1] = y;
			pa[3*(stss.length+1)+3*j] = x;
			pa[3*(stss.length+1)+3*j+1] = -y; //test
		}
		pos.needsUpdate = true;
		
		//DEBUG
		//console.log("d.xFwd=%.1f, d.xAft=%.1f, 0.5*d.breadth=%.1f", d.xFwd, d.xAft, 0.5*d.breadth);
		//console.log(pa);

		let deck = new THREE.Mesh(deckGeom, deckMat);
		deck.name = dk;//[i];
		deck.position.z = d.zFloor;
		//deck.scale.set(d.xFwd-d.xAft, d.breadth, d.thickness);
		//deck.position.set(0.5*(d.xFwd+d.xAft), 0, d.zFloor);
		decks.add(deck);
	}
	this.decks = decks;
	this.cmContainer.add(decks);
	
	//Bulkheads:
	var bulkheads = new THREE.Group();
	bulkheads.scale.set(1, 0.5*BOA, Depth);
	let bhGeom = new THREE.BoxBufferGeometry(1,1,1);
	bhGeom.translate(0,0,0.5);
	let bhMat = new THREE.MeshPhongMaterial({color: 0xcccccc/*this.randomColor()*/, transparent: true, opacity: deckOpacity, side: THREE.DoubleSide});
	bhGeom.translate(0.5,0,0);
	let bhs = ship.structure.bulkheads;
	//let bhk = Object.keys(bhs);
	//for (let i = 0; i < bhk.length; i++) {
	for (let bhk in bhs) {
		let bulkhead = new THREE.Mesh(bhGeom, bhMat);
		let bh = bhs[bhk];//bhs[bhk[i]];
		bulkhead.name = bhk;//[i];
		bulkhead.scale.set(bh.thickness, 1, 1);
		bulkhead.position.set(bh.xAft, 0, 0);
		bulkheads.add(bulkhead);
	}
	this.bulkheads = bulkheads;
	this.cmContainer.add(bulkheads);
	
	//Objects

	this.materials = {};
	this.stlPath = stlPath;
	let stlManager = new THREE.LoadingManager();
	this.stlLoader = new THREE.STLLoader(stlManager);
	/*stlManager.onLoad = function() {
		createGUI(materials, deckMat);
	}*/<commit_msg>Added style to decks and bulkheads<commit_after>//@EliasHasle

/*
Draft for new version. More modularized, and interacts with a ship state.
Uses an additional coordinate system for motions.
The position.xy and rotation.z of the Ship3D object plae the ship in the 3D world.
(Not geographically)
position.z is the (negative) draft.
fluctCont is a "fluctuations container" to be used for dynamically
changing motions like heave, pitch, roll.
cmContainer centers the motion on the metacenter.
normalizer nulls out the metacenter height before the draft is applied.


THREE.js Object3D constructed from Vessel.js Ship object.

There are some serious limitations to this:
1. null values encountered are assumed to be either at the top or bottom of the given station.
2. The end caps and bulkheads are sometimes corrected with zeros where they should perhaps have been clipped because of null values.

TODO: Use calculated draft for position.z, and place the ship model in a motion container centered at the calculated metacenter.
*/

//var hMat; //global for debugging

function Ship3D(ship, {shipState, stlPath, deckOpacity=0.2, objectOpacity=0.5}) {
	THREE.Group.call(this);
	
	this.normalizer = new THREE.Group();
	this.fluctCont = new THREE.Group();
	this.fluctCont.rotation.order = "ZYX"; //right?
	this.cmContainer = new THREE.Group();
	this.fluctCont.add(this.cmContainer);
	this.normalizer.add(this.fluctCont);
	this.add(this.normalizer);
	
	Object.defineProperty(this, "draft", {
		get: function() {
			return -this.position.z;
		}/*,
		set: function(value) {
			this.position.z = -value;
		}*/
	});
	Object.defineProperty(this, "heave", {
		get: function() {
			return this.fluctCont.position.z;
		},
		set: function(value) {
			this.fluctCont.position.z = value;
			//this.shipState.motion.heave = value;
		}
	});
	Object.defineProperty(this, "pitch", {
		get: function() {
			return this.fluctCont.rotation.y;
		},
		set: function(value) {
			this.fluctCont.rotation.y = value;
			//this.shipState.motion.pitch = value;
		}
	});
	Object.defineProperty(this, "roll", {
		get: function() {
			return this.fluctCont.rotation.x;
		},
		set: function(value) {
			this.fluctCont.rotation.x = value;
			//this.shipState.motion.roll = value;
		}
	});	
	
	this.objectOpacity = objectOpacity;
	
	this.ship = ship;
	this.shipState = shipState || ship.designState.clone();
	
	let hull = ship.structure.hull;

	let LOA = hull.attributes.LOA;
	let BOA = hull.attributes.BOA;
	let Depth = hull.attributes.Depth;

	//console.log("LOA:%.1f, BOA:%.1f, Depth:%.1f",LOA,BOA,Depth);
	let {w: {cg,mass}, T, GMt, GMl} = ship.calculateStability(this.shipState);
	
	this.cmContainer.position.set(-cg.x, -cg.y, -cg.z-GMt);
	this.normalizer.position.z = cg.z+GMt;
	this.position.z = -T;
	
	let designDraft = ship.designState.calculationParameters.Draft_design;
	this.hull3D = new Hull3D(hull, designDraft);
	this.cmContainer.add(this.hull3D);
	
	//DEBUG, to show only hull:
	//return;
	
	let stations = hull.halfBreadths.stations;
	//Decks:
	var decks = new THREE.Group();
	let deckMat = new THREE.MeshPhongMaterial({color: 0xcccccc/*this.randomColor()*/, transparent: true, opacity: deckOpacity, side: THREE.DoubleSide});
	//deckGeom.translate(0,0,-0.5);
	let ds = ship.structure.decks;
	//let dk = Object.keys(ds);
	let stss = stations.map(st=>LOA*st); //use scaled stations for now
	//console.log(dk);
	//for (let i = 0; i < dk.length; i++) {
	for (let dk in ds) {
		//let d = ds[dk[i]]; //deck in ship structure
		let d = ds[dk];
		
		//Will eventually use BoxBufferGeometry, but that is harder, because vertices are duplicated in the face planes.
		let deckGeom = new THREE.PlaneBufferGeometry(1, 1, stss.length, 1);//new THREE.BoxBufferGeometry(1,1,1,sts.length,1,1);
		//console.log("d.zFloor=%.1f", d.zFloor); //DEBUG
		let zHigh = d.zFloor;
		let zLow = d.zFloor-d.thickness;
		let wlHigh = hull.getWaterline(zHigh);
		let wlLow = hull.getWaterline(zLow);
		let pos = deckGeom.getAttribute("position");
		let pa = pos.array;
		for (let j = 0; j < stss.length+1; j++) {
			let x = d.xAft+(j/stss.length)*(d.xFwd-d.xAft);
			let y1 = Vessel.f.linearFromArrays(stss,wlHigh,x);
			let y2 = Vessel.f.linearFromArrays(stss,wlLow,x);
			let y = Math.min(0.5*d.breadth, y1, y2);
			pa[3*j] = x;
			pa[3*j+1] = y;
			pa[3*(stss.length+1)+3*j] = x;
			pa[3*(stss.length+1)+3*j+1] = -y; //test
		}
		pos.needsUpdate = true;
		
		//DEBUG
		//console.log("d.xFwd=%.1f, d.xAft=%.1f, 0.5*d.breadth=%.1f", d.xFwd, d.xAft, 0.5*d.breadth);
		//console.log(pa);
		let mat = deckMat;
		if (d.style) {
			mat = new THREE.MeshPhongMaterial({color: typeof d.style.color !== "undefined" ? d.style.color : 0xcccccc,  transparent: true, opacity: typeof d.style.opacity !== "undefined" ? d.style.opacity : deckOpacity, side: THREE.DoubleSide});
		}
		let deck = new THREE.Mesh(deckGeom, mat);
		deck.name = dk;//[i];
		deck.position.z = d.zFloor;
		//deck.scale.set(d.xFwd-d.xAft, d.breadth, d.thickness);
		//deck.position.set(0.5*(d.xFwd+d.xAft), 0, d.zFloor);
		decks.add(deck);
	}
	this.decks = decks;
	this.cmContainer.add(decks);
	
	//Bulkheads:
	var bulkheads = new THREE.Group();
	bulkheads.scale.set(1, 0.5*BOA, Depth);
	//Should have individually trimmed geometries like the decks
	let bhGeom = new THREE.BoxBufferGeometry(1,1,1);
	bhGeom.translate(0,0,0.5);
	let bhMat = new THREE.MeshPhongMaterial({color: 0xcccccc/*this.randomColor()*/, transparent: true, opacity: deckOpacity, side: THREE.DoubleSide});
	bhGeom.translate(0.5,0,0);
	let bhs = ship.structure.bulkheads;
	//let bhk = Object.keys(bhs);
	//for (let i = 0; i < bhk.length; i++) {
	for (let bhk in bhs) {
		let bh = bhs[bhk];//bhs[bhk[i]];
		let mat = bhMat;
		if (bh.style) {
			mat = new THREE.MeshPhongMaterial({color: typeof bh.style.color !== "undefined" ? bh.style.color : 0xcccccc, transparent: true, opacity: typeof bh.style.opacity !== "undefined" ? bh.style.opacity : deckOpacity, side: THREE.DoubleSide});
		}
		let bulkhead = new THREE.Mesh(bhGeom, mat);
		bulkhead.name = bhk;//[i];
		bulkhead.scale.set(bh.thickness, 1, 1);
		bulkhead.position.set(bh.xAft, 0, 0);
		bulkheads.add(bulkhead);
	}
	this.bulkheads = bulkheads;
	this.cmContainer.add(bulkheads);
	
	//Objects

	this.materials = {};
	this.stlPath = stlPath;
	let stlManager = new THREE.LoadingManager();
	this.stlLoader = new THREE.STLLoader(stlManager);
	/*stlManager.onLoad = function() {
		createGUI(materials, deckMat);
	}*/
{'fraction_non_alphanumeric': 0.156724955058016, 'fraction_numerical': 0.013074031704526883, 'mean_word_length': 3.8586740770146886, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6925617', 'n_tokens_mistral': 5009, 'n_tokens_neox': 4609, 'n_words': 1083}