I am making a double pendulum simulation in javafx. I want to trace the lower bob's path by a clear line, not a trace of dots(which I have achieved). I am new to javafx. I have not used any transitions to animate it. I have used Timeline and KeyFrame to make it work.
private static void doCalc(){
a1_v+=a1_a;
a2_v+=a2_a;
a1+=a1_v;
a2+=a2_v;
a1_a = calcA1();
a2_a = calcA2();
System.out.println(a1_a+"\n"+a2_a+"\n");
x1 = calcX(x0,a1,r1);
y1 = calcY(y0,a1,r1);
x2 = calcX(x1,a2,r2);
y2 = calcY(y1,a2,r2);
Circle tracer = new Circle(x2,y2,1);
tracer.setFill(Color.BLUE);
root.getChildren().add(0,tracer);
}
This is the method that does the calculation and adds the tracer every time a new frame is created. But I want a single line to represent my node's path, not a trail of dots.
This might be a stupid question and I know my coding practices in javafx are a bit weak. I was just looking for a small solution to implement so that I can quickly add it to my simulator. Thanks in advance :)
Just add a line from the old coordinates to the new ones:
private static void doCalc(){
// I think these must be the old coordinates you need:
double oldX2 = x2 ;
double oldY2 = y2 ;
a1_v+=a1_a;
a2_v+=a2_a;
a1+=a1_v;
a2+=a2_v;
a1_a = calcA1();
a2_a = calcA2();
System.out.println(a1_a+"\n"+a2_a+"\n");
x1 = calcX(x0,a1,r1);
y1 = calcY(y0,a1,r1);
x2 = calcX(x1,a2,r2);
y2 = calcY(y1,a2,r2);
Line traceLine = new Line(oldX2, oldY2, x2, y2);
traceLine.setStroke(Color.BLUE);
root.getChildren().add(0,traceLine);
}
You could also use a Path and update it:
// do this once:
Path trace = new Path();
trace.getElements().add(new MoveTo(initialX2, initialY2));
trace.setStroke(Color.BLUE);
root.getChildren().add(trace);
// ...
private static void doCalc(){
a1_v+=a1_a;
a2_v+=a2_a;
a1+=a1_v;
a2+=a2_v;
a1_a = calcA1();
a2_a = calcA2();
System.out.println(a1_a+"\n"+a2_a+"\n");
x1 = calcX(x0,a1,r1);
y1 = calcY(y0,a1,r1);
x2 = calcX(x1,a2,r2);
y2 = calcY(y1,a2,r2);
trace.getElements().add(new LineTo(x2, y2));
}
Note that either of these approaches (and indeed your original approach with the circles) create indefinitely many UI nodes and add them to the scene. Consequently, if the simulation runs for long enough, you will eventually run out of memory. If the simulation is expected to run for a while, you probably want to limit the number of these you keep in memory (e.g. stop adding new lines after some fixed number, or keep the number at a maximum by removing the earlier ones once you reach a limit, etc).
Related
I am stuck trying to figure out the magic Object for working with Xamarin.TensorFlow.Lite Interpreter.RunForMultipleInputsOutputs().
My object detection model outputs the following (exported from Azure Custom Vision)
detected_boxes The detected bounding boxes. Each bounding box is represented as [x1, y1, x2, y2] >where (x1, y1) and (x2, y2) are the coordinates of box corners.
detected_scores Probability for each detected boxes.
detected_classes The class index for the detected boxes.
I know it needs to fit something like how the tensorflow lite docs describe the output (except the number of detections):
Most of the examples I have found only work with image classification models which only have 1 output: blog1 blog2 However I did find an Issue that is that exact same issue I am having: https://github.com/xamarin/XamarinComponents/issues/565
I tried following the tensorflow api, and the example code of the GitHub issue(appreciated the sharing of that), but still crashing in the oblivion (Object not set to instance of object).
int detectedBoxesOutputIndex = Interpreter.GetOutputIndex("detected_boxes"); // 0
int detectedClassesOutputIndex = Interpreter.GetOutputIndex("detected_classes"); // 1
int detectedScoresOutputIndex = Interpreter.GetOutputIndex("detected_scores"); // 2
int numDetections = Interpreter
.GetOutputTensor(detectedClassesOutputIndex)
.NumElements();
var outputDict = new Dictionary<Java.Lang.Integer, Java.Lang.Object>();
int batchSize = 1;
// new float [][][]
_OutputBoxes = CreateJaggedArray(batchSize, numDetections, 4);
_OutputClasses = CreateJaggedArray(batchSize, numDetections);
_OutputScores = CreateJaggedArray(batchSize, numDetections);
var mOutputBoxes = OutputBoxes;
var mOutputClasses = OutputClasses;
var mOutputScores = OutputScores;
Java.Lang.Object[] inputArray = { imageByteBuffer };
var outputMap = new Dictionary<Java.Lang.Integer, Java.Lang.Object>();
outputMap.Add(new Java.Lang.Integer(detectedBoxesOutputIndex), mOutputBoxes);
outputMap.Add(new Java.Lang.Integer(detectedClassesOutputIndex), mOutputClasses);
outputMap.Add(new Java.Lang.Integer(detectedScoresOutputIndex), mOutputScores);
//stuck here
Interpreter.RunForMultipleInputsOutputs(inputArray, outputMap);
I can get the output of the first tensor via Interpreter.Run(img,output) where output is a float[64][4], so I know the model and all the image byte buffer prep is done correctly.
My Xamarin Forms Project: https://github.com/twofingerrightclick/GardenDefenseSystem/blob/master/GardenDefenseSystem/GardenDefenseSystem.Android/TensorflowObjectDetector.cs
I created an Issue on github, and solved the problem there in detail : https://github.com/xamarin/GooglePlayServicesComponents/issues/668
I've recently been working a lot with CFrame mechanics while scripting and I've got kind of stuck on this.
Even after using .lookVector, or even Vector3, the Jetpack model position stays equal to the position of the Torso instead of 5 (* -5) behind the torso.
Here is the code I have so far:
local player = script.Parent
local jetpack = game.ReplicatedStorage.Jetpack
local jetpackClone = jetpack:Clone()
jetpackClone.PrimaryPart = jetpackClone.Core
jetpackClone.Parent = player
jetpackClone:moveTo(player.Torso.Position + player.Torso.CFrame.lookVector * -5)
local weld = Instance.new("Motor6D")
weld.Parent = jetpackClone.Core
weld.Part0 = jetpackClone.Core
weld.Part1 = player.Torso
The way to fix this is quite simple.
Like Ahmad said, moveTo is used for models that doesn't have a primary part. But it is not only that.
One thing that Ahmad forgot is that 'lookVector' is not a CFrame, it is a Vector3 instead.
In this fragment, you did
moveTo(player.Torso.Position + player.Torso.CFrame.lookVector * -5)
That would be fine, if you didn't use a numerical value with Vector3's. To fix this, instead, your code should be
local player = script.Parent
local jetpack = game.ReplicatedStorage.Jetpack;
local jetpackClone = jetpack:Clone();
jetpackClone.PrimaryPart = jetpackClone.Core;
jetpackClone.Parent = player; -- Is the 'Player' a Player, or a character??
local weld = Instance.new("Weld", player.Torso) -- We use 'Weld' here, instead of Motor6D
weld.Part1 = jetpackClone.Core;
weld.Part0 = player.Torso;
weld.C0 = CFrame.new(0, 0, -5); -- We use the C0 property of Weld's
That should do it for welding the jetpack to your torso. Though, I would check if the CFrame is correct in it, I am not sure if it is or not, but if the jetpack appears in front of your torso, then replace weld.C0 = CFrame.new(0, 0, -5) to weld.C0 = CFrame.new(0, 0, 5).
Hope my answer helps!
moveTo is used for models that doesn't have a primary part. Which can be inaccurate. Instead use :SetPrimaryPartCFrame() also u were adding Position+CFrame(it would cause an error did u check output?)
I'm currently developing an android app for reading out multiple sensor values via Bluetooth and display them in a graph. When I stumbled upon jjoe64's GraphViewLibrary, I knew this would fit my purposes perfectly. But now I'm kind of stuck. Basically, I wrote a little function that would generate and display the values of three sensors in 3 different graphs one under the other. This works just fine when the activity is started first, all three graphs a nicely rendered and displayed. But when I want to update the graphs with different values using the resetData()-method to render the new values in each graph, only the last of the three graphs is updated. Obviously, because it's the last graph generated using this rather simple function. My question is: Is there any other elegant way to use a function like mine for generating and updating all three graphs one after the other? I already tried to set the GraphView variable back to null and different combinations of removing and adding the view. Passing the function a individual GraphView-variable like graphView1, graphView2... does also not work.
Here is the function:
private GraphView graphView;
private GraphViewSeries graphViewSerie;
private Boolean graphExisting = false;
...
public void makeGraphs (float[] valueArray, String heading, int graphId) {
String graphNumber = "graph"+graphId;
int resId = getResources().getIdentifier(graphNumber,"id", getPackageName());
LinearLayout layout = (LinearLayout) findViewById(resId);
int numElements = valueArray.length;
GraphViewData[] data = new GraphViewData[numElements];
for (int c = 0; c<numElements; c++) {
data[c] = new GraphViewData(c+1, valueArray[c]);
Log.i(tag, "GraphView Graph"+graphId+": ["+(c+1)+"] ["+valueArray[c]+"].");
}
if (!graphExisting) {
// init temperature series data
graphView = new LineGraphView(
this // context
, heading // heading
);
graphViewSerie = new GraphViewSeries(data);
graphView.addSeries(graphViewSerie);
((LineGraphView) graphView).setDrawBackground(true);
graphView.getGraphViewStyle().setNumHorizontalLabels(numElements);
graphView.getGraphViewStyle().setNumVerticalLabels(5);
graphView.getGraphViewStyle().setTextSize(10);
layout.addView(graphView);
}
else {
//graphViewSerie = new GraphViewSeries(data);
//graphViewSerie.resetData(data);
graphViewSerie.resetData(new GraphViewData[] {
new GraphViewData(1, 1.2f)
, new GraphViewData(2, 1.4f)
, new GraphViewData(2.5, 1.5f) // another frequency
, new GraphViewData(3, 1.7f)
, new GraphViewData(4, 1.3f)
, new GraphViewData(5, 1.0f)
});
}
And this is the function-call depending on an previously generated array (which is being monitored to be filled with the right values):
makeGraphs(graphData[0], "TempHistory", 1);
makeGraphs(graphData[1], "AirHistory", 2);
makeGraphs(graphData[2], "SensHistory", 3);
graphExisting = true;
Any help and / or any feedback in general is greatly appreciated! Lots of thanks in advance!
EDIT / UPDATE:
Thanks to jjoe64's answer I was able to modify the function to work properly. I was clearly having a mistake in my thinking, since I thought I'd also be changing a GraphViewSeries-object I would handle my function as additional parameter (which I tried before). Of course this does not work. However, with this minor Improvements I managed to make this work using a Graphviewseries Array. To give people struggling with a similar problem an idea of what I had to change, here the quick-and-dirty draft of the solution.
I just changed
private GraphViewSeries graphViewSerie;
to
private GraphViewSeries graphViewSerie[] = new GraphViewSeries[3];
and access the right Series using the already given parameter graphId within the function (if-clause) like this:
int graphIndex = graphId - 1;
graphViewSerie[graphIndex] = new GraphViewSeries(data);
In the else-clause I'm updating the series likewise by calling
graphViewSerie[graphIndex].resetData(data);
So, once again many thanks for your support, jjoe64. I'm sorry I wasn't able to update the question earlier, but I did not find time for it.
of course it is not working correct, because you save always the latest graphseries-object in the member graphViewSerie.
First you have to store the 3 different graphviewseries (maybe via array or map) and then you have to access the correct graphviewseries-object in the else clause.
Based on my experience with Zedgraph I could not set both of X and Y axes scale same to have a correct scatter graph! Assume we have a square grid of 10x10m cells in which each cell is a square shape 1x1m. when we try to draw points of such data, the output is not acceptable because each axis scaled to different scale. It is worse when we try to call Zoomall, then we find all points are fitted to chart area regardless their equal spacing!
I hope someone can help me to find a solution. Although Zedgraph is flexible library but this is a big fault!
perfectly aware, this q. is 9 years old, but still..
Have just encountered and solved the issue of presenting the graph in a square pane.
(It seems that was the OP question)
It's a bit "brute" and invokes redundant redraws, but gets the job done.
//"GraphWinFormsHost" is my ZGraph container
GraphWinFormsHost.SizeChanged += (sender, args) =>
{
//"IsEqualScale" is my property, indicating a square is needed
if(_ChartData == null || !_ChartData.IsEqualScale)
return;
_ZedGraphControl.GraphPane.Chart.IsRectAuto = true;
_ZedGraphControl.Refresh();
//here, the graph pane is redrawn according to available space
float x = _ZedGraphControl.GraphPane.Chart.Rect.X;
float y = _ZedGraphControl.GraphPane.Chart.Rect.Y;
float h = _ZedGraphControl.GraphPane.Chart.Rect.Height;
float w = _ZedGraphControl.GraphPane.Chart.Rect.Width;
float min = Math.Min(h, w);
_ZedGraphControl.GraphPane.Chart.Rect = new RectangleF(x, y, min, min);
};
Is Scale.IsAnyOrdinal true for any Axis.Scale's ?
ZedGraph appears to position nodes based on index offset rather than node value when the Scale.Type is set to AxisType.Text, Ordinal, DateAsOrdinal, or LinearAsOrdinal.
I recently had to solve the same problem. This is what has worked for me:
zg1.AxisChange();
if (myPane.XAxis.Scale.Max > myPane.YAxis.Scale.Max) {
myPane.YAxis.Scale.Max = myPane.XAxis.Scale.Max;
myPane.YAxis.Scale.Min = myPane.XAxis.Scale.Min;
myPane.YAxis.Scale.MajorStep = myPane.XAxis.Scale.MajorStep;
} else {
myPane.XAxis.Scale.Max = myPane.YAxis.Scale.Max;
myPane.XAxis.Scale.Min = myPane.YAxis.Scale.Min;
myPane.XAxis.Scale.MajorStep = myPane.YAxis.Scale.MajorStep;
}
zg1.AxisChange();
With the first call to AxisChange the control automatically calculate the correct values for my data. Then I copy the relevant parameters from one scale to the other one and apply the change.
I've ran into a weird problem with getCharBoundaries, I could not figure out what coordinate space the coordinates returned from the function was in. What ever I tried I could not get it to match up with what I expected.
So I made a new project and and added simple code to highlight the last charater in a textfield, and all of a sudden it worked fine. I then tried to copy over the TextField that had been causing me problems, into the new project. And now the same weird offset appeared 50px on the x axis. Everything else was spot on.
So after some headscracthing comparing the two TextFields, I simply can not see a difference in their properties or transformation.
So I was hoping that someone might now what property might affect the coordinates returned by getCharBoundaries.
I am using Flash CS4.
I've just had exactly the same problem and thought I'd help out by offering what my findings are. With a help from this thread, I tried to find everything that wasn't 'default' about the textfield I was using. I found that when I had switched my TextFormatAlign (or 'align' in the IDE) and TextFieldAutoSize properties to 'LEFT' as opposed to 'CENTER', it solved the problem.
A little late in the game perhaps, but worth knowing for anyone running into the same problem. This was the only thread I could find that raised the right flag...
Well the getCharBoundaries returns the boundaries in the textfield coordinate system. Where the origin is topleft corner of the textfield.
getCharBoundaries does not take into consideration the scrolling. you need to check if there are scrollbars on its parent (textarea) and if so relocate. One quick way of doing it is using localtoglobal and globaltolocal. Use the first to translate from the textfield coordinate system to the application coordinate system and then use the second to translate from the app coordinate system to the coordinate system of the parent of the textfield which is the textarea. I'm fine tuning a my method to get char boundaries i will publish it today on my blog
http://flexbuzz.blogspot.com/
Works For Me(tm) (Flex Builder AS3 project):
[Embed(systemFont="Segoe UI", fontWeight="bold", fontName="emb",
mimeType="application/x-font")]
private var EmbeddedFont:Class;
public function ScratchAs3()
{
stage.scaleMode = 'noScale';
stage.align = 'tl';
var m:Matrix = new Matrix(.8, .1, -.1, 1.1, 26, 78);
var t:TextField = new TextField();
t.autoSize = 'left';
t.wordWrap = false;
t.embedFonts = true;
t.defaultTextFormat = new TextFormat("emb", 100, 0, true);
t.transform.matrix = m;
t.text = "TEST STRING.";
addChild(t);
var r:Rectangle = t.getCharBoundaries(8);
var tl:Point = m.transformPoint(r.topLeft);
var tr:Point = m.transformPoint(new Point(r.right, r.top));
var bl:Point = m.transformPoint(new Point(r.left, r.bottom));
var br:Point = m.transformPoint(r.bottomRight);
graphics.beginFill(0xFF, .6);
graphics.moveTo(tl.x, tl.y);
graphics.lineTo(tr.x, tr.y);
graphics.lineTo(br.x, br.y);
graphics.lineTo(bl.x, bl.y);
graphics.lineTo(tl.x, tl.y);
}
To literally answer your question, it returns the coordinates in the TextField's coordinate system, not it's parent, and it is affected by DisplayObject.transform.matrix, which is the backing for the .x, .y, .scaleX, .scaleY, .width, .height, and .rotation properties.
What ever it was the solution was simple to add a new TextField, never found out what property screwed everything up.
The first answer is correct in most cases. However if your field is parented to another movie clip it may still return the wrong y coordinate. try this code:
//if this doesn't work:
myTextFormat = new TextFormat();
myTextFormat.align = TextFormatAlign.LEFT;
myFieldsParent.myField.autoSize = TextFieldAutoSize.LEFT;
myFieldsParent.myField.setTextFormat( myTextFormat);
//try this:
var x = myFieldsParent.myField.getCharBoundaries(o).x;
var y = myFieldsParent.myField.getCharBoundaries(o).y;
var myPoint:Point = new Point(myField.getCharBoundaries(o).x,myField.getCharBoundaries(o).y);
var pt:Point = new Point(myFieldsParent.myField.getCharBoundaries(o).x, myFieldsParent.myField.getCharBoundaries(o).y);
pt = myFieldsParent.myField.localToGlobal(pt);
//pt is the variable containing the coordinates of the char in the stage's coordinate space. You may still need to offset it with a fixed value but it should be constant.
I didn't test this code as I have adapted this example from code that is embedded into my project so I apologize if I'm missing something...