ANDROID: Load view for activity asynchronously - java

I have a custom view class that I want to set for my activity. However it's initiation is very long (Most of the code, such as loading bitmaps is placed in constructor). Therefore, I want to show the user a preloader before setting the View.
I've tried setting the loading screen first and launching the view on a different thread using asynctask and handlers, though without any luck. The first gives me can't create handler inside thread that has not called looper.prepare() and the other one just executes code line by line.
Maybe I'm doing something wrong, any ideas how to divide the loading process, e.g.:
1. set loading screen.
2. load view class on the background (instantiate).
3. set the view class in place of the loading screen.
Thanks
UPDATE
public MainMenuView(Context context) {
super(context);
System.gc();
this.context = context;
sh = new SharedHelper(context);
am = context.getAssets();
sp = new SoundHelper(context,
SoundHelper.CLICK,
SoundHelper.MAIN_MELODY
);
screenWH = Helper.getScreenDimensions(context);
bg = new Background(screenWH, am);
scaleAmount = bg.scaleAmount();
scaleAmount = scaleAmount * 0.9f;
strings = Helper.getStrings((int)sh.getOption(Option.LANGUAGE), new DBHelper(context));
mainTitle = Helper.scaleBMP(Helper.assetImage(am, "Main/title.png"), scaleAmount);
mainTitleM.postTranslate(bg.getMargins()[0], -10);
/**
* Load cloud-like buttons
* */
float top = 0.03f * screenWH[1];
float left = bg.getMargins()[0] + mainTitle.getWidth();
MainButton repeat = new MainButton("Main/Buttons/repeat_on.png", "Main/Buttons/repeat.png", am, scaleAmount);
repeat.setPosition(left, top);
repeat.setType(Helper.REPEAT);
btns.add(repeat);
left += 0.3f * repeat.getBMP().getWidth();
top += repeat.getBMP().getHeight();
MainButton test = new MainButton("Main/Buttons/test_on.png", "Main/Buttons/test.png", am, scaleAmount);
test.setPosition(left, top);
test.setType(Helper.TEST);
btns.add(test);
left -= 0.7f * test.getBMP().getWidth();
top += 0.5f * test.getBMP().getHeight();
MainButton learn = new MainButton("Main/Buttons/learn_on.png", "Main/Buttons/learn.png", am, scaleAmount);
learn.setPosition(left, top);
learn.setType(Helper.LEARN);
btns.add(learn);
/**
* Main control buttons
* */
left = 5;
top = bg.getTopMargin();
buttonClass parent = new buttonClass("Buttons/parents control.png", am, scaleAmount, Helper.PARENTS);
parent.setPosition(left, top);
btns.add(parent);
top += parent.getBMP().getHeight() + 2;
buttonClass facebook = new buttonClass("Buttons/facebook.png", am, scaleAmount, Helper.FACEBOOK);
facebook.setPosition(left, top);
btns.add(facebook);
top += facebook.getBMP().getHeight() + 2;
buttonClass share = new buttonClass("Buttons/share.png", am, scaleAmount, Helper.SHARE);
share.setPosition(left, top);
btns.add(share);
top += share.getBMP().getHeight() + 2;
buttonClass pay = new buttonClass("Buttons/pay.png", am, scaleAmount, Helper.PAY);
pay.setPosition(left, top);
btns.add(pay);
/**
* Sprites
* */
float widthsix = 0;
float widthasix = 0;
float highest = 0;
for(int i=1; i<=11; i++){
mainSprite spr = new mainSprite(i, context, scaleAmount);
if(i <= 6){
widthsix += spr.getBMP().getWidth();
}
else{
widthasix += spr.getBMP().getWidth();
}
if(i == 9){
highest = spr.getBMP().getHeight();
}
sprites.add(spr);
}
float[] margins = bg.getMargins();
left = margins[0] + (margins[2] - margins[0] - widthsix) / 2;
float leftA = margins[0] + (margins[2] - margins[0] - widthasix) / 2;
top = screenWH[1] - highest;
float angle = 0;
float offset = 0.05f * screenWH[1];
float angle_step = 30.0f;
int i = 1;
float topA = 0;
for(mainSprite s : sprites){
float top_mod = top - s.getBMP().getHeight() + (float)Math.sin(Math.toRadians(angle)) * offset - offset;
s.setPosition(left, top_mod);
left += s.getBMP().getWidth();
angle += angle_step;
if(i == 3){
topA = screenWH[1];
}
if(i == 6){
top = topA;
left = leftA;
angle_step = 36f;
angle = 0;
}
i++;
}
sp.playSound(SoundHelper.MAIN_MELODY);
addDialog();
/**
* start animation
* */
currentSpr = 8;
sprites.get(currentSpr).start();
handle.postDelayed(runAnimation, 5000);
}
public void loadMain(){
screenWH = Helper.getScreenDimensions(context);
bg = new Background(screenWH, am);
scaleAmount = bg.scaleAmount();
scaleAmount = scaleAmount * 0.9f;
strings = Helper.getStrings((int)sh.getOption(Option.LANGUAGE), new DBHelper(context));
mainTitle = Helper.scaleBMP(Helper.assetImage(am, "Main/title.png"), scaleAmount);
mainTitleM.postTranslate(bg.getMargins()[0], -10);
/**
* Load cloud-like buttons
* */
float top = 0.03f * screenWH[1];
float left = bg.getMargins()[0] + mainTitle.getWidth();
MainButton repeat = new MainButton("Main/Buttons/repeat_on.png", "Main/Buttons/repeat.png", am, scaleAmount);
repeat.setPosition(left, top);
repeat.setType(Helper.REPEAT);
btns.add(repeat);
left += 0.3f * repeat.getBMP().getWidth();
top += repeat.getBMP().getHeight();
MainButton test = new MainButton("Main/Buttons/test_on.png", "Main/Buttons/test.png", am, scaleAmount);
test.setPosition(left, top);
test.setType(Helper.TEST);
btns.add(test);
left -= 0.7f * test.getBMP().getWidth();
top += 0.5f * test.getBMP().getHeight();
MainButton learn = new MainButton("Main/Buttons/learn_on.png", "Main/Buttons/learn.png", am, scaleAmount);
learn.setPosition(left, top);
learn.setType(Helper.LEARN);
btns.add(learn);
/**
* Main control buttons
* */
left = 5;
top = bg.getTopMargin();
buttonClass parent = new buttonClass("Buttons/parents control.png", am, scaleAmount, Helper.PARENTS);
parent.setPosition(left, top);
btns.add(parent);
top += parent.getBMP().getHeight() + 2;
buttonClass facebook = new buttonClass("Buttons/facebook.png", am, scaleAmount, Helper.FACEBOOK);
facebook.setPosition(left, top);
btns.add(facebook);
top += facebook.getBMP().getHeight() + 2;
buttonClass share = new buttonClass("Buttons/share.png", am, scaleAmount, Helper.SHARE);
share.setPosition(left, top);
btns.add(share);
top += share.getBMP().getHeight() + 2;
buttonClass pay = new buttonClass("Buttons/pay.png", am, scaleAmount, Helper.PAY);
pay.setPosition(left, top);
btns.add(pay);
/**
* Sprites
* */
float widthsix = 0;
float widthasix = 0;
float highest = 0;
for(int i=1; i<=11; i++){
mainSprite spr = new mainSprite(i, context, scaleAmount);
if(i <= 6){
widthsix += spr.getBMP().getWidth();
}
else{
widthasix += spr.getBMP().getWidth();
}
if(i == 9){
highest = spr.getBMP().getHeight();
}
sprites.add(spr);
}
float[] margins = bg.getMargins();
left = margins[0] + (margins[2] - margins[0] - widthsix) / 2;
float leftA = margins[0] + (margins[2] - margins[0] - widthasix) / 2;
top = screenWH[1] - highest;
float angle = 0;
float offset = 0.05f * screenWH[1];
float angle_step = 30.0f;
int i = 1;
float topA = 0;
for(mainSprite s : sprites){
float top_mod = top - s.getBMP().getHeight() + (float)Math.sin(Math.toRadians(angle)) * offset - offset;
s.setPosition(left, top_mod);
left += s.getBMP().getWidth();
angle += angle_step;
if(i == 3){
topA = screenWH[1];
}
if(i == 6){
top = topA;
left = leftA;
angle_step = 36f;
angle = 0;
}
i++;
}
sp.playSound(SoundHelper.MAIN_MELODY);
addDialog();
/**
* start animation
* */
currentSpr = 8;
sprites.get(currentSpr).start();
//handle.postDelayed(runAnimation, 5000);
}
I'm loading the view in the onCreate method of Activity
new Handler().post(new Runnable(){
#Override
public void run(){
setContentView(new CustomView(context));
}
});

Move the code that loads all the bitmaps and stuff inside the AsyncTask instead of doing it in the constructor. Do this from the onCreate activity method.
You can then use the onProgressUpdate callback of the AsyncTask to inform of progress (e.g. a loading progress bar). For example, you can use ProgresBar.setMax to set it to the number of items to load, pass that to the progress callback as you load items via publishProgress, then call ProgressBar.setProgress with that value.
As you load the items, you can add these to an Assets class. This can be passed back via the onPostExecute callback method which you can then assign to a member variable in your activity.
You can then either have the layout of the activity look like:
LinearLayout
RelativeLayout [id=load_screen, background=loading_bg]
ProgressBar [id=loading_progress]
RelativeLayout [id=main, visibility=hidden]
...
then call findViewById(R.id.load_screen).setVisible(View.GONE) to hide the load screen and findViewById(R.id.load_screen).setVisible(View.VISIBLE)
Alternatively, you can load your replacement view (via the LayoutInflater.inflate call) and then call setContentView on it. When the inflate call returns, the view is fully loaded, so you can do all the handler setup here.
The layout loading or visibility switch will also happen in the onPostExecute method of the AsyncTask.

Related

Android chart with variable line stroke width

I am trying to create Android App with chart with variable line stroke width like this one in the link.
I have tried to realize it with MPAndroidChart. Override drawCubicBezier method in LineChartRenderer class and to set dynamically setStrokeWidth on Paint. But nothing happens. Probably my knowledge is not enough to do it.
Can you help me how to achieve a different line thickness or if there is another chart library that has such functionality?
This is how I set my new Renderer
chart2.setRenderer(new myLineChartRenderer(chart2, chart2.getAnimator(), chart2.getViewPortHandler()));
This is the class
public class myLineChartRenderer extends LineChartRenderer {
public myLineChartRenderer(LineChart chart, ChartAnimator animator, ViewPortHandler viewPortHandler) {
super(chart, animator, viewPortHandler);
}
#Override
protected void drawCubicBezier(ILineDataSet dataSet) {
Path mPath = new Path();
ArrayList<Path> mPaths = new ArrayList<Path>();
mRenderPaint.setAntiAlias(true);
mRenderPaint.setDither(true);
mRenderPaint.setStyle(Paint.Style.STROKE);
mRenderPaint.setStrokeJoin(Paint.Join.ROUND);
mRenderPaint.setStrokeCap(Paint.Cap.ROUND);
//mRenderPaint.setStrokeWidth(STROKE_WIDTH);
float phaseY = mAnimator.getPhaseY();
Transformer trans = mChart.getTransformer(dataSet.getAxisDependency());
mXBounds.set(mChart, dataSet);
float intensity = dataSet.getCubicIntensity();
cubicPath.reset();
if (mXBounds.range >= 1) {
float prevDx = 0f;
float prevDy = 0f;
float curDx = 0f;
float curDy = 0f;
// Take an extra point from the left, and an extra from the right.
// That's because we need 4 points for a cubic bezier (cubic=4), otherwise we get lines moving and doing weird stuff on the edges of the chart.
// So in the starting `prev` and `cur`, go -2, -1
// And in the `lastIndex`, add +1
final int firstIndex = mXBounds.min + 1;
final int lastIndex = mXBounds.min + mXBounds.range;
Entry prevPrev;
Entry prev = dataSet.getEntryForIndex(Math.max(firstIndex - 2, 0));
Entry cur = dataSet.getEntryForIndex(Math.max(firstIndex - 1, 0));
Entry next = cur;
int nextIndex = -1;
if (cur == null) return;
// let the spline start
cubicPath.moveTo(cur.getX(), cur.getY() * phaseY);
mPath.moveTo(cur.getX(), cur.getY() * phaseY);
for (int j = mXBounds.min + 1; j <= mXBounds.range + mXBounds.min; j++) {
prevPrev = prev;
prev = cur;
cur = nextIndex == j ? next : dataSet.getEntryForIndex(j);
nextIndex = j + 1 < dataSet.getEntryCount() ? j + 1 : j;
next = dataSet.getEntryForIndex(nextIndex);
prevDx = (cur.getX() - prevPrev.getX()) * intensity;
prevDy = (cur.getY() - prevPrev.getY()) * intensity;
curDx = (next.getX() - prev.getX()) * intensity;
curDy = (next.getY() - prev.getY()) * intensity;
cubicPath.cubicTo(prev.getX() + prevDx, (prev.getY() + prevDy) * phaseY,
cur.getX() - curDx,
(cur.getY() - curDy) * phaseY, cur.getX(), cur.getY() * phaseY);
mPath.cubicTo(prev.getX() + prevDx, (prev.getY() + prevDy) * phaseY,
cur.getX() - curDx,
(cur.getY() - curDy) * phaseY, cur.getX(), cur.getY() * phaseY);
mPaths.add(mPath);
// Log.i("Curve", (prev.getX() + prevDx)+" | "+ ((prev.getY() + prevDy) * phaseY)+" | "+ (cur.getX() - curDx)+" | "+ ((cur.getY() - curDy) * phaseY)+" | "+ cur.getX()+" | "+ (cur.getY() * phaseY));
}
}
// if filled is enabled, close the path
if (dataSet.isDrawFilledEnabled()) {
cubicFillPath.reset();
cubicFillPath.addPath(cubicPath);
//drawCubicFill(mBitmapCanvas, dataSet, cubicFillPath, trans, mXBounds);
}
mRenderPaint.setColor(dataSet.getColor());
mRenderPaint.setStyle(Paint.Style.STROKE);
trans.pathValueToPixel(cubicPath);
//mBitmapCanvas.drawPath(cubicPath, mRenderPaint);
for(int i=0; i<mPaths.size();i++)
{
mRenderPaint.setStrokeWidth(i+30);
mBitmapCanvas.drawPath(mPaths.get(i), mRenderPaint);
}
mRenderPaint.setPathEffect(null);
}
}
I tried to divide the path into separate parts and change the thickness of the line while drawing them, but the result is totally wrong.

custom seekbar with multi color

I am trying to create custom seek bar in android with multicolor. I tried below code
customseekbar.java
int proBarWidth = getWidth();
int proBarHeight = getHeight();
int thumboffset = getThumbOffset();
int lastproX = 0;
int proItemWidth, proItemRight;
for (int i = 0; i < mproItemsList.size(); i++) {
proItem proItem = mproItemsList.get(i);
Paint proPaint = new Paint();
proPaint.setColor(getResources().getColor(proItem.color));
proItemWidth = (int) (proItem.proItemPercentage
* proBarWidth / 100);
proItemRight = lastproX + proItemWidth;
// for last item give right of the pro item to width of the
// pro bar
if (i == mproItemsList.size() - 1
&& proItemRight != proBarWidth) {
proItemRight = proBarWidth;
}
Rect proRect = new Rect();
proRect.set(lastproX, thumboffset / 2, proItemRight,
proBarHeight - thumboffset / 2);
canvas.drawRect(proRect, proPaint);
lastproX = proItemRight;
}
super.onDraw(canvas);
view
<mypackage.customseekbar
android:id="#+id/customseekbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:max="100"
android:progress="0"
android:progressDrawable="#android:color/transparent"
android:thumb="#drawable/seek_thumb_normal"
android:thumbOffset="12dp" />
I used this method in MainMenuActivity. It gives me result something like below. I referred this link https://azzits.wordpress.com/2013/11/17/customseekbar/
But I am expecting something like below
Is there any way to draw this vertical gapped lines? How can I draw this vertical lines?
Progress bar with divider is a good place to look as mentioned by #Nilesh Rathod. Rather than using canvas.drawRect() you can use canvas.drawRoundRect(); Short example:
for (int i = 0; i < NUM_SEGMENTS; i++) {
float loLevel = i / (float) NUM_SEGMENTS;
float hiLevel = (i + 1) / (float) NUM_SEGMENTS;
if (loLevel <= level && level <= hiLevel) {
float middle = mSegment.left + NUM_SEGMENTS * segmentWidth * (level - loLevel);
canvas.drawRoundRect(mSegment.left, mSegment.top, middle, mSegment.bottom, mPaint);
mPaint.setColor(mBackground);
canvas.drawRoundRect(middle, mSegment.top, mSegment.right, mSegment.bottom, mPaint);
} else {
canvas.drawRoundRect(mSegment, mPaint);
}
mSegment.offset(mSegment.width() + gapWidth, 0);
}
I give full credit to the code above to the creator of the aforementioned link and don't claim any of it as mine as I am just illustrating the change that should be made to reach the desired effect. Please let me know if you run into further issues.

how to Smooth catmull rom spline curve when it is moving like amaging wire in libgdx

i have tried to move my curve but it is not moving well, when it changes its direction from left to right or right to left then the movement is quite awkward.
i want to move my curve like this video
video of curve movement what i actually want.
In this video when a it change its direction it is so graceful but in my case it change its direction and the curve gives a crazy shape at newly added point.
Experts please solve this problem.
here is my code
//create paths
private Bezier<Vector2> path1;
private CatmullRomSpline<Vector2> path2;
private ShapeRenderer sr;
int height,width;
Vector2 starting,ending,endingControl;
ArrayList<Vector2> listOfPoints;
Vector3 touchPos;
float timeDifference;
Boolean leftPos=false,rightPos=false;
Boolean isTouch=false,isTouchUp=false;
Vector2 mVector2;
private OrthographicCamera cam;
Vector2[] controlPoints;
#Override
public void create () {
width = Gdx.graphics.getWidth();
height = Gdx.graphics.getHeight();
ending=new Vector2(width/2,height/2);
endingControl=new Vector2(ending.x,ending.y+10);
starting=new Vector2(width/2,0);
controlPoints = new Vector2[]{starting,starting,ending,ending};
// set up the curves
path2 = new CatmullRomSpline<Vector2>(controlPoints, false);
listOfPoints=new ArrayList<Vector2>();
// setup ShapeRenderer
sr = new ShapeRenderer();
sr.setAutoShapeType(true);
sr.setColor(Color.BLACK);
cam=new OrthographicCamera();
cam.setToOrtho(false);
listOfPoints.add(new Vector2(width/2,0)); //starting
listOfPoints.add(new Vector2(width/2,0)); //starting
}
#Override
public void resize(int width, int height) {
// TODO Auto-generated method stub
super.resize(width, height);
cam.update();
}
#Override
public void render () {
cam.update();
Gdx.gl.glClearColor(1f, 1f, 1f, 1f);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
sr.begin();
sr.set(ShapeType.Filled);
if(Gdx.input.isTouched())
{
if(!isTouch){
listOfPoints.add(new Vector2(ending.x+2, ending.y-4));
int s=listOfPoints.size();
controlPoints=new Vector2[s+2];
listOfPoints.toArray(controlPoints);
controlPoints[s]=ending;
//endingControl.x=ending.y;
controlPoints[s+1]=ending;
path2 = new CatmullRomSpline<Vector2>(controlPoints, false);
}
isTouch=true;
ending.x+=3;
}
else {
if(isTouch){
listOfPoints.add(new Vector2(ending.x-2, ending.y-4));
int s=listOfPoints.size();
controlPoints=new Vector2[s+2];
listOfPoints.toArray(controlPoints);
controlPoints[s]=ending;
controlPoints[s+1]=ending;
path2 = new CatmullRomSpline<Vector2>(controlPoints, false);
}
isTouch=false;
ending.x-=3;
}
moveAndReduce();
for(int i = 0; i < 100; ++i){
float t = i /100f;
Vector2 st = new Vector2();
Vector2 end = new Vector2();
path2.valueAt(st,t);
path2.valueAt(end, t-0.01f);
sr.rectLine(st.x, st.y, end.x, end.y,3);
}
sr.end();
}
#Override
public void dispose () {
sr.dispose();
}
public void moveAndReduce()
{
for(Vector2 vector2:listOfPoints)
{
vector2.y-=3 ;
}
if(listOfPoints.size()>3 && listOfPoints.get(3).y<-1)
{
listOfPoints.remove(0);
listOfPoints.set(0, listOfPoints.get(1));
int s=listOfPoints.size();
controlPoints=new Vector2[s+2];
listOfPoints.toArray(controlPoints);
controlPoints[s]=ending;
controlPoints[s+1]=ending;
path2 = new CatmullRomSpline<Vector2>(controlPoints, false);
}
}
Going by the video the curve does not look like it is constrained by control points but just a simple trace of and accelerating point.
You create an array of floats the length in pixels matching the length of the line in the x direction. For example if screen is 200 pixels wide the line can be 100 so the array is 100 in length. Set each float in the array to the start value half the screen height. I call the array line in this answer. You call it what you like.
The you assign a head index that is the index of the rightmost point. Each frame you move the head index up by one. If it is over the array length-1 you set it to zero (beginning of array)
Rendering the Path
When you draw the line you draw all the points from head + 1
Path p = new Path();
for(int i = 0; i < 100; ++i){
p.lineTo(i, line[(i + head + 1) % 100]); // add path points
}
// draw the path;
Moving up and down
To make it move you have a movement float move that is 0 for no movement or positive and negative values the move up or down.
When you want it to move increase the move amount by a fixed value.
// moving down
if(move < maxMove){ // set a max move amount eg 10
move += moveAmount; // moveAmount 0.2 just as an example
}
Same for moving up, but subtract
When there is no input you move the move amount back to zero by a fixed rate
// assume this is code run when no input
if(move != 0){
if(Math.abs(move) < moveAmount){ // if close to zero set to zero
move = 0;
}else{
move -= Math.sign(move) * moveAmount; // else move towards zero at
// fixed rate
}
}
Moving forward
The line does not move forward, just appears to do so as we move the head position up the array each frame.
Back to moving the line's head the following move the line head position up or down (but is not complete the last line is modified to create a smoother curve)
float pos = line[head]; // get the pos of line at head
head += 1; // move the head forward 1
head %= 100; // if past end of array move to 0
line[head] = pos + move; // set the new head position
A better curve
This will move the head of the line up or down depending on move. The curve we get is not that nice so to make it a little smoother you need to change the rate the move value changes the head position.
// an sCurve for any value of move the result is from -1 to 1
// the greater or smaller move the closer to 1 or -1 the value gets
// the value -1.2 controls the rate at which the value moves to 1 or -1
// the closer to -1 the value is the slower the value moves to 1 or -1
float res = (2 / (1 + Math.pow(move,-1.2))) -1;
This in effect changes the shape of the lines curve to a almost sine wave when moving up and down
// so instead of
//line[head] = pos + move; // set the new head position
line[head] = pos + ( (2 / (1 + Math.pow(move,-1.2))) -1 ) * maxSpeed;
// max speed is the max speed the line head can move up or down
// per frame in pixels.
Example to show the curve
Below is a Javascript implementation that does it as outlined above (is not intended as answer code). Use the keyboard Arrow Up and Arrow down to move the line
If you are using a tablet or phone then the following image is what you will see as way to late for me to add and test touch for the example
const doFor = (count, callback) => {var i = 0; while (i < count) { callback(i ++) } };
const keys = {
ArrowUp : false,
ArrowDown : false,
};
function keyEvents(e){
if(keys[e.code] !== undefined){
keys[e.code] = event.type === "keydown";
e.preventDefault();
}
}
addEventListener("keyup", keyEvents);
addEventListener("keydown", keyEvents);
focus();
var gameOver = 0;
var gameOverWait = 100;
var score = 0;
var nextWallIn = 500
var nextWallCount = nextWallIn;
var wallHole = 50;
const wallWidth = 5;
const walls = [];
function addWall(){
var y = (Math.random() * (H - wallHole * 2)) + wallHole *0.5;
walls.push({
x : W,
top : y,
bottom : y + wallHole,
point : 1, // score point
});
}
function updateWalls(){
nextWallCount += 1;
if(nextWallCount >= nextWallIn){
addWall();
nextWallCount = 0;
nextWallIn -= 1;
wallHole -= 1;
}
for(var i = 0; i < walls.length; i ++){
var w = walls[i];
w.x -= 1;
if(w.x < -wallWidth){
walls.splice(i--,1);
}
if(w.x >= line.length- + wallWidth && w.x < line.length){
var pos = line[head];
if(pos < w.top || pos > w.bottom){
gameOver = gameOverWait;
}
}
if(w.point > 0 && w.x <= line.length){
score += w.point;
w.point = 0;
}
}
}
function drawWalls(){
for(var i = 0; i < walls.length; i ++){
var w = walls[i];
ctx.fillStyle = "red";
ctx.fillRect(w.x,0,wallWidth,w.top);
ctx.fillRect(w.x,w.bottom,wallWidth,H-w.bottom);
}
}
const sCurve = (x,p) => (2 / (1 + Math.pow(p,-x))) -1;
const ctx = canvas.getContext("2d");
var W,H; // canvas width and height
const line = [];
var move = 0;
var curvePower = 1.2;
var curveSpeed = 0.2;
var maxSpeed = 10;
var headMoveMultiply = 2;
var head;
function init(){
line.length = 0;
doFor(W / 2,i => line[i] = H / 2);
head = line.length - 1;
move = 0;
walls.length = 0;
score = 0;
nextWallIn = 500
nextWallCount = nextWallIn;
wallHole = 50;
ctx.font = "30px arial black";
}
function stepLine(){
var pos = line[head];
head += 1;
head %= line.length;
line[head] = pos + sCurve(move,curvePower)*headMoveMultiply ;
}
function drawLine(){
ctx.beginPath();
ctx.strokeStyle = "black";
ctx.lineWidth = 3;
ctx.lineJoin = "round";
ctx.lineCap = "round";
for(var i = 0; i <line.length; i++){
ctx.lineTo(i,line[(i + head + 1) % line.length]);
}
ctx.stroke();
}
function mainLoop(time){
if(canvas.width !== innerWidth || canvas.height !== innerHeight){
W = canvas.width = innerWidth;
H = canvas.height = innerHeight;
init();
}
if(gameOver === 1){
gameOver = 0;
init();
}
ctx.setTransform(1,0,0,1,0,0);
ctx.clearRect(0,0,W,H);
if(keys.ArrowUp){
if(move > - maxSpeed){
move -= curveSpeed;
}
}else if(keys.ArrowDown){
if(move < maxSpeed){
move += curveSpeed;
}
}else{
move -= Math.sign(move)*curveSpeed;
if(Math.abs(move) < curveSpeed){
move = 0;
}
}
if(gameOver === 0){
stepLine();
updateWalls();
}
drawLine();
drawWalls();
ctx.fillStyle = "Black";
ctx.textAlign = "left";
ctx.fillText("Score : " + score, 10,30);
if(gameOver > 0){
ctx.textAlign = "center";
ctx.fillText("Crashed !!", W / 2,H * 0.4);
gameOver -= 1;
}
requestAnimationFrame(mainLoop);
}
requestAnimationFrame(mainLoop);
canvas {
position : absolute;
top : 0px;
left : 0px;
z-index : -10;
}
<br><br><br>Up down arrow keys to move line.
<canvas id=canvas></canvas>

How to fit small cubes into a given volume and represent it graphically on a web page?

My query is to show programmatically, the fitting of many given non regular (but rectangular) cubes (i.e. boxes) of individually different sizes, inside a larger volume cube, such as a storage unit.
The mathematics part is understood. Like in Linear programming / linear algebra, we can add fit volume of all smaller cubes to find out the best fit for the volume of the larger cube.
The actual requirement is to show or allow this fitting graphically on a web-page, preferably in 3d. If possible, to allow user to interact with the fitting, i.e. shuffling the placement of the cubes, etc.
Also, since I am a Java developer by profession, Java or related languages / frameworks would be my choice. However, I can use any other technology / framework / language if the end results are met.
NB: Weight is also a concern (parameter). There is a maximum weight which can be stacked in any given storage unit.
Also, since storage units can be accessed without permission (by thieves), cost of the cubes stacked in one unit is also limited. The user may desire to fit cubes of higher cost in one unit which has higher security and vice versa.
Example: allow fitting many rectangular boxes containing household electronics in a given room. The boxes maybe of TVs, refrigerators, washing machines, dishwashers, playstations, xbox 360s, etc. The different dimensions of these boxes, is to give you an idea of what to expect while fitting to the limited volume.
If there is any FOSS library / project (or even non FOSS library or project) for the same, a pointer towards it would be welcome.
Disclaimer: Okay, I know it does not 100% answer your question and also the code it veeery old (as can be concluded from the old-fashioned CVS comments) and today I would not write it that way anymore. It does still run on Java 8, though, I tested it. But in addition to solving the little informatics challenge problem of water flowing through a 3D matrix of cuboids from top to bottom depending how "leaky" the matrix (symbolising some kind of Swiss cheese) is, it also uses some very simple 3D visualisation via Java 3D. Thus, you need to install Java 3D and put the corresponding libraries onto your classpath.
The 3D output looks something like this:
package vhs.bwinfo.cheese;
// $Id: Cuboid.java,v 1.1.2.1 2006/01/10 19:48:41 Robin Exp $
import javax.media.j3d.Appearance;
import javax.media.j3d.QuadArray;
import javax.media.j3d.Shape3D;
import javax.vecmath.Point3f;
import javax.vecmath.TexCoord2f;
import javax.vecmath.Vector3f;
public class Cuboid extends Shape3D {
private static final float POS = +0.5f;
private static final float NEG = -0.5f;
private static final Point3f[] POINTS = new Point3f[] {
new Point3f(NEG, NEG, NEG),
new Point3f(POS, NEG, NEG),
new Point3f(POS, NEG, POS),
new Point3f(NEG, NEG, POS),
new Point3f(NEG, POS, NEG),
new Point3f(POS, POS, NEG),
new Point3f(POS, POS, POS),
new Point3f(NEG, POS, POS)
};
private static final TexCoord2f[] TEX_COORDS = new TexCoord2f[] {
new TexCoord2f(0, 1),
new TexCoord2f(1, 1),
new TexCoord2f(1, 0),
new TexCoord2f(0, 0)
};
private static final int VERTEX_FORMAT =
QuadArray.COORDINATES |
QuadArray.NORMALS |
QuadArray.TEXTURE_COORDINATE_2;
public Cuboid(float scaleX, float scaleY, float scaleZ) {
Point3f[] points = new Point3f[8];
for (int i = 0; i < 8; i++)
points[i] = new Point3f(
POINTS[i].x * scaleX,
POINTS[i].y * scaleY,
POINTS[i].z * scaleZ
);
Point3f[] vertices = {
points[3], points[2], points[1], points[0], // bottom
points[4], points[5], points[6], points[7], // top
points[7], points[3], points[0], points[4], // left
points[6], points[5], points[1], points[2], // right
points[7], points[6], points[2], points[3], // front
points[5], points[4], points[0], points[1] // back
};
QuadArray geometry = new QuadArray(24, VERTEX_FORMAT);
geometry.setCoordinates(0, vertices);
for (int i = 0; i < 24; i++)
geometry.setTextureCoordinate(0, i, TEX_COORDS[i % 4]);
Vector3f normal = new Vector3f();
Vector3f v1 = new Vector3f();
Vector3f v2 = new Vector3f();
Point3f[] pts = new Point3f[4];
for (int i = 0; i < 4; i++)
pts[i] = new Point3f();
for (int face = 0; face < 6; face++) {
geometry.getCoordinates(face * 4, pts);
v1.sub(pts[0], pts[2]);
v2.sub(pts[1], pts[3]);
normal.cross(v1, v2);
normal.normalize();
for (int i = 0; i < 4; i++)
geometry.setNormal((face * 4 + i), normal);
}
setGeometry(geometry);
setAppearance(new Appearance());
}
public Cuboid(float scaleFactor) {
this(scaleFactor, scaleFactor, scaleFactor);
}
}
package vhs.bwinfo.cheese;
// $Id: LeakyCheese.java,v 1.2.2.2 2006/01/10 15:37:14 Robin Exp $
import com.sun.j3d.utils.applet.JMainFrame;
import javax.swing.*;
import java.util.Random;
import static java.lang.System.out;
public class LeakyCheese {
private int width = 20, height = 20, depth = 20;
private int numClasses = 100, samplesPerClass = 100;
private double pMin = 0, pMax = 1;
private double pDiff = pMax - pMin;
private double classSize = pDiff / numClasses;
private int[] stats;
enum CubeState {CHEESE, AIR, WATER}
final private CubeState[][][] cheese;
private static final Random RND = new Random();
public LeakyCheese(
int width, int height, int depth,
int numClasses, int samplesPerClass,
double pMin, double pMax
) {
this.width = width;
this.height = height;
this.depth = depth;
this.numClasses = numClasses;
this.samplesPerClass = samplesPerClass;
this.pMin = pMin;
this.pMax = pMax;
pDiff = pMax - pMin;
classSize = pDiff / numClasses;
cheese = new CubeState[width][height][depth];
}
public LeakyCheese(
int width, int height, int depth,
int numClasses, int samplesPerClass
) {
this(width, height, depth, numClasses, samplesPerClass, 0, 1);
}
public LeakyCheese() {
cheese = new CubeState[width][height][depth];
}
private boolean pourWater(int x, int y, int z) {
if (x < 0 || x >= width || y < 0 || y >= height || z < 0 || z >= depth)
return false;
if (cheese[x][y][z] != CubeState.AIR)
return false;
cheese[x][y][z] = CubeState.WATER;
boolean retVal = (y == 0);
retVal = pourWater(x + 1, y, z) || retVal;
retVal = pourWater(x - 1, y, z) || retVal;
retVal = pourWater(x, y + 1, z) || retVal;
retVal = pourWater(x, y - 1, z) || retVal;
retVal = pourWater(x, y, z + 1) || retVal;
retVal = pourWater(x, y, z - 1) || retVal;
return retVal;
}
private boolean isLeaky(double p) {
for (int x = 0; x < width; x++)
for (int y = 0; y < height; y++)
for (int z = 0; z < depth; z++)
cheese[x][y][z] = (RND.nextDouble() < p)
? CubeState.CHEESE
: CubeState.AIR;
boolean retVal = false;
for (int x = 0; x < width; x++)
for (int z = 0; z < depth; z++)
retVal = pourWater(x, height - 1, z) || retVal;
return retVal;
}
private void generateStats() {
if (stats != null)
return;
stats = new int[numClasses];
for (int i = 0; i < numClasses; i++) {
for (int j = 0; j < samplesPerClass; j++) {
double p = pMin + classSize * (RND.nextDouble() + i);
if (isLeaky(p))
stats[i]++;
}
}
}
public void printStats() {
generateStats();
out.println(
"p (cheese) | p (leaky)\n" +
"------------------+-----------"
);
for (int i = 0; i < numClasses; i++) {
out.println(
String.format(
"%1.5f..%1.5f | %1.5f",
pMin + classSize * i,
pMin + classSize * (i + 1),
(double) stats[i] / samplesPerClass
)
);
}
}
public static void main(String[] args) {
//new LeakyCheese().printStats();
//new LeakyCheese(40, 40, 40, 50, 100, 0.66, .71).printStats();
LeakyCheese cheeseBlock = new LeakyCheese(5, 20, 5, 20, 100);
//LeakyCheese cheeseBlock = new LeakyCheese(20, 20, 20, 20, 100);
while (!cheeseBlock.isLeaky(0.65))
;
out.println("*** required solution found - now rendering... ***");
JMainFrame f = new JMainFrame(new LeakyCheeseGUI(cheeseBlock.cheese), 512, 512);
f.setLocationRelativeTo(null);
f.setExtendedState(JFrame.MAXIMIZED_BOTH);
}
}
package vhs.bwinfo.cheese;
// $Id: LeakyCheeseGUI.java,v 1.1.2.1 2006/01/10 15:25:18 Robin Exp $
import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.universe.SimpleUniverse;
import vhs.bwinfo.cheese.LeakyCheese.CubeState;
import javax.media.j3d.*;
import javax.vecmath.Point3d;
import javax.vecmath.Vector3f;
import java.applet.Applet;
import java.awt.*;
import java.util.Random;
public class LeakyCheeseGUI extends Applet {
static final long serialVersionUID = -8194627556699837928L;
public BranchGroup createSceneGraph(CubeState[][][] cheese) {
// Create the root of the branch graph
BranchGroup bgRoot = new BranchGroup();
// Composite of two rotations around different axes. The resulting
// TransformGroup is the parent of all our cheese cubes, because their
// orientation is identical. They only differ in their translation
// values and colours.
Transform3D tRotate = new Transform3D();
Transform3D tRotateTemp = new Transform3D();
tRotate.rotX(Math.PI / 8.0d);
tRotateTemp.rotY(Math.PI / -4.0d);
tRotate.mul(tRotateTemp);
TransformGroup tgRotate = new TransformGroup(tRotate);
bgRoot.addChild(tgRotate);
// Bounding sphere for rendering
BoundingSphere bounds =
new BoundingSphere(new Point3d(0.0, 0.0, 0.0), 100.0);
// Set background colour
// Note: Using Canvas3D.setBackground does not work, because it is an
// AWT method. Java 3D, though, gets its background colour from its
// background node (black, if not present).
Background background = new Background(0.5f, 0.5f, 0.5f);
background.setApplicationBounds(bounds);
bgRoot.addChild(background);
TransparencyAttributes transpAttr;
// Little cheese cubes
Appearance cheeseAppearance = new Appearance();
transpAttr =
new TransparencyAttributes(TransparencyAttributes.NICEST, 0.98f);
cheeseAppearance.setTransparencyAttributes(transpAttr);
cheeseAppearance.setColoringAttributes(
new ColoringAttributes(1, 1, 0, ColoringAttributes.NICEST));
PolygonAttributes pa = new PolygonAttributes();
//pa.setPolygonMode(PolygonAttributes.POLYGON_LINE);
pa.setCullFace(PolygonAttributes.CULL_NONE);
cheeseAppearance.setPolygonAttributes(pa);
// Little water cubes
Appearance waterAppearance = new Appearance();
transpAttr =
new TransparencyAttributes(TransparencyAttributes.NICEST, 0.85f);
waterAppearance.setTransparencyAttributes(transpAttr);
waterAppearance.setColoringAttributes(
new ColoringAttributes(0, 0, 1, ColoringAttributes.NICEST));
pa = new PolygonAttributes();
pa.setCullFace(PolygonAttributes.CULL_NONE);
waterAppearance.setPolygonAttributes(pa);
// Little air cubes
Appearance airAppearance = new Appearance();
transpAttr =
new TransparencyAttributes(TransparencyAttributes.NICEST, 0.95f);
airAppearance.setTransparencyAttributes(transpAttr);
airAppearance.setColoringAttributes(
new ColoringAttributes(1, 1, 1, ColoringAttributes.NICEST));
pa = new PolygonAttributes();
//pa.setPolygonMode(PolygonAttributes.POLYGON_LINE);
pa.setCullFace(PolygonAttributes.CULL_NONE);
airAppearance.setPolygonAttributes(pa);
// Water-coloured (i.e. blue) wire frame around cheese block, if leaky
Appearance waterWireFrameAppearance = new Appearance();
waterWireFrameAppearance.setColoringAttributes(
new ColoringAttributes(0, 0, 1, ColoringAttributes.NICEST));
pa = new PolygonAttributes();
pa.setPolygonMode(PolygonAttributes.POLYGON_LINE);
pa.setCullFace(PolygonAttributes.CULL_NONE);
waterWireFrameAppearance.setPolygonAttributes(pa);
// Cheese-coloured (i.e. yellow) wire frame around cheese block, if not leaky
Appearance cheeseWireFrameAppearance = new Appearance();
cheeseWireFrameAppearance.setColoringAttributes(
new ColoringAttributes(1, 1, 0, ColoringAttributes.NICEST));
pa = new PolygonAttributes();
pa.setPolygonMode(PolygonAttributes.POLYGON_LINE);
pa.setCullFace(PolygonAttributes.CULL_NONE);
cheeseWireFrameAppearance.setPolygonAttributes(pa);
// Absolute offsets for the cheese block to fit into the viewing canvas
final float xOffs = -0.8f;
final float yOffs = -0.55f;
final float zOffs = 0;
// Create all those little cubes ;-)
final int xSize = cheese.length;
final int ySize = cheese[0].length;
final int zSize = cheese[0][0].length;
final int maxSize = Math.max(xSize, Math.max(ySize, zSize));
final float xCenterOffs = 0.5f * (maxSize - xSize) / maxSize;
final float yCenterOffs = 0.5f * (maxSize - ySize) / maxSize;
final float zCenterOffs = -0.5f * (maxSize - zSize) / maxSize;
boolean isLeaky = false;
for (int x = 0; x < xSize; x++)
for (int y = 0; y < ySize; y++)
for (int z = 0; z < zSize; z++) {
Transform3D tTranslate = new Transform3D();
tTranslate.setTranslation(
new Vector3f(
xOffs + xCenterOffs + 1.0f * x / maxSize,
yOffs + yCenterOffs + 1.0f * y / maxSize,
zOffs + zCenterOffs - 1.0f * z / maxSize
)
);
TransformGroup tgTranslate = new TransformGroup(tTranslate);
tgRotate.addChild(tgTranslate);
Cuboid cube = new Cuboid(1.0f / maxSize);
switch (cheese[x][y][z]) {
case CHEESE:
cube.setAppearance(cheeseAppearance);
break;
case WATER:
cube.setAppearance(waterAppearance);
if (y == 0)
isLeaky = true;
break;
case AIR:
cube.setAppearance(airAppearance);
}
tgTranslate.addChild(cube);
}
// If cheese block is leaky, visualise it by drawing a water-coloured
// (i.e. blue) wire frame around it. Otherwise use a cheese-coloured
// (i.e. yellow) one.
Transform3D tTranslate = new Transform3D();
tTranslate.setTranslation(
new Vector3f(
xOffs + xCenterOffs + 0.5f * (xSize - 1) / maxSize,
yOffs + yCenterOffs + 0.5f * (ySize - 1) / maxSize,
zOffs + zCenterOffs - 0.5f * (zSize - 1) / maxSize
)
);
TransformGroup tgTranslate = new TransformGroup(tTranslate);
tgRotate.addChild(tgTranslate);
Cuboid cuboid = new Cuboid(
1.0f * xSize / maxSize,
1.0f * ySize / maxSize,
1.0f * zSize / maxSize
);
cuboid.setAppearance(isLeaky ? waterWireFrameAppearance : cheeseWireFrameAppearance);
tgTranslate.addChild(cuboid);
// Let Java 3D perform optimizations on this scene graph.
bgRoot.compile();
return bgRoot;
}
public LeakyCheeseGUI(CubeState[][][] cheese) {
// Create a simple scene and attach it to the virtual universe
GraphicsConfiguration graphCfg = SimpleUniverse.getPreferredConfiguration();
Canvas3D canvas = new Canvas3D(graphCfg);
setLayout(new BorderLayout());
add(canvas, "Center");
SimpleUniverse universe = new SimpleUniverse(canvas);
// This will move the ViewPlatform back a bit so the objects
// in the scene can be viewed.
universe.getViewingPlatform().setNominalViewingTransform();
universe.addBranchGraph(createSceneGraph(cheese));
}
public static void main(String[] args) {
final Random RND = new Random(System.currentTimeMillis());
CubeState[][][] testCheese = new CubeState[5][8][11];
for (int x = 0; x < 5; x++)
for (int y = 0; y < 8; y++)
for (int z = 0; z < 11; z++)
testCheese[x][y][z] = (RND.nextFloat() < 0.7f)
? CubeState.CHEESE
: (RND.nextBoolean() ? CubeState.WATER : CubeState.AIR);
// Applet can also run as a stand-alone application
new MainFrame(new LeakyCheeseGUI(testCheese), 512, 512);
}
}
You will probably want to use Javascript, and specifically WebGL. Javascript is the de facto language for interactive web pages, and WebGL is a Javascript API for rendering 2D and 3D scenes on an HTML5 canvas element. A solution using WebGL should be compatible with all major browsers. Programming even simple scenes in WebGL can be pretty involved though, so I'd recommend using a framework such as three.js to simplify things.
Here is an example of interactive, draggable cubes using three.js. Some of the key lines of code from that example are:
// create the cube
var object = new THREE.Mesh( geometry, new THREE.MeshLambertMaterial( { color: Math.random() * 0xffffff } ) );
// set coordinates, rotation, and scale of the cubes
object.position.x = ...
object.position.y = ...
object.position.z = ...
object.rotation.x = ...
object.rotation.y = ...
object.rotation.z = ...
object.scale.x = ...
object.scale.y = ...
object.scale.z = ...
// lighting stuff
object.castShadow = true;
object.receiveShadow = true;
// add to scene and list of objects
scene.add( object );
objects.push( object );
Again, the full, working example is found at this link (click view source on that page to view the code on github).

How to get coordinates of click on resized ImageView in JavaFX Gridpane

Currently, there is a disconnect between where I click and the coordinates that are printed to terminal. It seems it is basing the coordinates off of the center GridPane but not the image itself of the imageView.
Previously, I would not resize the images in this application but rather in an automated photoshop process and the click/coordinates would be in sync.
Here is where I make an arrayList of resized Images which will be displayed in the GridPane
//Makes imageView arraylist from all images in a given directory
private ArrayList<ImageView> makeImageViewArr(File imagesDir) {
//transer file names from directory folder to string array
File[] strImageList = imagesDir.listFiles();
myMouseHandler mouseHandler = new myMouseHandler();
//instantiate imageview arraylist
arrImageList = new ArrayList<ImageView>();
//get files from folder & start at 1 to ignore ds.Store
for(int count = 1; count < strImageList.length; count++) {
ImageView imgView =
new ImageView(strImageList[count].toURI().toString());
imgView.setFitHeight(360);
imgView.setFitWidth(640);
imgView.setPreserveRatio(true);
imgView.setSmooth(true);
imgView.setOnMouseClicked(mouseHandler);
arrImageList.add(imgView);
}
return arrImageList;
}//END METHOD
Here is the class where I handle the actual click on the imageView
//inner class for mouse input
public class myMouseHandler implements EventHandler<MouseEvent>
{
/* Method which handles & executes mouse clicks
*
* #param e KeyEvent the code from the mouse click
*
* returns none
*/
#Override
public void handle(MouseEvent e)
{
//reset image to erase previous box
pane.setCenter(arrImageList.get(index));
//get image that was clicked on and pixel reader from image
Image clickedImg = arrImageList.get(index).getImage();
PixelReader pxlRdr = clickedImg.getPixelReader();
//get image height/width for copy
int clickedImgW = (int) clickedImg.getWidth();
int clickedImgH = (int) clickedImg.getHeight();
//get x and y coordinates from click
int xCord = (int) e.getX();
int yCord = (int) e.getY();
nudgeX = xCord;
nudgeY = yCord;
//create writeable image and pixelwriter to draw click region
WritableImage outlineImg = new WritableImage(pxlRdr,
clickedImgW, clickedImgH);
PixelWriter pxlWrtr;
pxlWrtr = outlineImg.getPixelWriter();
//draws region
drawRegion(pxlWrtr, xCord, yCord);
//display image with click boundary and link mouseHandler
//to refresh on next click
ImageView tempImg = new ImageView(outlineImg);
tempImg.setFitHeight(360);
tempImg.setFitWidth(640);
tempImg.setPreserveRatio(true);
tempImg.setSmooth(true);
myMouseHandler mouseHandler = new myMouseHandler();
tempImg.setOnMouseClicked( mouseHandler );
pane.setCenter( tempImg );
//print relevant info about click region
System.out.println("xCord: " + xCord);
System.out.println("yCord: " + yCord + "\n");
}//END METHOD
}//END INNER CLASS
This simply draws a region to show where the click has occurred.
//Draws region boundary after user clicks on image
private void drawRegion(PixelWriter pxlWrtr, int xCord, int yCord) {
Image clickedImg = arrImageList.get(index).getImage();
PixelReader pxlRdr = clickedImg.getPixelReader();
//get image height/width for copy
int clickedImgW = (int) clickedImg.getWidth();
int clickedImgH = (int) clickedImg.getHeight();
//draw right vert boundary
for( int yTrack = (yCord + 1); yTrack > (yCord - regionSize ); yTrack--) {
if((yTrack > 0 && yCord < clickedImgH - 1)
&& ( xCord < clickedImgW - 1 )) {
pxlWrtr.setColor(xCord + 1, yTrack, Color.RED);
}
}
//draw left vert boundary
for( int yTrack = (yCord + 1); yTrack > (yCord - regionSize); yTrack--) {
if((yTrack > 0 && yCord < clickedImgH - 1)
&& (xCord - regionSize) >= 0) {
pxlWrtr.setColor(xCord - regionSize, yTrack, Color.RED);
}
}
//draw bottom boundary
for(int xTrack = (xCord + 1); xTrack >= (xCord - regionSize); xTrack--) {
if(xTrack > 0 && yCord < clickedImgH - 1) {
pxlWrtr.setColor(xTrack, yCord + 1, Color.RED);
}
}
//draw top boundary
for(int xTrack = (xCord + 1); xTrack >= (xCord - regionSize); xTrack--) {
if(xTrack >= 0 && (yCord - regionSize) >= 0 ) {
pxlWrtr.setColor(xTrack, yCord - regionSize, Color.RED);
}
}
}//END METHOD
According to the documentation for preserveRatio:
…the dimensions of this node as reported by the node's bounds will be equal to the size of the scaled image…
So you can use that to scale the MouseEvent coordinates:
double x = e.getX();
double y = e.getY();
ImageView view = (ImageView) e.getSource();
Bounds bounds = view.getLayoutBounds();
double xScale = bounds.getWidth() / view.getImage().getWidth();
double yScale = bounds.getHeight() / view.getImage().getHeight();
x /= xScale;
y /= yScale;
int xCord = (int) x;
int yCord = (int) y;

Categories

Resources