I am working on xamarin.forms app. for that i need circular progressbar. I almost got it working in xamarin.ios using CustomRenderer but its not actually what i wanted.
My circular progressbar in xamarin.ios renderer works but starts from the right edge of the containg rectangle. but I want it to start from the top edge in clockwise direction.
I tried to change start angle to -90 or 270 but still it starts from the right side and sometimes it doesn't work also. I am not sure in which condition its not working but I think when I use Math.PI constant in code, Draw never calls. I have referred this link.
class NativeProgressBarRenderer : ViewRenderer
{
private bool _sizeChanged = false;
//private CG _paint;
private CGRect _ringDrawArea;
private nfloat _radius;
const float FULL_CIRCLE = 2 * (float)Math.PI;
// int _radius = 10;
float _lineWidth = 10;
nfloat _percentComplete = 0.0f;
UIColor _backColor = UIColor.LightGray; //UIColor.FromRGB(46, 60, 76);
UIColor _frontColor = UIColor.Green; //UIColor.FromRGB(234, 105, 92);
protected override void OnElementPropertyChanged(object sender,
System.ComponentModel.PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == ProgressBar.ProgressProperty.PropertyName ||
e.PropertyName == PlayerProgressBar.RingThicknessProperty.PropertyName ||
e.PropertyName == PlayerProgressBar.RingBaseColorProperty.PropertyName ||
e.PropertyName == PlayerProgressBar.RingProgressColorProperty.PropertyName)
{
SetNeedsLayout();
SetNeedsDisplay();
}
if (e.PropertyName == VisualElement.WidthProperty.PropertyName ||
e.PropertyName == VisualElement.HeightProperty.PropertyName)
{
_sizeChanged = true;
SetNeedsLayout();
SetNeedsDisplay();
}
}
public override void Draw(CoreGraphics.CGRect rect)
{
base.Draw(rect);
using (CGContext g = UIGraphics.GetCurrentContext())
{
var progressRing = (PlayerProgressBar)Element;
// Get Metrics
var mainDisplayInfo = DeviceDisplay.MainDisplayInfo;
// Screen density
var density = mainDisplayInfo.Density;
_lineWidth = (float)Math.Ceiling(progressRing.RingThickness * density);
var diameter = Math.Min(this.Bounds.Width, this.Bounds.Height);
_radius = (int)(diameter / 2) - _lineWidth;
_backColor = progressRing.RingBaseColor.ToUIColor();
_frontColor = progressRing.RingProgressColor.ToUIColor();
_percentComplete = (float)progressRing.Progress;
var x = Bounds.GetMidX();
var y = Bounds.GetMidY();
//DrawGraph(g, Bounds.Left, Bounds.Top); // Tried to change x,y
DrawGraph(g, Bounds.GetMidX(), this.Bounds.GetMidY());
};
}
public void DrawGraph(CGContext g, nfloat x, nfloat y)
{
//g.ScaleCTM(1, -1);
//g.TranslateCTM(0, -Bounds.Height);
//g.RotateCTM(270);
g.SetLineWidth(_lineWidth);
// Draw background circle
CGPath path = new CGPath();
_backColor.SetStroke();
path.AddArc(x, y, _radius, 270, _percentComplete * FULL_CIRCLE, true);
g.AddPath(path);
g.DrawPath(CGPathDrawingMode.Stroke);
// Draw overlay circle
var pathStatus = new CGPath();
_frontColor.SetStroke();
//CGAffineTransform cGAffineTransform = new CGAffineTransform();
//cGAffineTransform.Rotate(-90); // This also doesn't work
// Same Arc params except direction so colors don't overlap
pathStatus.AddArc(x, y, _radius, 0, _percentComplete * FULL_CIRCLE, false);
g.AddPath(pathStatus);
g.DrawPath(CGPathDrawingMode.Stroke);
//cGAffineTransform.Invert();
}
I expect to run it from top of the circle. Kindly help!
Thank you
This is because the start angle is not beginning at 0 o'clock. Here is its default coordinate system:
So each angle should minus 0.5π. Modify your code like:
// Draw overlay circle
var pathStatus = new CGPath();
_frontColor.SetStroke();
pathStatus.AddArc(x, y, _radius, -0.25f * FULL_CIRCLE, (_percentComplete-0.25f) * FULL_CIRCLE, false);
g.AddPath(pathStatus);
g.DrawPath(CGPathDrawingMode.Stroke);
Related
I made a test game in unity that makes it so when I click on a button, it spawns a cylinder created from a factory class. I'm trying to make it so when I create the cylinder, its height shrinks over the next 20 seconds. Some methods I found are difficult to translate into what I'm doing. If you could lead me to the right direction, I'd very much appreciate it.
Here's my code for the cylinder class
public class Cylinder : Shape
{
public Cylinder()
{
GameObject cylinder = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
cylinder.transform.position = new Vector3(3, 0, 0);
cylinder.transform.localScale = new Vector3(1.0f, Random.Range(1, 2)-1*Time.deltaTime, 1.0f);
cylinder.GetComponent<MeshRenderer>().material.color = Random.ColorHSV();
Destroy(cylinder, 30.0f);
}
}
This can be done with the Time.deltaTime and Vector3.Lerp in a coroutine function. Similar to Rotate GameObject over time and Move GameObject over time questions. Modified it a little bit to do just this.
bool isScaling = false;
IEnumerator scaleOverTime(Transform objectToScale, Vector3 toScale, float duration)
{
//Make sure there is only one instance of this function running
if (isScaling)
{
yield break; ///exit if this is still running
}
isScaling = true;
float counter = 0;
//Get the current scale of the object to be moved
Vector3 startScaleSize = objectToScale.localScale;
while (counter < duration)
{
counter += Time.deltaTime;
objectToScale.localScale = Vector3.Lerp(startScaleSize, toScale, counter / duration);
yield return null;
}
isScaling = false;
}
USAGE:
Will scale GameObject within 20 seconds:
StartCoroutine(scaleOverTime(cylinder.transform, new Vector3(0, 0, 90), 20f));
Check out Lerp. A general example of how to use it would be something like this:
float t = 0;
Update()
{
t += Time.deltaTime;
cylinder.localScale = new Vector3(1, Mathf.Lerp(2f, 1f, t/3f), 1); // shrink from 2 to 1 over 3 seconds;
}
You will create a new monobehaviour script and add it to your primitive. Then you wil use "Update" method of monobehaviour (or use coroutine) for change object over time.
Monobehaviour must be look like this:
public class ShrinkBehaviour : MonoBehaviour
{
bool isNeedToShrink;
Config currentConfig;
float startTime;
float totalDistance;
public void StartShrink(Config config)
{
startTime = Time.time;
currentConfig = config;
totalDistance = Vector3.Distance(currentConfig.startSize, currentConfig.destinationSize);
isNeedToShrink = true;
transform.localScale = config.startSize;
}
private void Update()
{
if (isNeedToShrink)
{
var nextSize = GetNextSize(currentConfig);
if (Vector3.Distance(nextSize, currentConfig.destinationSize) <= 0.05f)
{
isNeedToShrink = false;
return;
}
transform.localScale = nextSize;
}
}
Vector3 GetNextSize(Config config)
{
float timeCovered = (Time.time - startTime) / config.duration;
var result = Vector3.Lerp(config.startSize, config.destinationSize, timeCovered);
return result;
}
public struct Config
{
public float duration;
public Vector3 startSize;
public Vector3 destinationSize;
}
}
For using this, you must do next:
public Cylinder()
{
GameObject cylinder = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
var shrink = cylinder.AddComponent<ShrinkBehaviour>();
shrink.StartShrink(new ShrinkBehaviour.Config() { startSize = Vector3.one * 10, destinationSize = Vector3.one * 1, duration = 20f });
cylinder.transform.position = new Vector3(3, 0, 0);
cylinder.GetComponent<MeshRenderer>().material.color = Random.ColorHSV();
Destroy(cylinder, 30.0f);
}
You must remember, monobehaviour-script must be in separate file, and must have name similar to monobehaviour-class name. For example, ShrinkBehaviour.cs;
I'm working on a blackberry project and for that I need to create grid layout. I'm working on "Blackberry java sdk".
I'm using this code
public class GridScreen extends UiApplication {
// main method
public static void main(String[] args) {
GridScreen theApp = new GridScreen();
UiApplication.getUiApplication().pushScreen(new GFMScreen());
theApp.enterEventDispatcher();
}
}
// VFM
class GFMScreen extends MainScreen {
public GFMScreen() {
// this doesnt do anything for VCENTER!!
//super(Field.USE_ALL_HEIGHT);
// create a grid field manager, with 2 cols and 0 style param for super class
// style of Manager.FIELD_VCENTER | Field.USE_ALL_HEIGHT doesnt do a thing!
int columns = 2;
final GridFieldManager gfm = new GridFieldManager(columns, 0);
// add some items to the screen
int size = 6;
BitmapField[] fRay = new BitmapField[size];
for (int i = 0; i < size; i++) {
// create an bitmap field that's centered H + V (inside grid space)
fRay[i] = new BitmapField(loadBitmap("images/" + (i + 1) + ".png"),
Field.FIELD_HCENTER | Field.FIELD_VCENTER | Field.FOCUSABLE);
gfm.add(fRay[i]);
}
// set padding on top/bottom
{
// add gfm to screen - this does not center the gfm on the screen... is top aligned no matter what!
add(gfm);
int gfmHeight = 48 * (size / columns);
int borderHeight = (Display.getHeight() - gfmHeight) / 2;
gfm.setBorder(BorderFactory.createSimpleBorder(
new XYEdges(borderHeight, 0, borderHeight, 0),
Border.STYLE_TRANSPARENT));
System.out.println("border=" + borderHeight);
System.out.println("display=" + Display.getHeight());
System.out.println("gfm=" + gfmHeight);
}
}
/** #param res eg "images/icon.png" */
public static Bitmap loadBitmap(String res) {
EncodedImage img = EncodedImage.getEncodedImageResource(res);
return img.getBitmap();
}
}// end class
What is wrong in this code?
Is there any best approch to create grid layout in BlackBerry.
In above code error is "Display.getHeight() is not define".
Hope this code helps:
Bitmap[] images = new Bitmap[6];
for ((int i = 0; i < 6; i++) {
string filename = "images/" + String.valueOf(i + 1) + ".png";
images[i] = Bitmap.getBitmapResource(filename);
}
}
Actually I want to play a video in quad textured but the displayed video's color is some kind of depreciated if compared if I draw with the rectangle..
Below is the example taken from mdsn plus abit of modification, can anyone check for me please?
Thanks in advance.
public Game1()
{
graphics = new GraphicsDeviceManager(this);
graphics.PreferredBackBufferWidth = 1920;
graphics.PreferredBackBufferHeight = 1080;
graphics.PreferMultiSampling = false;
graphics.IsFullScreen = false;
// Set the back buffer format to color
graphics.PreferredBackBufferFormat = SurfaceFormat.Color;
Content.RootDirectory = "Content";
}
protected override void Initialize()
{
// TODO: Add your initialization logic here
PresentationParameters pp = GraphicsDevice.PresentationParameters;
pp.MultiSampleCount = 20;
quad = new Quad(Vector3.Zero, Vector3.Backward, Vector3.Up, 1,1);
camera = new Camera(this, new Vector3(0, 0, 1.15f), Vector3.Zero, Vector3.Up);
Components.Add(camera);
base.Initialize();
}
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
// TODO: use this.Content to load your game content here
video = Content.Load<Video>("1");
player = new VideoPlayer();
player.IsLooped = true;
// Setup our BasicEffect for drawing the quad
World = Matrix.Identity;
quadEffect = new BasicEffect(graphics.GraphicsDevice);
quadEffect.EnableDefaultLighting();
quadEffect.View = camera.view;
quadEffect.Projection = camera.projection;
quadEffect.TextureEnabled = true;
// Create a vertex declaration so we can call
// DrawUserIndexedPrimitives later
quadVertexDecl = new VertexDeclaration(VertexPositionTexture.VertexDeclaration.GetVertexElements());
}
protected override void Update(GameTime gameTime)
{
// Allows the game to exit
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
// TODO: Add your update logic here
// Play the video if it isn't already.
if (player.State != MediaState.Playing)
player.Play(video);
KeyboardState state = Keyboard.GetState();
.....
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Black);
// TODO: Add your drawing code here
RasterizerState rs = new RasterizerState();
rs.CullMode = CullMode.None;
GraphicsDevice.RasterizerState = rs;
quadEffect.World = World;
if (player.State == MediaState.Playing)
quadEffect.Texture = player.GetTexture();
foreach (EffectPass pass in quadEffect.CurrentTechnique.Passes)
{
pass.Apply();
GraphicsDevice.SamplerStates[0] = SamplerState.LinearClamp;
GraphicsDevice.DrawUserIndexedPrimitives(
PrimitiveType.TriangleList,
quad.Vertices, 0, 4,
quad.Indices, 0, 2);
}
base.Draw(gameTime);
}
You're using quadEffect.EnableDefaultLighting(); and that will add a light source. You can disable it by setting quadEffect.LightingEnabled = false;
About: I have a panel displaying a map that is constructed out of polygons and lines. To this map I apply transformations and provide a way to walk around (first-person-like) with rotating and moving the map with transformations.
The transformations applied to the map seem to block (or atleast slowdown) KeyEvents from beeing handled. Sometimes the application keeps rotating even if I released the key.
Also I noticed that the rotation transformation has a bigger impact on this effect.
The collision-detection has no effect on this (since its disabled at this point).
Due to the structure of the application I bind centerR to pointerR.
centerR.bind(pointerR);
Transformations applied:
mapElements = new Group();
Scale s = new Scale();
s.xProperty().bind(zoom);
s.yProperty().bind(zoom);
s.setPivotX(panelBounds.getWidth() / 2);
s.setPivotY(panelBounds.getHeight() / 2);
mapElements.getTransforms().add(s);
Translate t = new Translate();
t.xProperty().bind(new SimpleDoubleProperty(panelBounds.getWidth() / 2).subtract(centerX));
t.yProperty().bind(new SimpleDoubleProperty(panelBounds.getHeight() / 2).subtract(centerY));
mapElements.getTransforms().add(t);
Rotate r = new Rotate();
r.angleProperty().bind(centerR.multiply(-1));
r.pivotXProperty().bind(centerX);
r.pivotYProperty().bind(centerY);
mapElements.getTransforms().add(r);
KeyListener-Setup:
setOnKeyPressed(new EventHandler<KeyEvent>()
{
#Override
public void handle(KeyEvent event)
{
System.out.println(event);
if (event.getCode() == KeyCode.UP)
moveForwards = true;
if (event.getCode() == KeyCode.DOWN)
moveBackwards = true;
if (event.getCode() == KeyCode.LEFT)
rotateLeft = true;
if (event.getCode() == KeyCode.RIGHT)
rotateRight = true;
}
});
setOnKeyReleased(new EventHandler<KeyEvent>()
{
#Override
public void handle(KeyEvent event)
{
System.out.println(event);
if (event.getCode() == KeyCode.UP)
moveForwards = false;
if (event.getCode() == KeyCode.DOWN)
moveBackwards = false;
if (event.getCode() == KeyCode.LEFT)
rotateLeft = false;
if (event.getCode() == KeyCode.RIGHT)
rotateRight = false;
}
});
OnFrame-Rotation:
private final double fps = 20;
private final KeyFrame frame = new KeyFrame(Duration.millis(1000 / fps), new EventHandler<ActionEvent>()
{
#Override
public void handle(ActionEvent event)
{
double movement = ((moveForwards ? 3 : 0)
+ (moveBackwards ? -3 : 0))
* movementSpeed;
double rotation = ((rotateLeft ? -3 : 0)
+ (rotateRight ? 3 : 0))
* rotateSpeed;
double oldX = pointerX.get();
double oldY = pointerY.get();
pointerX.set(pointerX.get()
+ Math.cos((pointerR.get() - 90) * Math.PI / 180)
* movement);
pointerY.set(pointerY.get()
+ Math.sin((pointerR.get() - 90) * Math.PI / 180)
* movement);
if (checkCollisions)
handleCollision(oldX, oldY, pointerX.get(), pointerY.get());
pointerR.set(pointerR.get() + rotation);
}
});
What node are you calling on setOnKeyPressed on? Looking at the javadoc for this method:
Defines a function to be called when this Node or its child Node
has input focus and a key has been pressed. The function is called
only if the event hasn't been already consumed during its capturing
or bubbling phase.
Make sure the listener is added to the Scene if it is not already and nothing calls the consume method on the event object.
I am trying to move a pool ball in Java2ME. It is easy when the velocity is stable. I change the x and y coordinate of the ball according to x and y velocity. They are all integers. No problem. However a normal pool ball has to go first fast then slowdown and stop. Because the x and y coordinates of the ball are integers, I can't decrease the x and y velocity by percentage. I mean if the velocity is 9 and I want to decrease it by 10% I cant do it "9 * 0.1" because it has to be an integer. I know the coordinates can't be double. What can I do?
the code:
public void move() {
//... Move the ball at the given velocity.
m_x += m_velocityX; // m_x: x coordinate of the ball
m_y += m_velocityY; // m_y: y coordinate of the ball
//... ball hits the borders and change the way
if (m_x < 0) { // If at or beyond left side
m_x = 0; // Place against edge and
m_velocityX = -m_velocityX; // reverse direction.
} else if (m_x > m_rightBound) { // If at or beyond right side
m_x = m_rightBound; // Place against right edge.
m_velocityX = -m_velocityX; // Reverse direction.
}
if (m_y < 0) { // if we're at top
m_y = 0;
m_velocityY = -m_velocityY;
} else if (m_y > m_bottomBound) { // if we're at bottom
m_y = m_bottomBound;
m_velocityY = -m_velocityY;
}
}
If the velocity must be integral, just update the values to the floor of the floating-point calculation. So to reduce velocity by 10%:
m_velocityX = floor(m_velocityX * 0.9);
You might want to do something fancier someday, but this seems simple and workable.
You should store the ball speed as velocity and angle.
Also, every variable mentioned should be float or double.
Then everything will be easier, and much more accurate.
The algorithm will then be:
float x,y,velocity,angle
int ix,iy;
...
{
if(y<0) angle=-angle;
... etc, etc.
velocity*=0.95;
x+=velocity*cos(angle);
y+=velocity*sin(angle);
// And you get your precious integers ...
ix=floor(x);
iy=floor(y);
}
if the velocity is 9 and I want to decrease it by 10% I cant do it "9 * 0.1" because it has to be an integer
scale up the velocity and coordinates (multiply eg by 256 or shift left by eg 8)
calculate decrease for scaled up velocity "9 * 256 / 10"
calculate new (scaled up) position and velocity
scale down
About like below...
Ball move(Ball ball, Border border, Tracer tracer) {
tracer.trace("scale stuff up to handle acceleration = velocity / 8");
Scale scale = new Scale(256);
Int2D position = scale.up(ball.position);
Velocity velocity = new Velocity(scale.up(ball.velocity));
tracer.trace("calculate acceleration as scaled up velocity / 8";
Int2D acceleration = new Scale(8).down(velocity);
tracer.trace("Move the ball at the given velocity");
position = position.plus(velocity);
tracer.trace("slow down velocity");
velocity = velocity.slowDown(acceleration);
tracer.trace("scale back down to original");
ball = new Ball(scale.down(position), new Velocity(scale.down(velocity)));
tracer.trace("adjust if ball hits the borders and change the way");
return border.reflectIfNeeded(ball);
}
interface Tracer { void trace(String msg); }
class Scale {
final int scale; // better be power of 2
Scale(int scale) { this.scale = scale; }
Int2D up(Int2D src) { return new Int2D(src.x * scale, src.y * scale); }
Int2D down(Int2D src) { return new Int2D(src.x / scale, src.y / scale); }
} // Scale
class Border {
final Int2D topLeft, bottomRight;
Border(Int2D topLeft, Int2D bottomRight) {
this.topLeft = topLeft;
this.bottomRight = bottomRight;
}
Ball reflectIfNeeded(Ball ball) {
if (within(ball)) { return ball; }
throw new UnsupportedOperationException("not yet implemented");
}
private boolean within(Ball ball) {
return within(ball.position.x, topLeft.x, bottomRight.x)
&& within(ball.position.y, topLeft.y, bottomRight.y);
}
private boolean within(int value, int min, int max) {
return value > min && value < max;
}
} // Border
class Ball {
final Int2D position;
final Velocity velocity;
Ball(Int2D position, Velocity velocity) {
this.position = position;
this.velocity = velocity;
}
} // Ball
class Velocity extends Int2D {
Velocity(Int2D src) { super(src.x, src.y); }
Velocity slowDown(Int2D acceleration) {
return new Velocity(this.minus(acceleration));
}
Velocity reflectX() { return new Velocity(new Int2D(-x, y)); }
Velocity reflectY() { return new Velocity(new Int2D(x, -y)); }
} // Velocity
class Int2D {
final int x, y;
Int2D(int x, int y) { this.x = x; this.y = y; }
Int2D plus(Int2D other) { return new Int2D(x + other.x, y + other.y); }
Int2D minus(Int2D other) { return new Int2D(x - other.x, y - other.y); }
} // Int2D