|  |  | @ -66,119 +66,69 @@ public partial class TerrainEditingControls | 
			
		
	
		
		
			
				
					
					|  |  |  | 							new Vector2(tile.X + 0.5f, tile.Y + 0.5f)) < distanceSqr); |  |  |  | 							new Vector2(tile.X + 0.5f, tile.Y + 0.5f)) < distanceSqr); | 
			
		
	
		
		
			
				
					
					|  |  |  | 				} |  |  |  | 				} | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | 				UpdateEditToolMesh(terrain, ToolShape switch { |  |  |  | 				var tiles = (ToolShape switch { | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 					// TODO: Edit corner, not full tile. |  |  |  | 					// TODO: Edit corner, not full tile. | 
			
		
	
		
		
			
				
					
					|  |  |  | 					ToolShape.Corner => [tile], |  |  |  | 					ToolShape.Corner => [tile], | 
			
		
	
		
		
			
				
					
					|  |  |  | 					ToolShape.Circle => GetTilesInRadius(), |  |  |  | 					ToolShape.Circle => GetTilesInRadius(), | 
			
		
	
		
		
			
				
					
					|  |  |  | 					ToolShape.Square => GetTilesInSquare(), |  |  |  | 					ToolShape.Square => GetTilesInSquare(), | 
			
		
	
		
		
			
				
					
					|  |  |  | 					_ => throw new InvalidOperationException(), |  |  |  | 					_ => throw new InvalidOperationException(), | 
			
		
	
		
		
			
				
					
					|  |  |  | 				}); |  |  |  | 				}).ToHashSet(); | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 				// TODO: Handle different tool modes, such as painting. | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 				// TODO: Add a flatten tool mode: Flattens everything selected to nearest corner height. | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 				// TODO: Allow click-dragging which doesn't affect already changed tiles / corners. | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 				// TODO: Support undo and redo. | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 				// TODO: Support "disconnected" mode which can create vertical cliffs. | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 				// Raise / lower the terrain if left / right mouse button is pressed. | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 				if ((mouse is InputEventMouseButton { ButtonIndex: var button, Pressed: true }) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 				 && (button is MouseButton.Left or MouseButton.Right)) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 				{ | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 					GetViewport().SetInputAsHandled(); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 					const float AdjustHeight = 0.5f; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 					var amount = (button == MouseButton.Left) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 						? AdjustHeight : -AdjustHeight; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 					// Find corners that are "connected" and should be raised. | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 					var corners = new HashSet<(TilePos Position, Corner Corner)>(); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 					foreach (var pos in tiles) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 						var tile2 = terrain.GetTile(pos); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 						foreach (var corner2 in Enum.GetValues<Corner>()) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 							var height = tile2.Height[corner2]; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 							foreach (var (neighborPos, neighborCorner) in GetNeighbors(pos, corner2)) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 								if (tiles.Contains(neighborPos)) continue; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 								var neighborHeight = terrain.GetTile(neighborPos).Height[neighborCorner]; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 								if (neighborHeight == height) corners.Add((neighborPos, neighborCorner)); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 							} | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 						} | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 					} | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 					// Raise connected corners. | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 					foreach (var group in corners.GroupBy(e => e.Position, e => e.Corner)) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 						var pos = group.Key; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 						var tile2 = terrain.GetTile(pos); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 						foreach (var corner2 in group) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 							tile2.Height[corner2] += amount; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 						terrain.SetTile(pos, tile2); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 					} | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 					// Raise selected tiles themselves. | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 					foreach (var pos in tiles) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 						var tile2 = terrain.GetTile(pos); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 						tile2.Height.Adjust(amount); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 						terrain.SetTile(pos, tile2); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 					} | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 					terrain.UpdateMeshAndShape(); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 					terrain.NotifyPropertyListChanged(); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 				} | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 				UpdateEditToolMesh(terrain, tiles); | 
			
		
	
		
		
			
				
					
					|  |  |  | 			} else { |  |  |  | 			} else { | 
			
		
	
		
		
			
				
					
					|  |  |  | 				ClearEditToolMesh(); |  |  |  | 				ClearEditToolMesh(); | 
			
		
	
		
		
			
				
					
					|  |  |  | 			} |  |  |  | 			} | 
			
		
	
		
		
			
				
					
					|  |  |  | 		} |  |  |  | 		} | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// if (_isSelecting && (ev is InputEventMouseButton { ButtonIndex: MouseButton.Left, Pressed: false })) { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 	_isSelecting = false; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 	GetViewport().SetInputAsHandled(); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 	return; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// } |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// if ((ev is InputEventMouseButton { ButtonIndex: var wheel, Pressed: var pressed, ShiftPressed: true }) |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		//  && (wheel is MouseButton.WheelUp or MouseButton.WheelDown) && (_selection != null)) |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 	// NOTE: Potential bug in the Godot editor? |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 	// Does it zoom both when mouse wheel is "pressed" and "released"? |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 	// Because just cancelling one of them still causes zooming to occur. |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 	GetViewport().SetInputAsHandled(); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 	if (!pressed) return; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 	const float AdjustHeight = 0.5f; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 	var amount = (wheel == MouseButton.WheelUp) |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 		? AdjustHeight : -AdjustHeight; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 	var selection = TileRegion.From(_selection.Value); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 	// Raise connected corners. |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 	foreach (var innerCorner in Enum.GetValues<Corner>()) { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 		var outerCorner = innerCorner.GetOpposite(); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 		var innerPos = selection.GetTileFor(innerCorner); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 		var outerPos = innerPos.GetNeighbor(innerCorner); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 		var outerTile   = terrain.GetTile(outerPos); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 		var innerHeight = terrain.GetTile(innerPos).Height[innerCorner]; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 		var outerHeight = outerTile.Height[outerCorner]; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 		if (IsEqualApprox(outerHeight, innerHeight)) { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 			outerTile.Height[outerCorner] = innerHeight + amount; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 			terrain.SetTile(outerPos, outerTile); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 		} |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 	} |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 	// Raise connected sides. |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 	foreach (var side in Enum.GetValues<Side>()) { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 		foreach (var innerPos in selection.GetTilesFor(side)) { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 			var outerPos = innerPos.GetNeighbor(side); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 			var innerTile = terrain.GetTile(innerPos); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 			var outerTile = terrain.GetTile(outerPos); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 			var (innerCorner1, innerCorner2) = side.GetCorners(); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 			var (outerCorner1, outerCorner2) = side.GetOpposite().GetCorners(); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 			var changed = false; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 			var matchingCorners = new[]{ (innerCorner1, outerCorner1), (innerCorner2, outerCorner2) }; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 			foreach (var (innerCorner, outerCorner) in matchingCorners) { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 				var innerHeight = innerTile.Height[innerCorner]; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 				var outerHeight = outerTile.Height[outerCorner]; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 				if (IsEqualApprox(outerHeight, innerHeight)) { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 					outerTile.Height[outerCorner] = innerHeight + amount; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 					changed = true; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 				} |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 			} |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 			if (changed) terrain.SetTile(outerPos, outerTile); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 		} |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 	} |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 	// Raise selected tiles themselves. |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 	foreach (var pos in selection.GetAllTiles()) { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 		var tile = terrain.GetTile(pos); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 		tile.Height.Adjust(amount); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 		terrain.SetTile(pos, tile); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 	} |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 	terrain.UpdateMeshAndShape(); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 	terrain.NotifyPropertyListChanged(); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 	return; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// } |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// if ((ev is InputEventMouseButton { ButtonIndex: var wheel2, Pressed: var pressed2, CtrlPressed: true }) |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		//  && (wheel2 is MouseButton.WheelUp or MouseButton.WheelDown) && (_selection != null)) |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 	GetViewport().SetInputAsHandled(); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 	if (!pressed2) return; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 	var amount = (wheel2 == MouseButton.WheelUp) ? 1 : -1; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 	var selection = TileRegion.From(_selection.Value); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 	foreach (var pos in selection.GetAllTiles()) { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 		var tile = terrain.GetTile(pos); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 		tile.TexturePrimary = PosMod(tile.TexturePrimary + amount, 4); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 		terrain.SetTile(pos, tile); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 	} |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 	terrain.UpdateMeshAndShape(); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 	terrain.NotifyPropertyListChanged(); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 	return; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// } |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// if (ev is InputEventMouseMotion) |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 	_unhandledMotion = true; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// if ((ev is InputEventMouse mouse) && viewport.GetVisibleRect().HasPoint(mouse.Position)) |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		// 	OnInputRayCastTerrain(terrain, mouse); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 	} |  |  |  | 	} | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | 	void UpdateEditToolMesh(Terrain terrain, IEnumerable<TilePos> tiles) |  |  |  | 	void UpdateEditToolMesh(Terrain terrain, IEnumerable<TilePos> tiles) | 
			
		
	
	
		
		
			
				
					|  |  | @ -255,4 +205,13 @@ public partial class TerrainEditingControls | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | 		return (tile, corner); |  |  |  | 		return (tile, corner); | 
			
		
	
		
		
			
				
					
					|  |  |  | 	} |  |  |  | 	} | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 	static readonly Dictionary<Corner, (int X, int Y, Corner Opposite)[]> _offsetLookup = new(){ | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		[Corner.TopLeft    ] = [(-1, -1, Corner.BottomRight), (-1, 0, Corner.TopRight   ), (0, -1, Corner.BottomLeft )], | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		[Corner.TopRight   ] = [(+1, -1, Corner.BottomLeft ), (+1, 0, Corner.TopLeft    ), (0, -1, Corner.BottomRight)], | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		[Corner.BottomRight] = [(+1, +1, Corner.TopLeft    ), (+1, 0, Corner.BottomLeft ), (0, +1, Corner.TopRight   )], | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		[Corner.BottomLeft ] = [(-1, +1, Corner.TopRight   ), (-1, 0, Corner.BottomRight), (0, +1, Corner.TopLeft    )], | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 	}; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 	static IEnumerable<(TilePos, Corner)> GetNeighbors(TilePos pos, Corner corner) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		=> _offsetLookup[corner].Select(e => (new TilePos(pos.X + e.X, pos.Y + e.Y), e.Opposite)); | 
			
		
	
		
		
			
				
					
					|  |  |  | } |  |  |  | } | 
			
		
	
	
		
		
			
				
					|  |  | 
 |