mapping two points on image to another one - java

I am trying to map two points on one image to two points on the original image so i divided the work into three main actions first scaling the n rotation then translation after everything but cant position them correctly the scaling works fine and the translation also the rotation works perfectly if i didn't scale the images only way the rotation work perfectly when i rotate around custom point but the image get distorted
Rotate rotation = new Rotate();
rotation.setPivotX(proj.s2[0]);
rotation.setPivotY(proj.s2[1]);
MainView1.getTransforms().add(rotation);
MainView1.setManaged(false);
rotation.setAngle(Angle);
here is the code without custom rotation
guidebutton.setOnMouseClicked(event->{
if (!first_rot) {
proj.f2[0]=Lball.getCenterX();
proj.f2[1]= Lball.getCenterY();
proj.f1[0]=Rball.getCenterX();
proj.f1[1]= Rball.getCenterY();
MainView.setStyle("-fx-opacity : 0.0;");
guidetext.setText("now position them on the second image and click done");
first_rot=true;
}else {
proj.s2[0]=Lball.getCenterX();
proj.s2[1]= Lball.getCenterY();
proj.s1[0]=Rball.getCenterX();
proj.s1[1]= Rball.getCenterY();
//fixing the image first then fixing the points
// fixing the image
//adjusting the scale
double f[]=tranformations.dis_vec_d(proj.f1, proj.f2);//get the distance between the two points on the first image
double s[]=tranformations.dis_vec_d(proj.s1, proj.s2);//get the distance between the two points on the secondimage
double facx=f[0]/s[0];//factor of scale in x direction
double facy=f[1]/s[1];//factor of scale in y direction
//getting the position of second image inside the window
Bounds bounds = MainView1.getBoundsInLocal();
Bounds screenBounds = MainView1.localToScreen(bounds);
double x = screenBounds.getMinX();
double y = screenBounds.getMinY();
MainView1.setScaleX(facx);
// get the new position of image after scaling to adjust the position
bounds = MainView1.getBoundsInLocal();
screenBounds = MainView1.localToScreen(bounds);
double nx = screenBounds.getMinX();
double ny = screenBounds.getMinY();
double nmx = screenBounds.getMaxX()-nx;
double nmy = screenBounds.getMaxY()-ny;
MainView1.setTranslateX(x-nx);
MainView1.setTranslateY(y-ny);
double[]orig={nmx/2,nmy/2};
//adjusting rotation
//calculating the angle between the two line to adjust the rotation
double Angle=tranformations.angle_d(proj.s1, proj.s2);
Angle-=tranformations.angle_d(proj.f1, proj.f2);
//Add the Rotate to the ImageView's Transforms
MainView1.setRotate(Angle);
MainView1.setTranslateX(MainView.getTranslateX()+proj.f2[0]-proj.s2[0]);
MainView1.setTranslateY(MainView.getTranslateY()+proj.f2[1]-proj.s2[1]);
}
});
both views and points in unmanaged group "draw" when i get every thing work it get down when i use zooming when positioning points on the second image
i use this code for zooming using mouse wheel
final double SCALE_DELTA = 1.1;
draw.setOnScroll(event->{
event.consume();
if (event.getDeltaY() == 0) {
return;
}
double scaleFactor =(event.getDeltaY() > 0)? SCALE_DELTA: 1/SCALE_DELTA;
draw.setScaleX(draw.getScaleX() * scaleFactor);
draw.setScaleY(draw.getScaleY() * scaleFactor);
});
edit to explain the question more i have these two separate images and i use the two red points on lights as to correctly position them over each other to so they can form the new image complete image

First align one of the points using a translation then scale using the aligned point's coordinates as pivot and finally use the same pivot point to perform a rotation aligning the other points.
The following example uses groups containing 2 circles each, but it should be simple enough to replace centerX/centerY with the image coordinates of the points:
#Override
public void start(Stage primaryStage) throws Exception {
// create group containing scene that remains in place
Circle target1 = new Circle(100, 200, 20, Color.RED);
Circle target2 = new Circle(150, 100, 20, Color.RED);
Group targetGroup = new Group(target1, target2);
// create group that will be transformed
Circle c1 = new Circle(30, 30, 20, Color.BLUE);
Circle c2 = new Circle(400, 400, 20, Color.BLUE);
Group g = new Group(c1, c2);
Scene scene = new Scene(new Pane(targetGroup, g), 500, 500);
// register handler for swapping between transformed/untransformed scene on button click
scene.setOnMouseClicked(new EventHandler<MouseEvent>() {
boolean transformed;
final Translate translate = new Translate();
final Scale scale = new Scale();
final Rotate rotate = new Rotate();
{
// add transforms to transformation target
g.getTransforms().addAll(rotate, scale, translate);
}
#Override
public void handle(MouseEvent event) {
if (transformed) {
// reset transforms to identity
translate.setX(0);
translate.setY(0);
scale.setX(1);
scale.setY(1);
rotate.setAngle(0);
} else {
// align c1 and target1
translate.setX(target1.getCenterX() - c1.getCenterX());
translate.setY(target1.getCenterY() - c1.getCenterY());
// scale
double scaleFactor = Math.hypot(target1.getCenterX() - target2.getCenterX(), target1.getCenterY() - target2.getCenterY())
/ Math.hypot(c1.getCenterX() - c2.getCenterX(), c1.getCenterY() - c2.getCenterY());
scale.setPivotX(target1.getCenterX());
scale.setPivotY(target1.getCenterY());
scale.setX(scaleFactor);
scale.setY(scaleFactor);
// rotate
rotate.setPivotX(target1.getCenterX());
rotate.setPivotY(target1.getCenterY());
rotate.setAngle(Math.toDegrees(Math.atan2(target2.getCenterY() - target1.getCenterY(), target2.getCenterX() - target1.getCenterX())
- Math.atan2(c2.getCenterY() - c1.getCenterY(), c2.getCenterX() - c1.getCenterX())));
}
transformed = !transformed;
}
});
primaryStage.setScene(scene);
primaryStage.show();
}

This is particularly simple using complex numbers.
An arbitrary similarity transform can be written
Z = a.z + b
where the modulus of a is the scaling factor, the argument of a is the rotation angle and b is the translation.
These coefficients are readily obtained from the known pairs of points by the usual two-points interpolation formula
Z = Z0 + (z - z0).(Z1 - Z0)/(z1 - z0)
or
a = (Z1 - Z0)/(z1 - z0)
b = Z0 - a.z0
You have the option of using a complex data type, or to retranscript the formulas in terms of real/imaginary parts.

If the square isn't rotated, then yeah, pick x and y independently.
If the square is rotated, the math gets a little trickier. Let's let the two end points of the diagonal be X and Y, represented as complex numbers.
Then look at the equation:
(Y - X)/(1 + i) x + X
When x = 0, this returns X. When x = 1 + i, it returns Y. In fact, this equation maps the unit rectangle onto the square whose diagonal's endpoints are X and Y.
So pick two random numbers 0 ≤ a, b ≤ 1, turn it into a random point a + bi on the unit rectangle, and then use the above equation to map into into a random point in the square.

Related

Java Arc2D Collision detection (With Rotation)

I have tried to create NPC character that can "see" the player by using cones of vision.
The NPC will rotate back and forth at all times.
My problem is that the arc has a generic and unchanging position, but when its drawn to the screen it looks correct.
[Screenshots of the collisions in action][1]
[GitHub link for java files][2]
I'm using Arc2D to draw the shape like this in my NPC class
// Update the shapes used in the npc
rect.setRect(x, y, w, h);
ellipse.setFrame(rect);
visionArc.setArcByCenter(cx, cy, visionDistance, visionAngle, visionAngle * 2, Arc2D.PIE);
/ CenterX, CenterY (of the npc),
/ the distance from the arc to the npc
/ a constant value around 45 degrees and a constant value around 90 degress (to make a pie shape)
I've tried multiplying the position and the angles by the sin and cosine of the NPC's current angle
something like these
visionArc.setArcByCenter(cx * (Math.cos(Math.toRadians(angle))), cy (Math.sin(Math.toRadians(angle)), visionDistance, visionAngle, visionAngle * 2, Arc2D.PIE);
or
visionArc.setArcByCenter(cx, cy, visionDistance, visionAngle - angle, (visionAngle + angle) * 2, Arc2D.PIE);
or
visionArc.setArcByCenter(cx, cy, visionDistance, visionAngle * (Math.cos(Math.toRadians(angle))), visionAngle * 2, Arc2D.PIE);
I've tried a lot but can't seem to find what works. Making the vision angles not constant makes an arc that expands and contracts, and multiplying the position by the sin or cosine of the angle will make the arc fly around the screen, which doesn't really work either.
This is the function that draws the given NPC
public void drawNPC(NPC npc, Graphics2D g2, AffineTransform old) {
// translate to the position of the npc and rotate
AffineTransform npcTransform = AffineTransform.getRotateInstance(Math.toRadians(npc.angle), npc.x, npc.y);
// Translate back a few units to keep the npc rotating about its own center
// point
npcTransform.translate(-npc.halfWidth, -npc.halfHeight);
g2.setTransform(npcTransform);
// g2.draw(npc.rect); //<-- show bounding box if you want
g2.setColor(npc.outlineColor);
g2.draw(npc.visionArc);
g2.setColor(Color.BLACK);
g2.draw(npc.ellipse);
g2.setTransform(old);
}
This is my collision detection algorithim - NPC is a superclass to ninja (Shorter range, higher peripheral)
public void checkNinjas(Level level) {
for (int i = 0; i < level.ninjas.size(); i++) {
Ninja ninja = level.ninjas.get(i);
playerRect = level.player.rect;
// Check collision
if (playerRect.getBounds2D().intersects(ninja.visionArc.getBounds2D())) {
// Create an area of the object for greater precision
Area area = new Area(playerRect);
area.intersect(new Area(ninja.visionArc));
// After checking if the area intersects a second time make the NPC "See" the player
if (!area.isEmpty()) {
ninja.seesPlayer = true;
}
else {
ninja.seesPlayer = false;
}
}
}
}
Can you help me correct the actual positions of the arcs for my collision detection? I have tried creating new shapes so I can have one to do math on and one to draw to the screen but I scrapped that and am starting again from here.
[1]: https://i.stack.imgur.com/rUvTM.png
[2]: https://github.com/ShadowDraco/ArcCollisionDetection
After a few days of coding and learning and testing new ideas I came back to this program and implemented the collision detection using my original idea (ray casting) and have created the equivalent with rays!
Screenshot of the new product
Github link to the project that taught me the solution
Here's the new math
public void setRays() {
for (int i = 0; i < rays.length; i++) {
double rayStartAngleX = Math.sin(Math.toRadians((startAngle - angle) + i));
double rayStartAngleY = Math.cos(Math.toRadians((startAngle - angle) + i));
rays[i].setLine(cx, cy, cx + visionDistance * rayStartAngleX, cy + visionDistance * rayStartAngleY);
}
}
Here is a link the the program I started after I asked this question and moved on to learn more, and an image to what the new product looks like
(The original github page has been updated with a new branch :) I'm learning git hub right now too
I do not believe that using Arc2D in the way I intended is possible, however there is .setArcByTangent method, it may be possible to use that but I wasn't going to get into that. Rays are cooler.

How to define an offset for a PatternColor fill in iText?

I am trying to add tiled diagonal watermarks to the pdf, but it seems that pattern fills in iText are always tiled from the bottom left of the page, meaning that the tiles at the top and right side of the page can be cut abruptly. Is there an option to tile from the top left or with an offset instead?
Here is a sample of the code:
List<String> watermarkLines = getWatermarkLines();
Rectangle watermarkRect = getWatermarkRect();
PdfContentByte over = stamper.getOverContent(1);
PdfPatternPainter painter = over.createPattern(watermarkRect.getWidth(), watermarkRect.getHeight();
for (int x = 0; x < watermarkLines.size(); x++) {
AffineTransform trans = getWatermarkTransform(watermarkLines, x);
ColumnText.showTextAligned(painter, 0, watermarkLines.get(x), (float) trans.getTranslateX(), (float) trans.getTranslateY(), 45f);
}
over.setColorFill(new PatternColor(painter));
over.rectangle(0, 0, pageSize.getWidth(), pageSize.getHeight());
over.fill();
I tried changing the x and y of the rectangle function to negative or positive values, but it seems that the watermark is still stamped in the pattern as if it was tiled from the bottom left, cutting it in the same place as before.
First of, I cannot fathom which iText version you are using,
List<String> watermarkLines = getWatermarkLines();
...
ColumnText.showTextAligned(painter, 0, watermarkLines.get(x), (float) trans.getTranslateX(), (float) trans.getTranslateY(), 45f);
implies that the third parameter of the ColumnText.showTextAligned method you use is typed as String or Object. The iText 5 version I have at hand, though, requires a Phrase there. Below I'll show how to apply an offset with the current iText 5.5.13. You'll have to check whether it also works for your version.
Yes, you can apply an offset... in the pattern definition!
If instead of
PdfPatternPainter painter = over.createPattern(watermarkRect.getWidth(), watermarkRect.getHeight());
you create the pattern like this
PdfPatternPainter painter = over.createPattern(2 * watermarkRect.getWidth(), 2 * watermarkRect.getHeight(),
watermarkRect.getWidth(), watermarkRect.getHeight());
you have the same step size of pattern application (watermarkRect.getWidth(), watermarkRect.getHeight()) but a canvas twice that width and twice that height to position you text on. By positioning the text with an offset, you effectively move the whole pattern by that offset.
E.g. if you calculate the offsets as
Rectangle pageSize = pdfReader.getCropBox(1);
float xOff = pageSize.getLeft();
float yOff = pageSize.getBottom() + ((int)pageSize.getHeight()) % ((int)watermarkRect.getHeight());
and draw the text using
ColumnText.showTextAligned(painter, 0, new Phrase(watermarkLines.get(x)), (float) trans.getTranslateX() + xOff, (float) trans.getTranslateY() + yOff, 45f);
the pattern should fill the page as if starting at the top left corner of the visible page.
You haven't supplied getWatermarkLines, getWatermarkRect, and getWatermarkTransform. If I use
static AffineTransform getWatermarkTransform(List<String> watermarkLines, int x) {
return AffineTransform.getTranslateInstance(6 + 15*x, 6);
}
static Rectangle getWatermarkRect() {
return new Rectangle(65, 50);
}
static List<String> getWatermarkLines() {
return Arrays.asList("Test line 1", "Test line 2");
}
your original code for me creates a top left corner like this
and the code with the above offset creates one like this

Android: How to properly scale mouse/touch input?

I have an android game where the canvas is scaled to look the same on all devices using this code:
canvas.scale((float)(getWidth()/(double)WIDTH), (float)(getHeight()/(double)HEIGHT))
where WIDTH and HEIGHT are 1920 and 1080 respectively. My problem is that for all my touch collisions (i.e. a user touching a shape) is handled using Paths and Regions:
public static boolean collided(Path a, Path b) {
Region clip = new Region(0,0,3000, 3000);
Region rA = new Region();
rA.setPath(a, clip);
Region rB = new Region();
rB.setPath(b, clip);
return !rA.quickReject(rB) && rA.op(rB, Region.Op.INTERSECT);
}
Now I also have another scale method (that barely has any effect from what I can tell, but on occasion does have an influence) to scale coordinates and dimensions:
public static double scale(double x) {
return dpToPx(x)/Resources.getSystem().getDisplayMetrics().density;
}
My problem is that with all of this scaling I can't seem to get the mouse to be in the correct position. Here is the code I use to create the mouse Path that handles collision between the mouse and other shapes:
mouse.set(x, y);
mousePath.reset();
mousePath.addRect(mouse.x - 10, mouse.y - 10, mouse.x + 10, mouse.y + 10, Path.Direction.CW);
I draw mousePath to see where the mousePath is and this is the result I get (it is the box in green and where the mouse actually is in the general area of the blue circle)
This is the much more severe point, as it seems the closer I get to (0,0) the closer mousePath gets to being at where the mouse actually is.
So how do I get the mouse to be in the correct location?
After more searching it turns out this question is a duplicate of: https://stackoverflow.com/a/24895485/4188097.
The browser always returns the mouse position in untransformed coordinates. Your drawings have been done in transformed space. If you want to know where your mouse is in transformed space, you can convert untransformed mouse coordinates to transformed coordinates like this:
var mouseXTransformed = (mouseX-panX) / scaleFactor;
var mouseYTransformed = (mouseY-panY) / scaleFactor;

How to rotate around the image center by itext?

double degPi = degrees * Math.PI / 180;
double a = Math.cos(degPi)*tImgCover.getScaledHeight();
double b = Math.sin(degPi)*tImgCover.getScaledWidth();
double c = -Math.sin(degPi) * tImgCover.getScaledHeight();
double d = Math.cos(degPi)* tImgCover.getScaledWidth();
double e = absX;
double f = absY;
contentByte.addImage(imgae, a, b, c, d, e, f);/*add image*/
How to rotate around the image center by itext?
If we have an Image image and coordinates x, y, we can draw the image without rotation with its lower left corner at the given coordinates like this
contentByte.addImage(image, image.getWidth(), 0, 0, image.getHeight(), x, y);
A bitmap image from the resources has a size of 1x1 with the coordinate origin at its lower left. Thus, this operation stretches the image to its correct size and moves it so its lower left is at the given coordinates.
If we want to draw the same image as if the one drawn above was rotated around its center by an angle rotate, therefore, we can do this by moving the 1x1 image so that the origin is in its center, stretch it to its correct size, rotate it, and then move the origin (which still is at the center of the rotated image) to the center of the unrotated image. These operations are easier to express using AffineTransform instances (from package com.itextpdf.awt.geom) instead number tupels. Thus:
// Draw image as if the previous image was rotated around its center
// Image starts out being 1x1 with origin in lower left
// Move origin to center of image
AffineTransform A = AffineTransform.getTranslateInstance(-0.5, -0.5);
// Stretch it to its dimensions
AffineTransform B = AffineTransform.getScaleInstance(image.getWidth(), image.getHeight());
// Rotate it
AffineTransform C = AffineTransform.getRotateInstance(rotate);
// Move it to have the same center as above
AffineTransform D = AffineTransform.getTranslateInstance(x + image.getWidth()/2, y + image.getHeight()/2);
// Concatenate
AffineTransform M = (AffineTransform) A.clone();
M.preConcatenate(B);
M.preConcatenate(C);
M.preConcatenate(D);
//Draw
contentByte.addImage(image, M);
(AddRotatedImage.java test method testAddRotatedImage)
For example drawing both images using
int x = 200;
int y = 300;
float rotate = (float) Math.PI / 3;
results in something like this:
With a Flip
The OP asked in a comment
how to add rotate and flip image?
For this you simply insert a mirroring affine transformation into the sequence of transformations above.
Unfortunately the OP did not mention which he meant a horizontal or a vertical flip. But as changing the rotation angle accordingly transforms one in the other, that isn't really necessary, either.
// Draw image as if the previous image was flipped and rotated around its center
// Image starts out being 1x1 with origin in lower left
// Move origin to center of image
AffineTransform A = AffineTransform.getTranslateInstance(-0.5, -0.5);
// Flip it horizontally
AffineTransform B = new AffineTransform(-1, 0, 0, 1, 0, 0);
// Stretch it to its dimensions
AffineTransform C = AffineTransform.getScaleInstance(image.getWidth(), image.getHeight());
// Rotate it
AffineTransform D = AffineTransform.getRotateInstance(rotate);
// Move it to have the same center as above
AffineTransform E = AffineTransform.getTranslateInstance(x + image.getWidth()/2, y + image.getHeight()/2);
// Concatenate
AffineTransform M = (AffineTransform) A.clone();
M.preConcatenate(B);
M.preConcatenate(C);
M.preConcatenate(D);
M.preConcatenate(E);
//Draw
contentByte.addImage(image, M);
(AddRotatedImage.java test method testAddRotatedFlippedImage)
The result with the same image as above:
With Interpolation
The OP asked in a yet another comment
How anti aliasing ?
The iText Image class knows an Interpolation property. By setting it to true (before adding the image to the document, obviously),
image.setInterpolation(true);
low resolution images are subject to interpolation when drawn.
E.g. using a 2x2 image with differently colored pixels instead of the image of Willi, you get the following results, first without interpolation, then with interpolation:
Confer the AddRotatedImage.java test testAddRotatedInterpolatedImage which adds this image:
Beware: iText Image property Interpolation effectively sets the Interpolate entry in the PDF image dictionary. The PDF specification notes in this context:
NOTE A conforming Reader may choose to not implement this feature of PDF, or may use any specific implementation of interpolation that it wishes.
Thus, on some viewers interpolation may occur differently than in your viewer, maybe even not at all. If you need a specific kind of interpolation on every viewer, upscale the image with the desired amount of interpolation / anti-aliasing before loading it into an iText Image.
public static BufferedImage rotateClockwise90( BufferedImage inputImage ){
int width = inputImage.getWidth();
int height = inputImage.getHeight();
BufferedImage returnImage = new BufferedImage( height, width , inputImage.getType() );
for( int x = 0; x < width; x++ ) {
for( int y = 0; y < height; y++ ) {
returnImage.setRGB( height-y-1, x, inputImage.getRGB( x, y ) );
}
}
return returnImage;
}

How to draw smooth continuous line in java swing that also varies in Size?

I am writing a 2D traditional animation program in Java using swing and JPen. The application works well. However, i am dissatisfied with the results I am getting when I draw lines.
Using the JPen API, the swing panel listens for the stylus input and its code is:
/**
* This method is called whenever the stylus makes contact with the tablet while inside of the JPanel functioning
* as the Drawing Canvas.
* #param evt
*/
public void penLevelEvent(PLevelEvent evt) {
// Get kind of event: does it come from mouse (CURSOR), STYLUS or ERASER?
PKind kind = evt.pen.getKind();
// Discard events from mouse
if (kind == PKind.valueOf(PKind.Type.CURSOR)){
//System.out.println("returning since this is only a mouse cursor");
return;
}
// Get the current cursor location
// position value is in with respect to entire application window
// Get the tilt values (not with a Bamboo... so untested!)
float curX = evt.pen.getLevelValue(PLevel.Type.X);
float curY = evt.pen.getLevelValue(PLevel.Type.Y);
float pressure = evt.pen.getLevelValue(PLevel.Type.PRESSURE);// 0.0 - 1.0
float xTilt = evt.pen.getLevelValue(PLevel.Type.TILT_X);
float yTilt = evt.pen.getLevelValue(PLevel.Type.TILT_Y);
// Set the brush's size, and darkness relative to the pressure
float darkness = 255 * pressure;
// Transform them to azimuthX and altitude, two angles with the projection of the pen against the X-Y plane
// azimuthX is the angle (clockwise direction) between this projection and the X axis. Range: -pi/2 to 3*pi/2.
// altitude is the angle between this projection and the pen itself. Range: 0 to pi/2.
// Might be more pratical to use than raw x/y tilt values.
double[] aa = { 0.0, 0.0 };
PLevel.Type.evalAzimuthXAndAltitude(aa, xTilt, yTilt);
// or just PLevel.Type.evalAzimuthXAndAltitude(aa, evt.pen);
double azimuthX = aa[0];
double altitude = aa[1];
//-------------------------------------------------------------------------------------
// If the stylus is being pressed down, we want to draw a black
// line onto the screen. If it's the eraser, we want to create
// a white line, effectively "erasing" the black line
//-------------------------------------------------------------------------------------
if (kind == PKind.valueOf(PKind.Type.STYLUS)) {
//System.out.println("Darkness "+darkness);
int alpha = 255 - (int)darkness;
color = new Color(0,0,255, 255 - alpha);
}
else if (kind == PKind.valueOf(PKind.Type.ERASER)) {
System.out.println("Handle eraser");
}
else {
return; // IGNORE or CUSTOM...
}
//If movement of the stylus is occuring
if (evt.isMovement()) {
//and the buttonIsDown(boolean)
if(buttonIsDown) {
//drawingCanvas:JPanel -> instruct the jpanel to draw at the following coordinate using the specified pressure
drawingCanvas.stylusMovementInput( prevXPos,prevYPos, curX,curY, pressure);
}
prevXPos = curX;
prevYPos = curY;
}
prevXPos = curX;
prevYPos = curY;
}
So after the above method is invoked, the jpanel(drawingCanvas) starts to draw on a BufferedImage by obtaining the image's graphics2D. Here is the code stylusMovementInput->calls -> performDrawOnBufferImageGraphic2D :
/**
* Draw on the active frame that is selected. Then call channel refresh, to refresh the the composite image derived
* from call changes related to the current frame
* #param cX current
* #param cY current
* #param oX previous
* #param oY previous
* #param pressure pressure 0 - 1f
*/
private void performDrawOnBufferImageGraphic2D(float oX, float oY, float cX, float cY, float pressure){
//Obtain the current layer that user wants to draw one
//MyImageData is encapsulating a BufferedImage
MyImageData $activeData = getActiveLayer();
//Exit if one is not valid
if( $activeData == null) return;
//if valid layer, get the, get the bufferedImage.getGraphics
Graphics2D $activeDataGFX = $activeData.getImageGraphics();
// Customize the drawing brush (create a BasicStroke)
Stroke thickness = Sys.makeStroke(getPencilSize(pressure), null);
// Determine the tranparency with respect to the pressure
int alpha = (int)(255 * pressure * getPencilOpacityPercentage());
// Get the current color found in the color wheel
Color cwVal = Sys.getColorFromColorWheel();
Color drawingColor ;
if(cwVal != null){
// add alpha value to it
drawingColor = new Color(cwVal.getRed(), cwVal.getGreen(), cwVal.getBlue(), alpha);
}else throw new RuntimeException("ColorWheel is null drawing stylus draw");
//set the brush and drawingColor
$activeDataGFX.setStroke(thickness);
// Save reference to the current bufferedImage graphic component
Composite originalComposite =$activeDataGFX.getComposite();
if(getCurrentTool() == DrawingCanvasTool.ERASER){
//If eraser, set new composite information, to allow erasing to transparency
$activeDataGFX.setPaint( new Color(255,255,255, 0));
$activeDataGFX.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_IN, 0.0f));
}else {
//set the drawing color
$activeDataGFX.setPaint(drawingColor);
}
//---------------------------------------------------------------
// Rotate, Translate, Zoom the image according to the panning, zoom, and rotate
// set by the user
//---------------------------------------------------------------
//Figure out the canvas center, as it is used for rotating
float theta = (float)Math.toRadians(canvasRotation);
Dimension drawingAreaComponentSize = getSize();
float centerX = drawingAreaComponentSize.width/2;
float centerY = drawingAreaComponentSize.height/2;
AffineTransform transform = new AffineTransform();
transform.rotate(-theta, centerX, centerY);
transform.scale(1.0f / canvasZoom, 1.0f / canvasZoom);//erase
transform.translate(-canvasPan.x, -canvasPan.y);//erase
$activeDataGFX.setTransform(transform);
//Now Draw inside of the active data graphics object
Shape line = new Line2D.Float(cX,cY, oX, oY);
Path2D.Float t = new Path2D.Float(line);
$activeDataGFX.draw(t);
//drawing is complete
if(getCurrentTool() ==DrawingCanvasTool.ERASER){
//Restore the old composite object
$activeDataGFX.setComposite(originalComposite);
}
//Refresh basically merges frames along a frame column into a single preview buffered image
//which will later be used to view the tool animation when user clicks "play" button
channelPannel.refreshFrameOut( channelPannel.getCurrentFrame() );
}
I commented a lot of the code, and provided the critical points related to the question. Any help is much appreciated. And again, the problem is how do i draw a smooth line worthy of a descent drawing program.

Categories