java.awt drawRect() wrongly interprets "width" and "height" (produces +1 pixel right and down) - swing

I have started exercising with Swing programming and found one really weird thing which I consider as a bug in the basics.
The below example shows that "width" and "height" values are treated differently when we create a Rectangle and when we display it.
import java.awt.*;
// I create a rectangle 2 x 2 with height=2, width=2
Rectangle rec = new Rectangle(1, 1, 2, 2);
// I make sure that it contains only 4 points: (1,1), (1,2), (2,1), (2,2)
println("- " + rec.contains(1, 1) + ", " + rec.contains(1, 2) + ", " + rec.contains(1, 3));
println("- " + rec.contains(2, 1) + ", " + rec.contains(2, 2) + ", " + rec.contains(2, 3));
println("- " + rec.contains(3, 1) + ", " + rec.contains(3, 2) + ", " + rec.contains(3, 3));
Output:
true, true, false
true, true, false
false, false, false
Then:
// I make sure again that the right-bottom point of my rectangle is (2,2)
// by checking the intersection result with another Rectangle
println("intersect (2,2): " + rec.intersects(new Rectangle(2, 2, 5, 5)));
println("intersect (3,3): " + rec.intersects(new Rectangle(3, 3, 5, 5)));
Output:
intersect (2,2): true
intersect (3,3): false
So from math point of view this is really rectangle 2 x 2 which has 4 integer points (that is 4 pixels): (1,1), (1,2), (2,1), (2,2). But what if we try to render it?
// Let's check what is get drawn
g.drawRect(1, 1, 2, 2); // displays 3 x 3 (as if width=3, height=3) !!
// Try it different way
((Graphics2D)g).draw(rec); // displays 3 x 3 again (as if width=3, height=3) !!
The yellow small rectangle is shown as 3 x 3:
Conclusion: Rectangle() class constructor considers width/height as a real width/height of the shape in pixels, while drawing method drawRect() consider width/height as a shift relative to the left-top pixel.
So java.awt represents the same rectangle as N x M for calculations, but as (N+1) x (M+1) when it is rendered with drawRect().
I find this totally wrong, because the default human logic is to suppose that we draw exactly what we have!
I wonder if someone else knows about this issue and can give some references to bug trackers or/and some logical explanations of such behavior. Just Google search gives me nothing about.
I wonder also how another programmers solve this problem. Use wrappers to drawRect pushing (x, y, width - 1, height -1)? I just can't believe that it is OK for everyone that it is working so weird like this.
UPDATE 1: Posting the "minimal" code snippet which reproduces the problem:
import javax.swing.*;
import java.awt.*;
public class Main {
public static void main(String args[]) {
JFrame jFrame = new JFrame("Bug of drawRect() demo");
JPanel jPanel = new JPanel() {
#Override
public void paintComponent(Graphics g) {
g.drawRect(1,1,2,2);
}
};
jFrame.add(jPanel);
jFrame.setSize(400, 100);
jFrame.setLocationRelativeTo(null);
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jFrame.setVisible(true);
}
}
UPDATE 2: I am using OpenJDK 8 from https://github.com/ojdkbuild/ojdkbuild/releases. If I use "Go To -> Implementation(s)" in my IDE I see the drawRect() implementation here: C:\Program Files\ojdkbuild\java-1.8.0-openjdk-1.8.0.181-1\src.zip!\java\awt\Graphics.java.
public void drawRect(int x, int y, int width, int height) {
if ((width < 0) || (height < 0)) {
return;
}
if (height == 0 || width == 0) {
drawLine(x, y, x + width, y + height);
} else {
drawLine(x, y, x + width - 1, y);
drawLine(x + width, y, x + width, y + height - 1);
drawLine(x + width, y + height, x + 1, y + height);
drawLine(x, y + height, x, y + 1);
}
}
In this implementation I see that it draws the second and the third line at "x + width" instead of "x + width - 1" which causes the bug.

As I ran into the same problem, with the code below (it is more complex than that), it took me some time to understand what I was doing wrong (I put emphasis here, like you, I think the API is wrong: you draw rectangle of 100x100, it should result in 100x100, not 101x101.):
public class MyComponent extends JComponent {
#Override
public void paintComponent(final Graphics g) {
g.setColor(Color.BLACK);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(Color.RED);
g.fillRect(0, 0, 100, getHeight());
g.setColor(Color.WHITE);
g.drawRect(0, 0, 100, 100);
}
public static void main(final String[] args) {
final JFrame frame = new JFrame("Test");
frame.setSize(150, +156);
frame.getContentPane().add(new MyComponent());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
This result in the sample window, where we can see the drawn rectangle having one additional pixels on width/height.
This is problematic in my case, because I am calling repaint with the region in which I'm drawing the rectangle. So:
I am calling repaint(0, 0, 100, 100)
Swing repaint the 100x100 square
The white line is still there because it was not repaint.
The problem is that the Graphic class is not homogeneous in how it use the width and height, as one can see in the below Javadoc extract:
From drawRect javadoc:
Draws the outline of the specified rectangle. The left and right edges
of the rectangle are at x and x + width. The top and bottom edges are
at y and y + height. The rectangle is drawn using the graphics
context's current color.
From fillRect javadoc:
Fills the specified rectangle. The left and right edges of the
rectangle are at x and x + width - 1. The top and bottom edges are at
y and y + height - 1. The resulting rectangle covers an area width
pixels wide by height pixels tall. The rectangle is filled using the
graphics context's current color.
Fixing such an API is hard because code depending on it would fail (because to fix the actual problem with code above, I would pass 99x99 to have 100x100 rectangle: fixing the API would result in a 99x99 rectangle).
You may expect this to never be fixed unless the class is retrofitted into a new package (probably merging Graphics2D along the way).
TL;DR: you should probably create a static method drawRect doing the same as fillRect, like below:
public static void drawRect(Graphics g, int x, int y, int w, int h) {
g.drawRect(x, y, w - 1, h - 1);
}

Related

Linking buttons to imported information from csv file

I would like to ask some help turning this representation into something more interactive. Im working with Processsing latest version. The main idea is I have a table in excel, I imported it into processsing. So far so good. Displayed the values as bubbles. My problem is that would like to create buttons to See further details from each bubble. So, click a button A, the corresponding bubbleA would light up and display its specific value, according to the imported table. I dont know how to connect the button to each bubble, neither how to turn the bubble to its original state once the button is pressed again.
I`m not from the programming field, I just manage to get this far with info that I have found online, so the code must be pretty messed up. I was trying to use the libraries from Processing but I cant really understand how the controlP5 works.. I asssume the structure is way more advanced than what I can handle now.
So, if anyone can help me, I would appreciate it very much.
Table B_A_table;
//Bubble[] bubbles = new Bubble[29];
ArrayList<Bubble> bubbles = new ArrayList<Bubble>();
float scale = 3;
int c_verdeClaro = color(182,189,149);
import controlP5.*;
ControlP5 cp5;
PFont font;
PFont font1;
void setup() {
size(1000, 1000);
colorMode(HSB,255);
loadData();
font = createFont("Verdana", 18);
font1 = createFont("Verdana Bold", 18);
//font = loadFont("Arial-Black-48.vlw");
//BEGIN TEST
cp5 = new ControlP5(this);
noStroke();
smooth();
//TEST
cp5 = new ControlP5(this);
ButtonBar b = cp5.addButtonBar("bar")
.setPosition(500, 0)
.setSize(400, 20)
.addItems(split("Man Woman 20s 30s 40s 50s µµ½É±Ç"," "))
;
println(b.getItem("Man"));
b.changeItem("a","text","first");
b.changeItem("b","text","second");
b.changeItem("c","text","third");
b.onMove(new CallbackListener(){
public void controlEvent(CallbackEvent ev) {
ButtonBar bar = (ButtonBar)ev.getController();
println("hello ",bar.hover());
}
});
//WND TEST
}
void draw() {
// Display all bubbles
background(0);
Graph_Bike_Aware_Experience_Sim();
//Graph_Bike_Uso_Satisf();
pushMatrix();
translate(400,500);
for (Bubble bubble : bubbles){
bubble.display();
}
popMatrix();
}
void loadData() {
// "header" indicates the file has header row. The size of the array
// is then determined by the number of rows in the table.
B_A_table = loadTable("BikeAwareExpr_Nao.csv", "header");
for (int i = 0; i<B_A_table.getRowCount(); i++) {
// Iterate over all the rows in a table.
TableRow row = B_A_table.getRow(i);
// Access the fields via their column name (or index).
float x = row.getFloat("awareness_x")*scale;
float y = row.getFloat("experi")*scale;
float d = row.getFloat("awareness_x")*0.2;
String n = row.getString("Awareness_Bike");
Float id = row.getFloat("ID");
// Make a Bubble object out of the data from each row.
bubbles.add(new Bubble(x, y, d, n,id));
//bubbles[i] = new Bubble(x, y, d, n);
}
}
// This simple Bubble class draws a circle to the window
// and displays a text label when the mouse hovers.
class Bubble {
float x, y;
float diameter;
String name;
Float id;
// Create the Bubble
Bubble(float tempX, float tempY, float tempD, String s, Float id) {
x = tempX;
y = tempY;
diameter = tempD;
name = s;
}
// Display the Bubble
void display() {
stroke(220);
strokeWeight(1);
noFill();
ellipse(x, y, diameter, diameter);
fill(200);
textFont(font,15);
textAlign(CENTER);
text(name, x,y);
}
}
void Graph_Bike_Aware_Experience_Sim(){
pushMatrix();
int tam_table = B_A_table.getRowCount();
translate(400,500);
stroke(150);
strokeWeight(1);
line( - 100 *scale, 0*scale, + 100*scale, 0*scale);
line(0*scale, - 100*scale, 0*scale, + 100*scale);
//fill(c_verdeClaro);
noStroke();
fill(255);
textFont(font,13);
text("Experience", 40 , -97*scale);
text("Awareness", -89*scale, -10);
fill(50);
//textFont(Arial-Black-48, 32);
text("0", 10,15);
pushMatrix();
fill(0);
textFont(font1,30 );
text("IDEAL", 80*scale , -120*scale);
fill(70);
textFont(font1,25 );
text("Bicycle", 0 , -110*scale);
popMatrix();
popMatrix();
}
/*void Graph_Bike_Uso_Satisf(){
pushMatrix();
int tam_table = B_A_table.getRowCount();
translate(900,400);
stroke(150);
strokeWeight(1);
line( - 100 *scale, 0*scale, + 100*scale, 0*scale);
line(0*scale, - 100*scale, 0*scale, + 100*scale);
//fill(c_verdeClaro);
noStroke();
c_verdeClaro_grad();
c_verdeEscuro_grad();
c_Vermelho_grad();
fill(255);
text("Experience", 40 , -97*scale);
text("Awareness", -89*scale, -10);
fill(0);
//textFont(Arial-Black-48, 32);
text("word", 10, 50);
text("IDEAL", 97*scale , -97*scale);
popMatrix();
}
*/
To handle this without Object Oriented Programing, or 'OOP' is a waste of time and code.
But relax OOP also referred to as "Objects" or " Classes". It is not that hard to get. In fact it is simple to learn, I mean, the basic stuff. And it makes things very easy.
In your case, you need to build an object to represent your data. Then you create several of them, populate with data, and all will be hold together. Kind of knowing it own business.
The usual example is
Class car
has those properties
color
plate
Then you can say:
Make a Blue, 3231 car!
Make a Red, 3001 car!
and so on...
When you need it you can ask, Blue car what's your plate, and it will know.
They can also have kind of "behaviours" known as functions or methods. Those do things, so you could say: "Red, turn right", or "Bubble, show me your data!" and they will OBEY you. :)
There is no point in trying to teach this hear though. There are TONs of great tutorials around. Use Duck Duck Go :) or google if you like.
This one here are from Processing's tutorials section itself. Great ones to begin with. :)

Cocos2dx: Sprite3D won't render to texture

I use RenderTexture to render a layer with all its nodes to a texture then apply an OpenGL shader on that texture to create post-process effects. It works all fine except with Sprite3D and Billboard nodes. It has been asked on their forums a few times without any response. I wonder if anyone got this to work.
Here is an example:
Layer* gameLayer = Layer::create();
this->addChild(gameLayer, 0);
auto dir = Director::getInstance()->getWinSize();
Camera *camera = Camera::createPerspective(60, (GLfloat)dir.width / dir.height, 1, 1000);
camera->setPosition3D(Vec3(0, 100, 100));
camera->lookAt(Vec3(0, 0, 0), Vec3(0, 1, 0));
gameLayer->addChild(camera); //add camera to the scene
// You'll get a NULL camera inside BillBoard::calculateBillbaordTransform() function
// if you call visit()
/*auto billboard = BillBoard::create("cocos2d-x.png", BillBoard::Mode::VIEW_POINT_ORIENTED);
billboard->setPosition(Vec2(VisibleRect::center().x, VisibleRect::center().y));
gameLayer->addChild(billboard, 100);*/
// This one won't render into the texture
Sprite3D* sprite3D = Sprite3D::create("blend_test/character_3_animations_test.c3b");
sprite3D->setScale(5.0f); //sets the object scale in float
sprite3D->setRotation3D(Vec3(0.0f, 0.0f, 0.0f));
//sprite3D->setPosition3D(Vec3(VisibleRect::center().x, VisibleRect::center().y, 0.0f)); //sets sprite position
sprite3D->setPosition(Vec2(VisibleRect::center().x, VisibleRect::center().y));
gameLayer->addChild(sprite3D, 1); //adds sprite to scene, z-index: 1
// This one works just fine and appears black and white as expected
// in the resulting texture
Sprite* sprite2D = Sprite::create("cocos2d-x.png");
sprite2D->setPosition(Vec2(VisibleRect::center().x, VisibleRect::center().y));
gameLayer->addChild(sprite2D);
// Black and white OpenGL shader
GLProgram* glProgram = GLProgram::createWithFilenames("shaders/gray.vert", "shaders/gray.frag");
glProgram->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_COLOR, GLProgram::VERTEX_ATTRIB_POSITION);
glProgram->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_POSITION, GLProgram::VERTEX_ATTRIB_COLOR);
glProgram->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_TEX_COORD, GLProgram::VERTEX_ATTRIB_TEX_COORD);
glProgram->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_TEX_COORD1, GLProgram::VERTEX_ATTRIB_TEX_COORD1);
glProgram->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_TEX_COORD2, GLProgram::VERTEX_ATTRIB_TEX_COORD2);
glProgram->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_TEX_COORD3, GLProgram::VERTEX_ATTRIB_TEX_COORD3);
glProgram->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_NORMAL, GLProgram::VERTEX_ATTRIB_NORMAL);
glProgram->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_BLEND_WEIGHT, GLProgram::VERTEX_ATTRIB_BLEND_WEIGHT);
glProgram->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_BLEND_INDEX, GLProgram::VERTEX_ATTRIB_BLEND_INDEX);
glProgram->link();
glProgram->updateUniforms();
RenderTexture* renderTexture = RenderTexture::create(VisibleRect::width(), VisibleRect::height());
renderTexture->retain();
Sprite* ppSprite = Sprite::createWithTexture(renderTexture->getSprite()->getTexture());
ppSprite->setTextureRect(Rect(0, 0, ppSprite->getTexture()->getContentSize().width,
ppSprite->getTexture()->getContentSize().height));
ppSprite->setAnchorPoint(Point::ZERO);
ppSprite->setPosition(Point::ZERO);
ppSprite->setFlippedY(true);
ppSprite->setGLProgram(glProgram);
this->addChild(ppSprite, 100);
renderTexture->beginWithClear(0.0f, 0.0f, 0.0f, 0.0f);
auto renderer = _director->getRenderer();
auto& parentTransform = _director->getMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
gameLayer->visit(renderer, parentTransform, true);
//gameLayer->visit();
renderTexture->end();
ppSprite->setTexture(renderTexture->getSprite()->getTexture());
Cocos2d-x v3.11.1 (current as of this post) and below don't properly support RenderTextures with Sprite3D because of a clear depth buffer bug.
There is a GitHub issue on the bug. But a workaround now exists:
...
sprite3D->setForce2DQueue(true); // puts your Sprite3D on same render queue as the RenderTexture. More info below.
...
auto rt = RenderTexture::create(1280, 720, Texture2D::PixelFormat::RGBA8888, GL_DEPTH24_STENCIL8); // By default a depth buffer isn't created
rt->setKeepMatrix(true); // required
...
...
rt->beginWithClear(0, 0, 0, 0, 1); // required, clears the depth buffer
Also, changes need to be made to RenderTexture.cpp. This fixes the clear depth buffer bug in Cocos2d-x.
void RenderTexture::onClear()
{
// save clear color
GLfloat oldClearColor[4] = {0.0f};
GLfloat oldDepthClearValue = 0.0f;
GLint oldStencilClearValue = 0;
GLboolean oldDepthWrite = GL_FALSE;
// backup and set
if (_clearFlags & GL_COLOR_BUFFER_BIT)
{
glGetFloatv(GL_COLOR_CLEAR_VALUE, oldClearColor);
glClearColor(_clearColor.r, _clearColor.g, _clearColor.b, _clearColor.a);
}
if (_clearFlags & GL_DEPTH_BUFFER_BIT)
{
glGetFloatv(GL_DEPTH_CLEAR_VALUE, &oldDepthClearValue);
glClearDepth(_clearDepth);
glGetBooleanv(GL_DEPTH_WRITEMASK, &oldDepthWrite);
glDepthMask(true);
}
if (_clearFlags & GL_STENCIL_BUFFER_BIT)
{
glGetIntegerv(GL_STENCIL_CLEAR_VALUE, &oldStencilClearValue);
glClearStencil(_clearStencil);
}
// clear
glClear(_clearFlags);
// restore
if (_clearFlags & GL_COLOR_BUFFER_BIT)
{
glClearColor(oldClearColor[0], oldClearColor[1], oldClearColor[2], oldClearColor[3]);
}
if (_clearFlags & GL_DEPTH_BUFFER_BIT)
{
glClearDepth(oldDepthClearValue);
glDepthMask(oldDepthWrite);
}
if (_clearFlags & GL_STENCIL_BUFFER_BIT)
{
glClearStencil(oldStencilClearValue);
}
}
See the issue for more details. I also made an example gist of the workaround. Screenshot below.
I'm not sure about billboards, but this workaround might fix it too.
Info on Cocos2d-x render queues:
The Sprite3D needs to be on the same render queue as the RenderTexture. Cocos2d-x (as of v3.7 or so) now has 5 render queues:
Global Z Order < 0
3D Opaque
3D Transparent
Global Z Order == 0 (default for 2D)
Global Z Order > 0
You can put the Sprite3D and the RenderTexture on the last queue with setGlobalZOrder(1) or just put the Sprite3D in the 2D queue with sprite3D->setForce2DQueue(true).
unlike cocos2d RenderTexture the following worked fine for 3D screen capture (or anything i imagine)!
Sprite * CcGlobal::getScreenAsSprite(void) {
Size screenSize = Director::getInstance()->getWinSize();
int width = screenSize.width;
int height = screenSize.height;
std::shared_ptr<GLubyte> buffer(new GLubyte[width * height * 4], [](GLubyte* p) { CC_SAFE_DELETE_ARRAY(p); });
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer.get());
Image* image = new (std::nothrow) Image;
image->initWithRawData(buffer.get(), width * height * 4, width, height, 8);
Texture2D *texture = new (std::nothrow) Texture2D();
texture->initWithImage(image);
SpriteFrame *spriteFrame = SpriteFrame::createWithTexture(texture, Rect(Vec2(0, 0), screenSize));
Sprite *sprite = Sprite::createWithSpriteFrame(spriteFrame);
sprite->setFlippedY(true);
delete image;
return sprite;
}
===================================================

Convert a Vector3f x and y to exact pixel size

I am currently playing a bit around with a jave game engine. ( Developed by a Friend using LWJGL ). This Engine uses Vector3f Positions to draw a picture on the screen. I want to draw Pictures with an exact pixel position... so I created a Vector3f and, somehow, have to convert the x and y values to a pixel position now. But how?
Assuming the components of the Vector3f use OpenGL screen coordinates (origin in the center of the screen, x goes right, y goes up and the axes have values of -1 to 1). The resulting pixel coordinate uses LWJGL coordinate system, i.e. origin is in bottom left corner.
import org.lwjgl.opengl.Display;
import org.lwjgl.util.Point;
import org.lwjgl.util.vector.Vector3f;
public static Point toPixelPos(Vector3f v) {
int width = Display.getWidth();
int height = Display.getHeight();
int x = (int) (width * (v.x + 1) / 2);
int y = (int) (height * (v.y + 1) / 2);
return new Point(x, y);
}

Body and Sprite Positions

When I compile my hero doesn't touch the floor but stops anyways a few pixels above. I figured if I traced both bodies and their respective sprites I'd know which ones aren't coincinding.
trace("Hero: ", hero.position.y, "/", heroSprite.y);
trace("Floor: ", floor.position.y, "/", floorSprite.y);
I get the following,
Hero: 470.2(...) / 470.2
Floor: 0 / 0
Also, how is the floor position 0 in its y property when:
createWall(stage.stageWidth/2, 500, 100, 30); //(y = 500)
I read that while the nape body 'registration point' is in the middle, the sprite one is in the upper-left corner so when giving the sprite the same x and y of the body it won't match. Below the sprite will be out of position.
public function createWall(x:Number, y:Number, width:Number, height:Number):void
{
wall.shapes.add(new Polygon(Polygon.rect(x, y, width, height)));
wall.space = space;
wallSprite.graphics.beginFill(0x000000);
wallSprite.graphics.drawRect(x, y, width, height);
wallSprite.graphics.endFill;
addChild(wallSprite);
wall.userData.sprite = (wallSprite);
addChild(wall.userData.sprite);
}
I tried wallSprite.graphics.drawRect(-width/2, -height/2, width, height); but didn't work. Althought I believe the problem is there, placing the sprite properly.
Drawing does not affect the position of an object. In your case the wall is at 0,0 and you draw at x:stage.stageWidth/2 , y: 500 but that's not going to become the wall coordinates, those are still 0,0 anyway.

Setting X Coordinate from Mouse Location

i have a darkBlueRect rectangle sprite and a copy of the same sprite with a larger scale and a lighter color - lightBlueRect.
i'm attempting to shift the location of the lightBlueRect according to the mouse.x location when the mouse is moving over the darkBlueRect. this way if the mouse is on the right of the darkBlueRect than the location of the scaled up lightBlueRect will be on the opposite side and shifted towards the left proportionate to the mouse position and scale. in addition, the lightBlueRect must appear "locked" to the darkBlueRect so lightBlueRect.x must never be more than darkBlueRect.x and lightBlueRect.x + lightBlueRect.width must never be less than darkBlueRect.x + darkBlueRect.width.
the image below depicts 3 states of what i'm attempting to accomplish:
State A: mouse.x is over darkBlueRect.x = 1 and both sprites are aligned to the left.
State B: mouse.x is in the middle of darkBlueRect and both sprites are aligned to the middle.
State C: mouse.x is on the last pixel of darkBlueRect and both sprites are aligned to the right.
for this example, the darkBlueRect.width is equal to 170 and the lightBlueRect.width is equal to 320, or a scale of 1.89.
each time the mouse changes it's x position over darkBlueRect the following is called. however, while my current code works for the most part, it's not exactly correct. when the mouse.x is over darkBlueRect.x = 1, as shown in State A, the lightBlueRect.x is not property aligned with darkBlueRect and appears less than darkBlueRect.x.
var scale:Number = 1.89;
lightBlueRect.x = darkBlueRect.x - Math.round((mouse.x * scale) / darkBlueRect.width * (lightBlueRect.width - darkBlueRect.width));
what equation can i use so that no matter the scale of the lightBlueRect it's first position (mouse over first pixel) and last position (mouse over last pixel) will result in the 2 sprites being aligned as well as property proportionate positioning in between?
[EDIT] the coordinates of the darkBlueRect is {0, 0}, so when the lightBlueRect moves towards the left it is moving into the negative. i could have simply written my code (what doesn't work) like this instead:
var scale:Number = 1.89;
lightBlueRect.x = 0 - Math.round((mouse.x * scale) / darkBlueRect.width * (lightBlueRect.width - darkBlueRect.width));
[EDIT 2]
when the display objects are small, the problem is difficult to notice. however, when they are large the problem becomes move obvious. the problem, here, being that the objects on the left side are misaligned.
also the problem is probably exasperated by the fact that both the lightBlueRect and darkBlueRect are scalable. darkBlueRect is scaled down and lightBlueRect is scaled up.
here is a link to the test displaying the problem. mousing over the shape quickly will obviously result in inaccurate alignment since it's based on frame rate speed, but this is not my concern. still, when you slowly mouse over the shape it will not align correctly on the left side when the mouse is over the first pixel of darkBlueRect: http://www.geoffreymattie.com/test/test.html
[SWF(width = "1000", height = "600", backgroundColor = "0xCCCCCC")]
import flash.display.Sprite;
import flash.events.MouseEvent;
var downScale:Number = 0.48;
var upScale:Number = 2.64;
var darkBlueRect:Sprite = createSprite();
darkBlueRect.scaleX = darkBlueRect.scaleY = downScale;
darkBlueRect.x = stage.stageWidth / 2 - darkBlueRect.width / 2;
darkBlueRect.y = stage.stageHeight / 2 - darkBlueRect.height / 2;
addChild(darkBlueRect);
var lightBlueRect:Sprite = createSprite();
lightBlueRect.scaleX = lightBlueRect.scaleY = upScale;
lightBlueRect.y = stage.stageHeight / 2 - lightBlueRect.height / 2;
lightBlueRect.x = stage.stageWidth;
lightBlueRect.mouseEnabled = false;
addChild(lightBlueRect);
darkBlueRect.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveEventHandler);
function mouseMoveEventHandler(evt:MouseEvent):void
{
lightBlueRect.x = darkBlueRect.x + Math.max(0.0, Math.min(darkBlueRect.mouseX / darkBlueRect.width * downScale, 1.0)) * (darkBlueRect.width - lightBlueRect.width);
}
function createSprite():Sprite
{
var result:Sprite = new Sprite();
result.graphics.beginFill(0x0000FF, 0.5);
result.graphics.drawRect(0, 0, 700, 200);
result.graphics.endFill();
return result;
}
i believe the problem is that the scaling of the shapes.
Assuming you have a Clamp function handy, and that width is floating-point so that division works as expected:
lBR.x = dBR.x + Clamp((mouse.x - dBR.x) / dBR.width, 0, 1) * (dBR.width - lBR.width);
(You can define Clamp(x, m, M) = min(max(x, m), M) if you don't have one.)