Crop Image texture on WorldSpace Canvas using RectTransform on Overlay Canvas - unity3d

I've been trying for several days to crop an Image texture (Board - originalImage in the code example) on WorldSpace Canvas using RectTransform(CropArea - cropArea in the code example) on Overlay Canvas.
The problem is that I can't find the correct coordinates of the cropArea on the original image.
I've tried with this:
Texture2D croppedTexture = new Texture2D((int)cropArea.rectTransform.rect.width, (int)cropArea.rectTransform.rect.height);
Texture2D originalTexture = (Texture2D) originalImage.mainTexture;
croppedTexture.SetPixels(originalTexture.GetPixels((int)cropArea.rectTransform.anchoredPosition.x, (int)cropArea.rectTransform.anchoredPosition.y, (int)cropArea.rectTransform.rect.width, (int)cropArea.rectTransform.rect.height));
croppedTexture.Apply();
resultImage.texture = croppedTexture;
But the result image is not cropped properly. It is a bit to the left and a bit down.
Does anybody have an idea how can I achieve this?

I found I have to consider so many variables. Here is a simplified version.
Need a new field: worldCanvas
var cropRectTrans = cropArea.rectTransform;
var origRectTrans = originalImage.rectTransform;
var origRectSize = origRectTrans.sizeDelta;
var pivot = origRectTrans.pivot;
Texture2D originalTexture = (Texture2D)originalImage.mainTexture;
// Scale pivot to pixel unit.
pivot.Scale(origRectSize);
// Get corners of the overlay rectangle in world space.
// The canvas is "Screen Space Overlay", so these positions are
// also the screen positions.
var cropCorners = new Vector3[4];
cropRectTrans.GetWorldCorners(cropCorners);
// Transform the left-bottom and right-top corners to the space
// of the original image. The translated position needs to be added
// with the scaled pivot, so that we can obtain the coordinates
// relative to the left-bottom corner of the image.
var cam = worldCanvas.worldCamera;
RectTransformUtility.ScreenPointToLocalPointInRectangle(
origRectTrans, cropCorners[0], cam, out Vector2 lb);
RectTransformUtility.ScreenPointToLocalPointInRectangle(
origRectTrans, cropCorners[2], cam, out Vector2 tr);
var point = lb + pivot;
var size = tr - lb;
// Scale the position and size if the image is scaled.
var scale = new Vector2(
originalTexture.width / origRectSize.x,
originalTexture.height / origRectSize.y
);
point.Scale(scale);
size.Scale(scale);
// Finally we get the correct position and size in the original image space.
Texture2D croppedTexture = new Texture2D((int)size.x, (int)size.y);
croppedTexture.SetPixels(originalTexture.GetPixels(
(int)point.x, (int)point.y, (int)size.x, (int)size.y));
croppedTexture.Apply();
resultImage.texture = croppedTexture;

Related

How can i Draw Lines in Monogame?

I just started learning monogame a couple of days ago and I was wondering how I can draw a line from a Start Vector2 and an End Vector2 with a specific line thickness?
Should I use a one pixel image to draw it onto the screen and then use a Bresenham's line algorithm to find there positions, or is there a more optimized and less complicated method to do this using monogames built in functions?
One way is to create a Texture2D with a width that is the distance between the two Vector2s and a height that is the desired width. Then you apply a rotation to the texture when you draw it.
Here is an example (as a SpriteBatch extension method):
public static void DrawLineBetween(
this SpriteBatch spriteBatch,
Vector2 startPos,
Vector2 endPos,
int thickness,
Color color)
{
// Create a texture as wide as the distance between two points and as high as
// the desired thickness of the line.
var distance = (int)Vector2.Distance(startPos, endPos);
var texture = new Texture2D(spriteBatch.GraphicsDevice, distance, thickness);
// Fill texture with given color.
var data = new Color[distance * thickness];
for (int i = 0; i < data.Length; i++)
{
data[i] = color;
}
texture.SetData(data);
// Rotate about the beginning middle of the line.
var rotation = (float)Math.Atan2(endPos.Y - startPos.Y, endPos.X - startPos.X);
var origin = new Vector2(0, thickness / 2);
spriteBatch.Draw(
texture,
startPos,
null,
Color.White,
rotation,
origin,
1.0f,
SpriteEffects.None,
1.0f);
}
Example of use:
var startPos = new Vector2(0, 0);
var endPos = new Vector2(800, 480);
_spriteBatch.DrawLineBetween(startPos, endPos, 12, Color.White);
How it looks:
It's not a perfect solution. You'll want to modify it if you want to draw connected lines with different angles without any visible seams. Also not sure about the performance.
I use a library I found. It draws a number of 2D primitives, like lines, boxes, etc. Really easy to use. Called "C3.MonoGame.Primitives2D", it can be found here:
https://github.com/z2oh/C3.MonoGame.Primitives2D
Here's a screenshot of a demo I wrote using many of its methods:
It's just one file of around 500 lines. If you don't like Git or using libraries, you can just copy & paste it into your project.

Draw images on a canvas next to each other without space in flutter

I'm creating a game in flutter. I'm using a tilesheet to render tiles. I chop the image in 50x50 sections and then render them depending on their surrounding.
The tiles are rendered at the position of their corresponding "game object". In order to keep the code clean from details about converting from the game world positions and sizes to actual screen sizes my painting classes always obtain world space and then scales it up to screen space.
However, after scaling up the tiles I'm sometimes left with gaps between the tiles.
I've tried:
Placing the tiles at their appropriate screen position, without scaling the canvas first, in case the scaling produces the problem.
Adding borders around the tiles in the tilesheet in case the canvas.drawImageRect samples too many pixels.
Making the board take a size divisible by the number of tiles, in case there is a rounding error.
Drawing a subsection of the tile.
I can't seem to make the gap disappear. Do you have any suggestions?
Below is the relevant drawing code, where size and position are in world space and frameX and frameY is the positions to extract from the spritesheet.
void drawFrameAt(int x, int y, Canvas canvas, Offset position, Size size) {
var frameX = x * frameWidth;
var frameY = y * frameHeight;
var rect = Rect.fromLTWH(frameX, frameY, frameWidth, frameHeight);
var width = size.width;
var height = size.height;
var destination = Rect.fromLTWH(position.dx, position.dy, width, height);
canvas.drawImageRect(image, rect, destination, Paint());
}

how to copy a part of a raw image

I want to copy selected part of a raw image to another image
I get start and end position as percentage and by that I can calculate the start and end position in width
how can I copy that selected part to another raw image?
Assuming it's a Texture2D, you can do the following:
Calculate A texture start/end X (dX)
Create a new Texture2D (B), sized as dX and full Y
Call A.GetPixels()
Iterate on array copying pixels to new texture
Apply on new texture
Pseudo code:
var aPixels = aTexture.GetPixels();
var bWidth = endX - startX;
var bTexture = new Texture2D(bWidth, endY);
var bPixels = bTexture.GetPixels();
for (int x = startX; x < endX; x++)
{
for (int y = 0; y < endY; y++)
{
var aIndex = x + y * A.width;
var bIndex = (x - startX) + y * bWidth;
bPixels[bIndex] = aPixels[aIndex];
}
}
bTexture.Apply();
Note that my code quite possibly won't work; as I'm typing this on a mobile phone.
Usually, Image Processing is an expensive process for CPUs, so I don't recommend it in Unity,
But anyway, For your image and in this special case, I think you can crop your image by changing the Size and Offset of texture in material.
Update:
This is an example of what I mentioned:
You can calculate Tile and Offset based on the dragged mouse position on Texture. (Check Here)
I found this.
you can set start coordinates and width and height to GetPixels();
void Start () {
public Texture2D mTexture;
Color[] c = mTexture.GetPixels (startX, startY, width, height);
Texture2D m2Texture = new Texture2D (width, height);
m2Texture.SetPixels (c);
m2Texture.Apply ();
gameObject.GetComponent<MeshRenderer> ().material.mainTexture = m2Texture;
}
```

Is it possible to define a 3d model/prefab size in unity?

Is there a way I can define a 3d model size in unity? Like height = 1, width = 3, depth = 3?
I want the model to take a defined space in unity's scene, no matter how big or small I make the fbx in Blender. So I can't use scale as changing the model size in Blender will break this scaling.
I need it to be a square 3 wide, 3 long and 1 high, not depending on the model's size that is exported from Blender. Is it possible?
The same question but from another angle - how to set model size in unity? There is only the scale setting, but no size setting. This looks weird.
So far I have found a workaround like getting object's rendered bounds and adjusting scaling quotient in a script, but this doesn't seem right to me.
You can use the Mesh.bounds to get the 3D model size without applied scaling.
Then you recalculate the scale according to your needs e.g.
// The desired scales
// x = width
// y = height
// z = depth
var targetScale = new Vector3(3, 1, 3);
var meshFilter = GetComponent<MeshFilter>();
var mesh = meshFilter.mesh;
// This would be equal to the 3D bounds if scale was 1,1,1
var meshBounds = mesh.bounds.size;
// This would make the model have world scale 1,1,1
var invertMeshBounds = new Vector3(1/meshBounds.x, 1/meshBounds.y, 1/meshBounds.z);
// Use this if you want exactly the scale 3,1,3 but maybe stretching the fbx
var finalScale = Vector3.Scale(invertMeshBounds, targetScale);
As I understand you want to keep the correct relative scales of the 3D model but make it fit into the defined targetScale so I would use the smallest of the 3 values as scaling factor
var minFactor = Mathf.Min(finalScale.x, finalScale.y);
minFactor = Mathf.Min(minFactor, finalScale.z);
transform.localScale = Vector3.one * minFactor;

How get screenshot of all objects in scene by its bounds

I generated a model in Unity3d by vertics data. I created BoxCollider, that contains all game objects for the model.
How can I get screenshot of camera view according to the bounds of the model (BoxCollider)?
Here is my model
You can use this snippet, I explain the code with comment:
int textureWidth = GameObject.find("UnicGroups").GetComponent<BoxCollider>.size.x; // Width of photo
int texturHeight = GameObject.find("UnicGroups").GetComponent<BoxCollider>.size.y; // Height of photo
// You can add EmptyGameObject to your model
// and move that game object to upper left corner of
// your model. Then you can replace x and y value
// with position of that game object
float x = GameObject.find("UnicGroups/YourEmptyGameObject").transform.Position.x; // x position of upper left corner of screenshot
float y = GameObject.find("UnicGroups/YourEmptyGameObject").transform.Position.y; // Y position of upper left corner of screenshot
// create the texture
Texture2D screenTexture = new Texture2D(textureWidth, texturHeight,TextureFormat.RGB24,true);
screenTexture.ReadPixels(new Rect(x, y, textureWidth, texturHeight),0,0);
screenTexture.Apply();
// Save image to file
byte[] dataToSave = screenTexture.EncodeToPNG();
// Set destination path for saving file
string destination = Path.Combine(Application.persistentDataPath,System.DateTime.Now.ToString("yyyy-MM-dd-HHmmss") + ".png");
File.WriteAllBytes(destination, dataToSave);