How to scroll to a specific element in ScrollRect with Unity UI? - user-interface

I built a registration form for a mobile game using Unity 5.1.
To do that, I use Unity UI components: ScrollRect + Autolayout (Vertical layout) + Text (labels) + Input Field.
This part works fine.
But, when keyboard is opened, the selected field is under keyboard. Is there a way to programmatically scroll the form to bring the selected field into view?
I have tried using ScrollRect.verticalNormalizedPosition and it works fine to scroll some, however I am not able to make selected field appear where I want.
Thanks for your help !

I am going to give you a code snippet of mine because I feel like being helpful. Hope this helps!
protected ScrollRect scrollRect;
protected RectTransform contentPanel;
public void SnapTo(RectTransform target)
{
Canvas.ForceUpdateCanvases();
contentPanel.anchoredPosition =
(Vector2)scrollRect.transform.InverseTransformPoint(contentPanel.position)
- (Vector2)scrollRect.transform.InverseTransformPoint(target.position);
}

None of the suggestions worked for me, the following code did
Here is the extension
using UnityEngine;
using UnityEngine.UI;
namespace BlinkTalk
{
public static class ScrollRectExtensions
{
public static Vector2 GetSnapToPositionToBringChildIntoView(this ScrollRect instance, RectTransform child)
{
Canvas.ForceUpdateCanvases();
Vector2 viewportLocalPosition = instance.viewport.localPosition;
Vector2 childLocalPosition = child.localPosition;
Vector2 result = new Vector2(
0 - (viewportLocalPosition.x + childLocalPosition.x),
0 - (viewportLocalPosition.y + childLocalPosition.y)
);
return result;
}
}
}
And here is how I used it to scroll a direct child of the content into view
private void Update()
{
MyScrollRect.content.localPosition = MyScrollRect.GetSnapToPositionToBringChildIntoView(someChild);
}

Although #maksymiuk's answer is the most correct one, as it properly takes into account anchors, pivot and all the rest thanks to InverseTransformPoint() function, it still didn't work out-of-box for me - for vertical scroller, it was changing its X position too. So I just made change to check if vertical or horizontal scroll is enabled, and not change their axis if they aren't.
public static void SnapTo( this ScrollRect scroller, RectTransform child )
{
Canvas.ForceUpdateCanvases();
var contentPos = (Vector2)scroller.transform.InverseTransformPoint( scroller.content.position );
var childPos = (Vector2)scroller.transform.InverseTransformPoint( child.position );
var endPos = contentPos - childPos;
// If no horizontal scroll, then don't change contentPos.x
if( !scroller.horizontal ) endPos.x = contentPos.x;
// If no vertical scroll, then don't change contentPos.y
if( !scroller.vertical ) endPos.y = contentPos.y;
scroller.content.anchoredPosition = endPos;
}

here's the way I clamped selected object into ScrollRect
private ScrollRect scrollRect;
private RectTransform contentPanel;
public void ScrollReposition(RectTransform obj)
{
var objPosition = (Vector2)scrollRect.transform.InverseTransformPoint(obj.position);
var scrollHeight = scrollRect.GetComponent<RectTransform>().rect.height;
var objHeight = obj.rect.height;
if (objPosition.y > scrollHeight / 2)
{
contentPanel.localPosition = new Vector2(contentPanel.localPosition.x,
contentPanel.localPosition.y - objHeight - Padding.top);
}
if (objPosition.y < -scrollHeight / 2)
{
contentPanel.localPosition = new Vector2(contentPanel.localPosition.x,
contentPanel.localPosition.y + objHeight + Padding.bottom);
}
}

The preconditions for my version of this problem:
The element I want to scroll to should be fully visible (with minimal clearance)
The element is a direct child of the scrollRect's content
Keep scoll position if element is already fully visible
I only care about the vertical dimension
This is what worked best for me (thanks for the other inspirations):
// ScrollRect scrollRect;
// RectTransform element;
// Fully show `element` inside `scrollRect` with at least 25px clearance
scrollArea.EnsureVisibility(element, 25);
Using this extension method:
public static void EnsureVisibility(this ScrollRect scrollRect, RectTransform child, float padding=0)
{
Debug.Assert(child.parent == scrollRect.content,
"EnsureVisibility assumes that 'child' is directly nested in the content of 'scrollRect'");
float viewportHeight = scrollRect.viewport.rect.height;
Vector2 scrollPosition = scrollRect.content.anchoredPosition;
float elementTop = child.anchoredPosition.y;
float elementBottom = elementTop - child.rect.height;
float visibleContentTop = -scrollPosition.y - padding;
float visibleContentBottom = -scrollPosition.y - viewportHeight + padding;
float scrollDelta =
elementTop > visibleContentTop ? visibleContentTop - elementTop :
elementBottom < visibleContentBottom ? visibleContentBottom - elementBottom :
0f;
scrollPosition.y += scrollDelta;
scrollRect.content.anchoredPosition = scrollPosition;
}

A variant of Peter Morris answer for ScrollRects which have movement type "elastic". It bothered me that the scroll rect kept animating for edge cases (first or last few elements). Hope it's useful:
/// <summary>
/// Thanks to https://stackoverflow.com/a/50191835
/// </summary>
/// <param name="instance"></param>
/// <param name="child"></param>
/// <returns></returns>
public static IEnumerator BringChildIntoView(this UnityEngine.UI.ScrollRect instance, RectTransform child)
{
Canvas.ForceUpdateCanvases();
Vector2 viewportLocalPosition = instance.viewport.localPosition;
Vector2 childLocalPosition = child.localPosition;
Vector2 result = new Vector2(
0 - (viewportLocalPosition.x + childLocalPosition.x),
0 - (viewportLocalPosition.y + childLocalPosition.y)
);
instance.content.localPosition = result;
yield return new WaitForUpdate();
instance.horizontalNormalizedPosition = Mathf.Clamp(instance.horizontalNormalizedPosition, 0f, 1f);
instance.verticalNormalizedPosition = Mathf.Clamp(instance.verticalNormalizedPosition, 0f, 1f);
}
It introduces a one frame delay though.
UPDATE: Here is a version which does not introduce a frame delay and it also takes scaling of the content into account:
/// <summary>
/// Based on https://stackoverflow.com/a/50191835
/// </summary>
/// <param name="instance"></param>
/// <param name="child"></param>
/// <returns></returns>
public static void BringChildIntoView(this UnityEngine.UI.ScrollRect instance, RectTransform child)
{
instance.content.ForceUpdateRectTransforms();
instance.viewport.ForceUpdateRectTransforms();
// now takes scaling into account
Vector2 viewportLocalPosition = instance.viewport.localPosition;
Vector2 childLocalPosition = child.localPosition;
Vector2 newContentPosition = new Vector2(
0 - ((viewportLocalPosition.x * instance.viewport.localScale.x) + (childLocalPosition.x * instance.content.localScale.x)),
0 - ((viewportLocalPosition.y * instance.viewport.localScale.y) + (childLocalPosition.y * instance.content.localScale.y))
);
// clamp positions
instance.content.localPosition = newContentPosition;
Rect contentRectInViewport = TransformRectFromTo(instance.content.transform, instance.viewport);
float deltaXMin = contentRectInViewport.xMin - instance.viewport.rect.xMin;
if(deltaXMin > 0) // clamp to <= 0
{
newContentPosition.x -= deltaXMin;
}
float deltaXMax = contentRectInViewport.xMax - instance.viewport.rect.xMax;
if (deltaXMax < 0) // clamp to >= 0
{
newContentPosition.x -= deltaXMax;
}
float deltaYMin = contentRectInViewport.yMin - instance.viewport.rect.yMin;
if (deltaYMin > 0) // clamp to <= 0
{
newContentPosition.y -= deltaYMin;
}
float deltaYMax = contentRectInViewport.yMax - instance.viewport.rect.yMax;
if (deltaYMax < 0) // clamp to >= 0
{
newContentPosition.y -= deltaYMax;
}
// apply final position
instance.content.localPosition = newContentPosition;
instance.content.ForceUpdateRectTransforms();
}
/// <summary>
/// Converts a Rect from one RectTransfrom to another RectTransfrom.
/// Hint: use the root Canvas Transform as "to" to get the reference pixel positions.
/// </summary>
/// <param name="from"></param>
/// <param name="to"></param>
/// <returns></returns>
public static Rect TransformRectFromTo(Transform from, Transform to)
{
RectTransform fromRectTrans = from.GetComponent<RectTransform>();
RectTransform toRectTrans = to.GetComponent<RectTransform>();
if (fromRectTrans != null && toRectTrans != null)
{
Vector3[] fromWorldCorners = new Vector3[4];
Vector3[] toLocalCorners = new Vector3[4];
Matrix4x4 toLocal = to.worldToLocalMatrix;
fromRectTrans.GetWorldCorners(fromWorldCorners);
for (int i = 0; i < 4; i++)
{
toLocalCorners[i] = toLocal.MultiplyPoint3x4(fromWorldCorners[i]);
}
return new Rect(toLocalCorners[0].x, toLocalCorners[0].y, toLocalCorners[2].x - toLocalCorners[1].x, toLocalCorners[1].y - toLocalCorners[0].y);
}
return default(Rect);
}

In case anyone looking for a smooth scroll (using lerp).
[SerializeField]
private ScrollRect _scrollRectComponent; //your scroll rect component
[SerializeField]
RectTransform _container; //content transform of the scrollrect
private IEnumerator LerpToChild(RectTransform target)
{
Vector2 _lerpTo = (Vector2)_scrollRectComponent.transform.InverseTransformPoint(_container.position) - (Vector2)_scrollRectComponent.transform.InverseTransformPoint(target.position);
bool _lerp = true;
Canvas.ForceUpdateCanvases();
while(_lerp)
{
float decelerate = Mathf.Min(10f * Time.deltaTime, 1f);
_container.anchoredPosition = Vector2.Lerp(_scrollRectComponent.transform.InverseTransformPoint(_container.position), _lerpTo, decelerate);
if (Vector2.SqrMagnitude((Vector2)_scrollRectComponent.transform.InverseTransformPoint(_container.position) - _lerpTo) < 0.25f)
{
_container.anchoredPosition = _lerpTo;
_lerp = false;
}
yield return null;
}
}

simple and works perfectly
var pos = 1 - ((content.rect.height / 2 - target.localPosition.y) / content.rect.height);
scrollRect.normalizedPosition = new Vector2(0, pos);

Yes,this is possible using coding to scroll vertically, please try this code :
//Set Scrollbar Value - For Displaying last message of content
Canvas.ForceUpdateCanvases ();
verticleScrollbar.value = 0f;
Canvas.ForceUpdateCanvases ();
This code working fine for me ,when i developed chat functionality.

width signifiy the width of childern in scroll rect (assuming that all childerns width is same), spacing signifies the space between childerns, index signifies the target element you want to reach
public float getSpecificItem (float pWidth, float pSpacing,int pIndex) {
return (pIndex * pWidth) - pWidth + ((pIndex - 1) * pSpacing);
}

Vertical adjustment:
[SerializeField]
private ScrollRect _scrollRect;
private void ScrollToCurrentElement()
{
var siblingIndex = _currentListItem.transform.GetSiblingIndex();
float pos = 1f - (float)siblingIndex / _scrollRect.content.transform.childCount;
if (pos < 0.4)
{
float correction = 1f / _scrollRect.content.transform.childCount;
pos -= correction;
}
_scrollRect.verticalNormalizedPosition = pos;
}

Here's what I created to solve this problem. Place this behavior on each button that can be selected via controller UI navigation.
It supports fully nested children objects.
It only supports vertical scrollrects, but can be easily adapted to do horizontal.
using UnityEngine;
using System;
using UnityEngine.EventSystems;
using UnityEngine.UI;
namespace Legend
{
public class ScrollToOnSelect: MonoBehaviour, ISelectHandler
{
ScrollRect scrollRect;
RectTransform target;
void Start()
{
scrollRect = GetComponentInParent<ScrollRect>();
target = (RectTransform)this.transform;
}
Vector3 LocalPositionWithinAncestor(Transform ancestor, Transform target)
{
var result = Vector3.zero;
while (ancestor != target && target != null)
{
result += target.localPosition;
target = target.parent;
}
return result;
}
public void EnsureScrollVisible()
{
Canvas.ForceUpdateCanvases();
var targetPosition = LocalPositionWithinAncestor(scrollRect.content, target);
var top = (-targetPosition.y) - target.rect.height / 2;
var bottom = (-targetPosition.y) + target.rect.height / 2;
var topMargin = 100; // this is here because there are headers over the buttons sometimes
var result = scrollRect.content.anchoredPosition;
if (result.y > top - topMargin)
result.y = top - topMargin;
if (result.y + scrollRect.viewport.rect.height < bottom)
result.y = bottom - scrollRect.viewport.rect.height;
//Debug.Log($"{targetPosition} {target.rect.size} {top} {bottom} {scrollRect.content.anchoredPosition}->{result}");
scrollRect.content.anchoredPosition = result;
}
public void OnSelect(BaseEventData eventData)
{
if (scrollRect != null)
EnsureScrollVisible();
}
}
}

Related

Unity gun flipping

I'm making a top down shooter game and when I point my gun backwards, it is upside down. Here's my player script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
public float moveSpeed = 5f;
public Rigidbody2D rb;
public Camera cam;
Vector2 movement;
Vector2 mousePos;
public GameObject crossHair;
public SpriteRenderer sr;
void Start() {
Cursor.visible = false;
}
// Update is called once per frame
void Update()
{
sr = GetComponent<SpriteRenderer>();
rb = GetComponent<Rigidbody2D>();
movement.x = Input.GetAxisRaw("Horizontal");
movement.y = Input.GetAxisRaw("Vertical");
mousePos = cam.ScreenToWorldPoint(Input.mousePosition);
crossHair.transform.position = new Vector2(mousePos.x, mousePos.y);
}
void FixedUpdate()
{
rb.velocity = new Vector2(movement.x, movement.y) * moveSpeed;
Vector3 lookDir = mousePos - rb.position;
lookDir.Normalize();
float angle = Mathf.Atan2(lookDir.y, lookDir.x) * Mathf.Rad2Deg;
rb.rotation = angle;
if (transform.position.y >= 6.4f) {
transform.position = new Vector2(transform.position.x, 6.4f);
}
if (transform.position.y <= -6.4f) {
transform.position = new Vector2(transform.position.x, -6.4f);
}
if (transform.position.x >= 11.9) {
transform.position = new Vector2(11.9f, transform.position.y);
}
if (transform.position.x <= -11.9) {
transform.position = new Vector2(-11.9f, transform.position.y);
}
float z = transform.rotation.eulerAngles.z;
if (z > 0 && z < 180 && !sr.flipX)
sr.flipX = true;
else if (z < 360 && z > 180 && sr.flipX)
sr.flipX = false;
}
void Flip(){
Vector3 Scaler = transform.localScale;
Scaler.y *= -1;
transform.localScale = Scaler;
}
}
I've searched a lot but I couldn't make it work. I tried with spriteRenderer but it doesn't work either. I really need help.(Btw my player rotates with the player)
Sounds to me like your system is just rotating the object without a second though for what direction it's facing. I imagine if you put an arrow or triangle pointing in the direction you like instead of a gun, it would look just fine.
So what I would do is the following:
if (the rotation is backwards)
render the sprite flipped vertically
else
render the sprite normally

Xamarin: Unable to extract central square of photo in Android/iOS

I am trying to get an image using the camera. The image is to be 256x256 and I want it to come from the centre of a photo taken using the camera on a phone. I found this code at: https://forums.xamarin.com/discussion/37647/cross-platform-crop-image-view
I am using this code for Android...
public byte[] CropPhoto(byte[] photoToCropBytes, Rectangle rectangleToCrop, double outputWidth, double outputHeight)
{
using (var photoOutputStream = new MemoryStream())
{
// Load the bitmap
var inSampleSize = CalculateInSampleSize((int)rectangleToCrop.Width, (int)rectangleToCrop.Height, (int)outputWidth, (int)outputHeight);
var options = new BitmapFactory.Options();
options.InSampleSize = inSampleSize;
//options.InPurgeable = true; see http://developer.android.com/reference/android/graphics/BitmapFactory.Options.html
using (var photoToCropBitmap = BitmapFactory.DecodeByteArray(photoToCropBytes, 0, photoToCropBytes.Length, options))
{
var matrix = new Matrix();
var martixScale = outputWidth / rectangleToCrop.Width * inSampleSize;
matrix.PostScale((float)martixScale, (float)martixScale);
using (var photoCroppedBitmap = Bitmap.CreateBitmap(photoToCropBitmap, (int)(rectangleToCrop.X / inSampleSize), (int)(rectangleToCrop.Y / inSampleSize), (int)(rectangleToCrop.Width / inSampleSize), (int)(rectangleToCrop.Height / inSampleSize), matrix, true))
{
photoCroppedBitmap.Compress(Bitmap.CompressFormat.Jpeg, 100, photoOutputStream);
}
}
return photoOutputStream.ToArray();
}
}
public static int CalculateInSampleSize(int inputWidth, int inputHeight, int outputWidth, int outputHeight)
{
//see http://developer.android.com/training/displaying-bitmaps/load-bitmap.html
int inSampleSize = 1; //default
if (inputHeight > outputHeight || inputWidth > outputWidth) {
int halfHeight = inputHeight / 2;
int halfWidth = inputWidth / 2;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) > outputHeight && (halfWidth / inSampleSize) > outputWidth)
{
inSampleSize *= 2;
}
}
return inSampleSize;
}
and this code for iOS...
public byte[] CropPhoto(byte[] photoToCropBytes, Xamarin.Forms.Rectangle
rectangleToCrop, double outputWidth, double outputHeight)
{
byte[] photoOutputBytes;
using (var data = NSData.FromArray(photoToCropBytes))
{
using (var photoToCropCGImage = UIImage.LoadFromData(data).CGImage)
{
//crop image
using (var photoCroppedCGImage = photoToCropCGImage.WithImageInRect(new CGRect((nfloat)rectangleToCrop.X, (nfloat)rectangleToCrop.Y, (nfloat)rectangleToCrop.Width, (nfloat)rectangleToCrop.Height)))
{
using (var photoCroppedUIImage = UIImage.FromImage(photoCroppedCGImage))
{
//create a 24bit RGB image to the output size
using (var cGBitmapContext = new CGBitmapContext(IntPtr.Zero, (int)outputWidth, (int)outputHeight, 8, (int)(4 * outputWidth), CGColorSpace.CreateDeviceRGB(), CGImageAlphaInfo.PremultipliedFirst))
{
var photoOutputRectangleF = new RectangleF(0f, 0f, (float)outputWidth, (float)outputHeight);
// draw the cropped photo resized
cGBitmapContext.DrawImage(photoOutputRectangleF, photoCroppedUIImage.CGImage);
//get cropped resized photo
var photoOutputUIImage = UIKit.UIImage.FromImage(cGBitmapContext.ToImage());
//convert cropped resized photo to bytes and then stream
using (var photoOutputNsData = photoOutputUIImage.AsJPEG())
{
photoOutputBytes = new Byte[photoOutputNsData.Length];
System.Runtime.InteropServices.Marshal.Copy(photoOutputNsData.Bytes, photoOutputBytes, 0, Convert.ToInt32(photoOutputNsData.Length));
}
}
}
}
}
}
return photoOutputBytes;
}
I am struggling to work out exactly what the parameters are to call the function.
Currently, I am doing the following:
double cropSize = Math.Min(DeviceDisplay.MainDisplayInfo.Width, DeviceDisplay.MainDisplayInfo.Height);
double left = (DeviceDisplay.MainDisplayInfo.Width - cropSize) / 2.0;
double top = (DeviceDisplay.MainDisplayInfo.Height - cropSize) / 2.0;
// Get a square resized and cropped from the top image as a byte[]
_imageData = mediaService.CropPhoto(_imageData, new Rectangle(left, top, cropSize, cropSize), 256, 256);
I was expecting this to crop the image to the central square (in portrait mode side length would be the width of the photo) and then scale it down to a 256x256 image. But it never picks the centre of the image.
Has anyone ever used this code and can tell me what I need to pass in for the 'rectangleToCrop' parameter?
Note: Both Android and iOS give the same image, just not the central part that I was expecting.
Here are the two routines I used:
Android:
public byte[] ResizeImageAndCropToSquare(byte[] rawPhoto, int outputSize)
{
// Create object of bitmapfactory's option method for further option use
BitmapFactory.Options options = new BitmapFactory.Options();
// InPurgeable is used to free up memory while required
options.InPurgeable = true;
// Get the original image
using (var originalImage = BitmapFactory.DecodeByteArray(rawPhoto, 0, rawPhoto.Length, options))
{
// The shortest edge will determine the size of the square image
int cropSize = Math.Min(originalImage.Width, originalImage.Height);
int left = (originalImage.Width - cropSize) / 2;
int top = (originalImage.Height - cropSize) / 2;
using (var squareImage = Bitmap.CreateBitmap(originalImage, left, top, cropSize, cropSize))
{
// Resize the square image to the correct size of an Avatar
using (var resizedImage = Bitmap.CreateScaledBitmap(squareImage, outputSize, outputSize, true))
{
// Return the raw data of the resized image
using (MemoryStream resizedImageStream = new MemoryStream())
{
// Resize the image maintaining 100% quality
resizedImage.Compress(Bitmap.CompressFormat.Png, 100, resizedImageStream);
return resizedImageStream.ToArray();
}
}
}
}
}
iOS:
private const int BitsPerComponent = 8;
public byte[] ResizeImageAndCropToSquare(byte[] rawPhoto, int outputSize)
{
using (var data = NSData.FromArray(rawPhoto))
{
using (var photoToCrop = UIImage.LoadFromData(data).CGImage)
{
nint photoWidth = photoToCrop.Width;
nint photoHeight = photoToCrop.Height;
nint cropSize = photoWidth < photoHeight ? photoWidth : photoHeight;
nint left = (photoWidth - cropSize) / 2;
nint top = (photoHeight - cropSize) / 2;
// Crop image
using (var photoCropped = photoToCrop.WithImageInRect(new CGRect(left, top, cropSize, cropSize)))
{
using (var photoCroppedUIImage = UIImage.FromImage(photoCropped))
{
// Create a 24bit RGB image of output size
using (var cGBitmapContext = new CGBitmapContext(IntPtr.Zero, outputSize, outputSize, BitsPerComponent, outputSize << 2, CGColorSpace.CreateDeviceRGB(), CGImageAlphaInfo.PremultipliedFirst))
{
var photoOutputRectangleF = new RectangleF(0f, 0f, outputSize, outputSize);
// Draw the cropped photo resized
cGBitmapContext.DrawImage(photoOutputRectangleF, photoCroppedUIImage.CGImage);
// Get cropped resized photo
var photoOutputUIImage = UIImage.FromImage(cGBitmapContext.ToImage());
// Convert cropped resized photo to bytes and then stream
using (var photoOutputNsData = photoOutputUIImage.AsPNG())
{
var rawOutput = new byte[photoOutputNsData.Length];
Marshal.Copy(photoOutputNsData.Bytes, rawOutput, 0, Convert.ToInt32(photoOutputNsData.Length));
return rawOutput;
}
}
}
}
}
}
}

How can I change the bitmap in a PPT picture?

The answer here doesn't replace the bitmap, it adds to it. Is there a way to replace the bitmap in a picture?
Using PowerPoint COM interop (not VSTO).
The code is:
/// <summary>
/// Create or update a picture (bitmap) tag.
/// </summary>
private static PowerPointTagLocation CreateOrUpdatePictureTag(PowerPointTagLocation tagLoc, bool isUpdate, Image picture,
string text, Slide slide)
{
// get a unique name for the picture
string name = FileUtils.MakeValidFileName("wr_bitmap", true) + rand.Next(9999);
// we need a physical file as the bitmap to display.
string filename = name + ".png";
filename = Path.Combine(Path.GetTempPath(), filename);
if (File.Exists(filename))
{
Trap.trap();
File.Delete(filename);
}
// if it's an update for a picture, we just update the AlternativeText.
// And the bitmap if we get it.
if (isUpdate && tagLoc.IsPicture) {
tagLoc.TagShape.AlternativeText = text;
if (picture != null) {
picture.Save(filename, ImageFormat.Png);
tagLoc.TagShape.Fill.UserPicture(filename);
File.Delete(filename);
}
return tagLoc;
}
if (picture == null)
CommonBitmaps.photo_scenery.Save(filename, ImageFormat.Png);
else
picture.Save(filename, ImageFormat.Png);
Shape shape;
using (Bitmap bitmap = new Bitmap(filename)) {
PictHandler.GetPictureSize(tagLoc.Tag, bitmap, 0, out var width, out var height);
// position/extent in points. Place in middle of slide
float x, y;
try
{
PageSetup page = ((Presentation)slide.Parent).PageSetup;
x = (page.SlideWidth - (width / 20f)) / 2f;
y = (page.SlideHeight - (height / 20f)) / 2f;
}
catch (Exception) {
x = y = 0;
}
shape = slide.Shapes.AddPicture(filename, MsoTriState.msoFalse, MsoTriState.msoTrue, x, y, width / 20f, height / 20f);
// set select
shape.AlternativeText = text;
shape.Name = name;
}
// copy properties across
// if (copyPicture)
// pic.AssignProperties(tagLoc.TagRange.Picture);
// delete the old picture as we wrote a new one.
if (isUpdate)
tagLoc.Delete();
// delete the temp file
File.Delete(filename);
return new PowerPointTagLocation(tagLoc.Tag, shape);
}
I put this in as a support ticket to Microsoft and the answer I finally got back was you cannot replace the picture.

Implementing circle progress bar in libGDX

I want to create a circle progress bar, that will look like this using libgdx library. For now on I've created simple horizontal progressbar using different Images as layers overlapping themselfs and one of them resizing to simulate progress. I found this example, how to draw circle progress bar, but I don't understand how to use this implementation and how can I handle it. The textureRegion in constructor is the progress indicator? and I could not find the method responsible to set the actual progress.
EDIT: I've made my solution basic on this implementation. I put 3 layers which overlapping themselfs and whenever the middle layer swipe's the progress is showed. But Ive have a problem: whenever I try to resize my ProgressCircle instance it move down below the background layer.
public class ProgressCircle extends Image {
public enum IntersectAt {
NONE, TOP, BOTTOM, LEFT, RIGHT;
}
TextureRegion texture;
PolygonSpriteBatch polyBatch;
Vector2 center;
Vector2 centerTop;
Vector2 leftTop;
Vector2 leftBottom;
Vector2 rightBottom;
Vector2 rightTop;
Vector2 progressPoint;
float[] fv;
IntersectAt intersectAt;
private float PROGRESS_WIDTH=0f;
private float PROGRESS_HEIGHT=0f;
private float posX,posY;
public ProgressCircle(TextureRegion region, PolygonSpriteBatch polyBatch,float width,float height,float posX,float posY) {
super(region);
PROGRESS_WIDTH=width;
PROGRESS_HEIGHT=height;
this.posX=posX;
this.posY=posY;
this.texture = region;
this.polyBatch = polyBatch;
center = new Vector2(this.getWidth() / 2, this.getHeight() / 2);
centerTop = new Vector2(this.getWidth() / 2, this.getHeight());
leftTop = new Vector2(0, this.getHeight());
leftBottom = new Vector2(0, 0);
rightBottom = new Vector2(this.getWidth(), 0);
rightTop = new Vector2(this.getWidth(), this.getHeight());
progressPoint = new Vector2(this.getWidth() / 2, this.getHeight() / 2);
setPercentage(0);
}
private Vector2 IntersectPoint(Vector2 line) {
Vector2 v = new Vector2();
boolean isIntersect;
//check top
isIntersect = Intersector.intersectSegments(leftTop, rightTop, center, line, v);
//check bottom
if (isIntersect) {
intersectAt = IntersectAt.TOP;
return v;
} else
isIntersect = Intersector.intersectSegments(leftBottom, rightBottom, center, line, v);
//check left
if (isIntersect) {
intersectAt = IntersectAt.BOTTOM;
return v;
} else isIntersect = Intersector.intersectSegments(leftTop, leftBottom, center, line, v);
//check bottom
if (isIntersect) {
intersectAt = IntersectAt.LEFT;
return v;
} else isIntersect = Intersector.intersectSegments(rightTop, rightBottom, center, line, v);
if (isIntersect) {
intersectAt = IntersectAt.RIGHT;
return v;
} else {
intersectAt = IntersectAt.NONE;
return null;
}
}
public void setPercentage(float percent) {
//100 % = 360 degree
float angle = convertToRadians(90);
angle -= convertToRadians(percent * 360 / 100);
float len = this.getWidth() > this.getHeight() ? this.getWidth() : this.getHeight();
float dy = (float) (Math.sin(angle) * len);
float dx = (float) (Math.cos(angle) * len);
Vector2 line = new Vector2(center.x + dx, center.y + dy);
Vector2 v = IntersectPoint(line);
if (intersectAt == IntersectAt.TOP) {
if (v.x >= this.getWidth() / 2) {
fv = new float[]{
center.x,
center.y,
centerTop.x,
centerTop.y,
leftTop.x,
leftTop.y,
leftBottom.x,
leftBottom.y,
rightBottom.x,
rightBottom.y,
rightTop.x,
rightTop.y,
v.x,
v.y
};
} else {
fv = new float[]{
center.x,
center.y,
centerTop.x,
centerTop.y,
v.x,
v.y
};
}
} else if (intersectAt == IntersectAt.BOTTOM) {
fv = new float[]{
center.x,
center.y,
centerTop.x,
centerTop.y,
leftTop.x,
leftTop.y,
leftBottom.x,
leftBottom.y,
v.x,
v.y
};
} else if (intersectAt == IntersectAt.LEFT) {
fv = new float[]{
center.x,
center.y,
centerTop.x,
centerTop.y,
leftTop.x,
leftTop.y,
v.x,
v.y
};
} else if (intersectAt == IntersectAt.RIGHT) {
fv = new float[]{
center.x,
center.y,
centerTop.x,
centerTop.y,
leftTop.x,
leftTop.y,
leftBottom.x,
leftBottom.y,
rightBottom.x,
rightBottom.y,
v.x,
v.y
};
} else // if (intersectAt == IntersectAt.NONE)
{
fv = null;
}
}
#Override
public void draw(Batch batch, float parentAlpha) {
if (fv == null) return;
batch.end();
drawMe();
batch.begin();
}
public void drawMe() {
EarClippingTriangulator e = new EarClippingTriangulator();
ShortArray sv = e.computeTriangles(fv);
PolygonRegion polyReg = new PolygonRegion(texture, fv, sv.toArray());
PolygonSprite poly = new PolygonSprite(polyReg);
poly.setSize(PROGRESS_WIDTH,PROGRESS_HEIGHT);
poly.setPosition(posX,posY);
poly.setOrigin(poly.getOriginX(), poly.getOriginY());
poly.setRotation(this.getRotation());
poly.setColor(Color.GREEN);
polyBatch.begin();
poly.draw(polyBatch);
polyBatch.end();
}
float convertToDegrees(float angleInRadians) {
float angleInDegrees = angleInRadians * 57.2957795f;
return angleInDegrees;
}
float convertToRadians(float angleInDegrees) {
float angleInRadians = angleInDegrees * 0.0174532925f;
return angleInRadians;
}
}
and my init function:
ProgressCircle sprite;
private void initCircleProgress() {
Group group = new Group();
Image downBackground = new Image(atlas.findRegion("progressBackground"));
downBackground.setColor(Color.BLUE);
downBackground.setSize(USER_SCREEN_SIZE_WIDTH/5,USER_SCREEN_SIZE_WIDTH/5);
downBackground.setPosition(10,USER_SCREEN_SIZE_HEIGHT-(2*downBackground.getHeight()+20));
sprite = new ProgressCircle(atlas.findRegion("progressBackground"), pbatch,
downBackground.getWidth(),downBackground.getHeight(),downBackground.getX(),downBackground.getY());
sprite.setSize(downBackground.getWidth(),downBackground.getHeight());
sprite.setSize(downBackground.getX(),downBackground.getY());
Image label = new Image(atlas.findRegion("progressBackground"));
label.setSize(downBackground.getWidth()*0.8f,downBackground.getHeight()*0.8f);
label.setPosition(downBackground.getX()+(downBackground.getWidth()-label.getWidth())/2,
downBackground.getY()+(downBackground.getHeight()-label.getHeight())/2);
label.setColor(Color.WHITE);
group.addActor(downBackground);
group.addActor(sprite);
group.addActor(label);
group.setPosition(10,GAME_SIZE_HEIGHT-(group.getHeight()+20));
stage.addActor(group);
}
You need a circular gradient to mask out the desired parts.
Incrementing the masking values over time will result in a circular motion.
As you can see in the link to masking there are several methods to achieve this. A stencil buffer would be a good choice but can result in jaggy edges. Writing your own shader would be perfect but kinda hard if you never wrote a shader. You can also try to parse both pix maps and decide what pixels to draw while looping through both pixmaps. If the masking image and your source have equal texture space you can do this in one loop so it would not be that much slower then a shader and whatever works for you is a good solution, you can always optimize later.

WriteableBitmap not invalidating and rendering in Windows Phone but working in silverlight5?

I am having a property which returns WriteableBitmap. when i am binding that property to the silverlight5 Image it showing the image, but when i am doing that in the WP it is not showing the image. when i am googling around this issue i saw that in WP the raw pixel values did not have alpha bits is set. But the same is working with silverlight. I don't know what is happening. Anybody have the similar issue or any round about?
(Imageproperty as WriteableBitmap).Invalidate();
<Image Source="{Binding Imageproperty}"/> (this is working in silverlight not in WP(7.1))
I had a similar sort of issue when trying to port some of the FaceLight code across from Silverlight to Windows Phone. The easiest way around this would be to manually set the Alpha value to 255 / opaque. So say if you had your WriteableBitmap that you wanted to display, result:
//convert to byte array
int stride = result.PixelWidth * 4; //brga32 is 32
int bytes = Math.Abs(stride) * result.PixelHeight;
byte[] data = result.ToByteArray();
int dataIndex = 0;
int nOffset = stride - result.PixelWidth * 4;
for (int y = 0; y < result.PixelHeight; ++y)
{
for (int x = 0; x < result.PixelWidth; ++x)
{
data[dataIndex + 3] = 0xFF; //set alpha to 255
dataIndex += 4; //skip to next pixel
}
dataIndex += nOffset;
}
WriteableBitmap finalImg = new WriteableBitmap(input.PixelWidth, input.PixelHeight);
return finalImg.FromByteArray(data);
Displaying the result from this method (the finalImg.FromByteArray(data) call) should display properly on the phone.
An alternative to this method above as well, is writing the WriteableBitmap to a .jpeg and then display the .jpeg instead - that seemed to work for me also but I didn't investigate that too thoroughly.
If the writeablebitmap has its pixeldata set or changed after the binding is established you need to call Invalidate to cause an update of the screen.
This goes for both Silverlight and Phone but you might have a race condition here that runs differently on both platforms.
I know it's an old post, but today I encountered the same problem on Windows Phone 8.1 Silverlight and I found nice solution, so I have decided to left it for people with similar problem. It was posted by Charles Petzold on MSDN as Video Feeds on Windows Phone 7 (it is shown on VideoSink class example, but it shouldn't be a problem to reproduce it with other case). He created a simple class that derives from VideoSink:
SimpleVideoSink C# code:
public class SimpleVideoSink : VideoSink
{
VideoFormat videoFormat;
WriteableBitmap writeableBitmap;
Action<WriteableBitmap> action;
public SimpleVideoSink(Action<WriteableBitmap> action)
{
this.action = action;
}
protected override void OnCaptureStarted() { }
protected override void OnCaptureStopped() { }
protected override void OnFormatChange(VideoFormat videoFormat)
{
this.videoFormat = videoFormat;
System.Windows.Deployment.Current.Dispatcher.BeginInvoke(() =>
{
writeableBitmap = new WriteableBitmap(videoFormat.PixelWidth,
videoFormat.PixelHeight);
action(writeableBitmap);
});
}
protected override void OnSample(long sampleTimeInHundredNanoseconds,
long frameDurationInHundredNanoseconds, byte[] sampleData)
{
if (writeableBitmap == null)
return;
int baseIndex = 0;
for (int row = 0; row < writeableBitmap.PixelHeight; row++)
{
for (int col = 0; col < writeableBitmap.PixelWidth; col++)
{
int pixel = 0;
if (videoFormat.PixelFormat == PixelFormatType.Format8bppGrayscale)
{
byte grayShade = sampleData[baseIndex + col];
pixel = (int)grayShade | (grayShade << 8) |
(grayShade << 16) | (0xFF << 24);
}
else
{
int index = baseIndex + 4 * col;
pixel = (int)sampleData[index + 0] | (sampleData[index + 1] << 8) |
(sampleData[index + 2] << 16) | (sampleData[index + 3] << 24);
}
writeableBitmap.Pixels[row * writeableBitmap.PixelWidth + col] = pixel;
}
baseIndex += videoFormat.Stride;
}
writeableBitmap.Dispatcher.BeginInvoke(() =>
{
writeableBitmap.Invalidate();
});
}
}
However, this code needs some modification - VideoSink.CaptureSource must be provided with our CaptureSource (I just passed it into constructor):
public SimpleVideoSink(CaptureSource captureSource, Action<WriteableBitmap> action)
{
base.CaptureSource = captureSource;
this.action = action;
}
When we initialize SimpleVideoSink class in a ViewModel, we have to provide it an Action parameter. In my case it was enough to provide ViewModel with initialized field writeableBitmap:
ViewModel C# code:
private WriteableBitmap videoWriteableBitmap;
public WriteableBitmap VideoWriteableBitmap
{
get
{
return videoWriteableBitmap;
}
set
{
videoWriteableBitmap = value;
RaisePropertyChanged("VideoWriteableBitmap");
}
}
private void OnWriteableBitmapChange(WriteableBitmap writeableBitmap)
{
VideoWriteableBitmap = writeableBitmap;
}
//this part goes to constructor/method
SimpleVideoSink videoFrameHandler = new SimpleVideoSink(captureSource, OnWriteableBitmapChange);
Then all we have to do is to bind it to View:
View XAML code:
<Image Source="{Binding VideoWriteableBitmap}" />
In this example Image source is refreshed on every OnSample method invocation, and is dispatched to main thread through WriteableBitmap.Dispatcher.
This solution generates proper image with no blank pixel (alpha channel is also filled), and Invalidate() method works as it should.

Resources