I have cloning the properties of entities for copy, paste. The context menu is implemented to cut, copy, paste the things. But the ellipse is not pasted at the correct position. It is pasted at some distance.
Following is my ellipse class.
include "ellipse.h"
Ellipse::Ellipse(int i, QPointF point1, QPointF point2, QPointF point3)
{
// assigns id
id = i;
/* set values of three points
and calculate radii of ellipse */
p1 = point1;
p2 = point2;
p3 = point3;
d12 = qSqrt(qPow((p2.x()-p1.x()), 2) + qPow((p2.y()-p1.y()), 2));
d13 = qSqrt(qPow((p3.x()-p1.x()), 2) + qPow((p3.y()-p1.y()), 2));
if(d12>d13)
{
majRadius = d12;
minRadius = d13;
theta = atan2((p2.y()-p1.y()),(p2.x()-p1.x())) * (180/M_PI);
}
else
{
majRadius = d13;
minRadius = d12;
theta = atan2((p3.y()-p1.y()),(p3.x()-p1.x())) * (180/M_PI);
}
}
Ellipse::Ellipse(int i, QPointF point1, qreal rad, qreal radM)
{
// assigns id
id = i;
/* set values of center point
and radii of ellipse */
p1 = point1;
minRadius = rad;
majRadius = radM;
}
int Ellipse::type() const
{
// Enable the use of qgraphicsitem_cast with ellipse item.
return Type;
}
QRectF Ellipse::boundingRect() const
{
// bounding rectangle for ellipse
float topLeftX = majRadius * cos(theta);
float topLeftY = majRadius * sin(theta);
float bottomRightX = minRadius * cos(theta + M_PI/2);
float bottomRightY = minRadius * sin(theta + M_PI/2);
float halfWidth = sqrt((topLeftX * topLeftX) + (bottomRightX * bottomRightX));
float halfHeight = sqrt((topLeftY * topLeftY) + (bottomRightY * bottomRightY));
return QRectF(p1.x() - 1.5 * halfWidth, p1.y() - 1.5 * halfHeight,
3 * halfWidth, p1.y() + 3 * halfHeight);
}
void Ellipse::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
QWidget *widget)
{
// draws/paints the path of ellipse
QPen paintpen;
paintpen.setWidth(1);
painter->setRenderHint(QPainter::Antialiasing);
if (isSelected())
{
// sets brush for center point
painter->setBrush(Qt::SolidPattern);
paintpen.setColor(Qt::red);
painter->setPen(paintpen);
painter->drawEllipse(p1, 2, 2);
painter->save();
painter->translate(p1.x(), p1.y());
painter->rotate(theta);
painter->translate(-p1.x(), -p1.y());
// sets pen for circumference
paintpen.setStyle(Qt::DashLine);
paintpen.setColor(Qt::black);
painter->setBrush(Qt::NoBrush);
painter->setPen(paintpen);
painter->drawEllipse(p1, majRadius, minRadius);
painter->restore();
}
else
{
painter->setBrush(Qt::SolidPattern);
painter->drawEllipse(p1, 2, 2);
painter->save();
painter->translate(p1.x(), p1.y());
painter->rotate(theta);
painter->translate(-p1.x(), -p1.y());
painter->setBrush(Qt::NoBrush);
painter->setPen(paintpen);
painter->drawEllipse(p1, majRadius, minRadius);
painter->restore();
}
}
getEntity *Ellipse::clone()
{
Ellipse *e = new Ellipse;
e->p1 = p1;
e->p2 = p2;
e->p3 = p3;
e->majRadius = majRadius;
e->minRadius = minRadius;
return e;
}
getEntity *Ellipse::clone()
{
Ellipse *e = new Ellipse;
e->p1.x();
e->p1.y();
e->p2.x();
e->p2.y();
e->p3.x();
e->p3.y();
e->majRadius = majRadius;
e->minRadius = minRadius;
return e;
}
Related
I have a TriangleMesh with a texture/diffuse map that is a 1024x1024 texture which is fully black except the last 3 lines which are filled with red, green and blue.
I gave each vertex of each triangle a constant V value (either 1021, 1022 or 1023) added 0.5 to it to center and divided it by the texture's height so it would only use one of the 3 colors and arbitrary U values.
// arbitrary u values.
float u1 = 0.1f;
float u2 = 0.3f;
float u3 = 0.9f;
int randomY = ThreadLocalRandom.current()
.nextInt((int) atlas.getHeight() - 3, (int) atlas.getHeight());
float y = randomY + 0.5f;
float v = (float) (y / atlas.getHeight());
int texIndex1 = mesh.addUV(u1, v);
int texIndex2 = mesh.addUV(u2, v);
int texIndex3 = mesh.addUV(u3, v);
mesh.getFaces().addAll(
vertexIndex1, texIndex1,
vertexIndex2, texIndex2,
vertexIndex3, texIndex3
);
The addUV method looks like this(i have my own mesh class that extends TriangleMesh which contains some helper methods)
public int addUV(float u, float v) {
int cur = getTexCoords().size() / 2;
getTexCoords().addAll(u, v);
return cur;
}
The expected result is a mesh that has it's triangles colored solid red, green and blue because V is constant and each line(y) is filled with a single color however what i got instead was a bunch of different colors that change as u zoom in/out.
If i use the same U value for each vertex as well, it does give the correct result but i don't understand why it wouldn't do the same with arbitrary U values given that the color at any given U is the exact same.
The current result(gif to show the color changing): https://i.imgur.com/4lTcLfH.gif | As seen it actually does show the correct colors but only if u zoom in a lot
The expected result(can be produced if i have constant U values as well like 0.5, 0.5, 0.5): https://i.imgur.com/x35u6xv.gif | Looks as it should, doesn't change when u zoom in/out
The texture i used as the diffuse map: https://i.imgur.com/BB6P7z6.png
Minimal reproducible example with a quad made of 2 triangles:
Create a main method (either in the same class or another) and add: Application.launch(TextureMappingIssue.class); i didn't add it in my example as depending on the setup, the main method must be in a different class
import javafx.application.Application;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.*;
import javafx.scene.image.Image;
import javafx.scene.image.PixelWriter;
import javafx.scene.image.WritableImage;
import javafx.scene.layout.AnchorPane;
import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.MeshView;
import javafx.scene.shape.TriangleMesh;
import javafx.scene.transform.Rotate;
import javafx.scene.transform.Scale;
import javafx.scene.transform.Translate;
import javafx.stage.Stage;
import javax.imageio.ImageIO;
import java.nio.file.Path;
/**
* Note that when the application is first opened without
* the camera moved, it looks as it should, as soon as
* the camera is moved (i.e if the mouse is moved in this case)
* it looks completely different, even if the camera is moved slightly
*/
public class TextureMappingIssue extends Application {
private static final int WIDTH = 800;
private static final int HEIGHT = 600;
private AnchorPane modelPane;
private Group scene;
private SubScene subScene;
#Override
public void start(Stage primaryStage) throws Exception {
modelPane = new AnchorPane();
modelPane.setPrefWidth(WIDTH);
modelPane.setPrefHeight(HEIGHT);
initScene();
// smaller palette = looks correct until u zoom out more
int paletteWidth = 1024;
int paletteHeight = 1024;
/*
* amount of copies for red, green, blue(the colors at the bottom), the center one is picked
* note that copies = 1 just writes the original color, copies = 2 writes the original + 1 copy and so on (so with copies = 3, it writes the color 3 times and picks the 2nd one for v)
*/
int copies = 1;
float QUAD_SCALE = 1f;
float[] vertices = {
-QUAD_SCALE, -QUAD_SCALE, 0,
-QUAD_SCALE, QUAD_SCALE, 0,
QUAD_SCALE, QUAD_SCALE, 0,
QUAD_SCALE, -QUAD_SCALE, 0
};
int[] indices = {
0, 0, 1, 1, 2, 2, // first triangle
0, 3, 2, 4, 3, 5, // second triangle
};
// set these to 0f, 0f, 0f (or any value as long as they're identical to get the expected result)
float u1 = 0.1f;
float u2 = 0.3f;
float u3 = 0.5f;
int colorIndex = 1; // either 0, 1 or 2 (red, green, blue)
int offset = (3 - colorIndex) * copies;
// v is constant for each vertex in my actual application as well.
float v1 = (paletteHeight - offset + (copies / 2) + 0.5f) / paletteHeight;
float[] texCoords = {
u1, v1, u2, v1, u3, v1
};
Image palette = generatePalette(paletteWidth, paletteHeight, copies);
ImageIO.write(SwingFXUtils.fromFXImage(palette, null), "png", Path.of("./testpalette.png")
.toFile());
TriangleMesh triangle = new TriangleMesh();
triangle.getPoints().addAll(vertices);
triangle.getFaces().addAll(indices);
triangle.getTexCoords().addAll(texCoords);
triangle.getTexCoords().addAll(texCoords);
MeshView view = new MeshView(triangle);
PhongMaterial material = new PhongMaterial();
material.setDiffuseMap(palette);
//material.setSpecularMap(specular);
// material.setSpecularPower(32); // default
view.setMaterial(material);
scene.getChildren().add(view);
Scene scene = new Scene(modelPane, WIDTH, HEIGHT, true, SceneAntialiasing.BALANCED);
scene.setFill(Color.BLACK);
primaryStage.setScene(scene);
primaryStage.show();
}
private void initScene() {
scene = new Group();
//Group grid = new Grid3D().create(48f, 1.25f);
//scene.getChildren().add(grid);
subScene = createScene3D();
scene.getChildren().add(new AmbientLight(Color.WHITE));
modelPane.getChildren().addAll(subScene);
}
private SubScene createScene3D() {
SubScene scene3d = new SubScene(scene, modelPane.getPrefWidth(), modelPane.getPrefHeight(), true, SceneAntialiasing.BALANCED);
scene3d.setFill(Color.rgb(25, 25, 25));
new OrbitCamera(scene3d, scene);
return scene3d;
}
private Image generatePalette(int width, int height, int copies) {
WritableImage palette = new WritableImage(width, height);
Color[] debugColors = {Color.RED, Color.GREEN, Color.BLUE};
PixelWriter writer = palette.getPixelWriter();
int offset = height - (debugColors.length * copies);
for (int y = 0; y < offset; y++) {
for (int x = 0; x < width; x++) {
writer.setColor(x, y, Color.BLACK);
}
}
int colorOff = 0;
for (int y = offset; y < height - (copies - 1); y += copies) {
Color c = debugColors[colorOff];
if (c == Color.GREEN) {
System.out.println("Y = " + y);
}
for (int k = 0; k < copies; k++) {
for (int x = 0; x < width; x++) {
writer.setColor(x, y + k, c);
}
}
colorOff++;
}
return palette;
}
private Image generateSpecular(int width, int height) {
WritableImage specular = new WritableImage(width, height);
PixelWriter writer = specular.getPixelWriter();
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
writer.setColor(x, y, Color.WHITE);
}
}
return specular;
}
/*
* Orbit camera
*/
private static class OrbitCamera {
private final SubScene subScene;
private final Group root3D;
private final double MAX_ZOOM = 300.0;
public OrbitCamera(SubScene subScene, Group root) {
this.subScene = subScene;
this.root3D = root;
init();
}
private void init() {
camera.setNearClip(0.1D);
camera.setFarClip(MAX_ZOOM * 1.15D);
camera.getTransforms().addAll(
yUpRotate,
cameraPosition,
cameraLookXRotate,
cameraLookZRotate
);
Group rotateGroup = new Group();
try {
rotateGroup.getChildren().addAll(cameraXform);
} catch (Exception e) {
e.printStackTrace();
}
cameraXform.ry.setAngle(0);
cameraXform.rx.setAngle(-18);
cameraXform.getChildren().add(cameraXform2);
cameraXform2.getChildren().add(cameraXform3);
cameraXform3.getChildren().add(camera);
cameraPosition.setZ(-cameraDistance);
root3D.getChildren().addAll(rotateGroup);
subScene.setCamera(camera);
subScene.setOnScroll(event -> {
double zoomFactor = 1.05;
double deltaY = event.getDeltaY();
if (deltaY < 0) {
zoomFactor = 2.0 - zoomFactor;
}
double z = cameraPosition.getZ() / zoomFactor;
z = Math.max(z, -MAX_ZOOM);
z = Math.min(z, 10.0);
cameraPosition.setZ(z);
});
subScene.setOnMousePressed(event -> {
if (!event.isAltDown()) {
dragStartX = event.getSceneX();
dragStartY = event.getSceneY();
dragStartRotateX = cameraXRotate.getAngle();
dragStartRotateY = cameraYRotate.getAngle();
mousePosX = event.getSceneX();
mousePosY = event.getSceneY();
mouseOldX = event.getSceneX();
mouseOldY = event.getSceneY();
}
});
subScene.setOnMouseDragged(event -> {
if (!event.isAltDown()) {
double modifier = 1.0;
double modifierFactor = 0.3;
if (event.isControlDown()) modifier = 0.1;
if (event.isSecondaryButtonDown()) modifier = 0.035;
mouseOldX = mousePosX;
mouseOldY = mousePosY;
mousePosX = event.getSceneX();
mousePosY = event.getSceneY();
mouseDeltaX = mousePosX - mouseOldX;
mouseDeltaY = mousePosY - mouseOldY;
double flip = -1.0;
if (event.isSecondaryButtonDown()) {
double newX = cameraXform2.t.getX() + flip * mouseDeltaX * modifierFactor * modifier * 2.0;
double newY = cameraXform2.t.getY() + 1.0 * -mouseDeltaY * modifierFactor * modifier * 2.0;
cameraXform2.t.setX(newX);
cameraXform2.t.setY(newY);
} else if (event.isPrimaryButtonDown()) {
double yAngle = cameraXform.ry.getAngle() - 1.0 * -mouseDeltaX * modifierFactor * modifier * 2.0;
double xAngle = cameraXform.rx.getAngle() + flip * mouseDeltaY * modifierFactor * modifier * 2.0;
cameraXform.ry.setAngle(yAngle);
cameraXform.rx.setAngle(xAngle);
}
}
});
}
private final PerspectiveCamera camera = new PerspectiveCamera(true);
private final Rotate cameraXRotate = new Rotate(-20.0, 0.0, 0.0, 0.0, Rotate.X_AXIS);
private final Rotate cameraYRotate = new Rotate(-20.0, 0.0, 0.0, 0.0, Rotate.Y_AXIS);
private final Rotate cameraLookXRotate = new Rotate(0.0, 0.0, 0.0, 0.0, Rotate.X_AXIS);
private final Rotate cameraLookZRotate = new Rotate(0.0, 0.0, 0.0, 0.0, Rotate.Z_AXIS);
private final Translate cameraPosition = new Translate(0.0, 0.0, 0.0);
private Xform cameraXform = new Xform();
private Xform cameraXform2 = new Xform();
private Xform cameraXform3 = new Xform();
private double cameraDistance = 25.0;
private double dragStartX = 0;
private double dragStartY = 0;
private double dragStartRotateX = 0;
private double dragStartRotateY = 0;
private double mousePosX = 0;
private double mousePosY = 0;
private double mouseOldX = 0;
private double mouseOldY = 0;
private double mouseDeltaX = 0;
private double mouseDeltaY = 0;
private Rotate yUpRotate = new Rotate(0.0, 0.0, 0.0, 0.0, Rotate.X_AXIS);
public Camera getCamera() {
return camera;
}
public Xform getCameraXform() {
return cameraXform;
}
}
private static class Xform extends Group {
Translate t = new Translate();
Translate p = new Translate();
public Rotate rx = new Rotate();
public Rotate ry = new Rotate();
Rotate rz = new Rotate();
Scale s = new Scale();
public Xform() {
rx.setAxis(Rotate.X_AXIS);
ry.setAxis(Rotate.Y_AXIS);
rz.setAxis(Rotate.Z_AXIS);
getTransforms().addAll(t, rz, ry, rx, s);
}
}
}
Edit: updated the code to support generating multiple copies of a single color and picking the center one, however this doesn't solve the issue either, it's just less visible :/
Update: the issue can be reproduced even with a 128x3 image (where it's just the red, green, blue color with 128 pixel rows)
Update 2: I can reproduce the same issue in my original code even with my original palette (that is a 128x512 image of colors that are all potentially used)
Update 3: I have decided to go for per pixel shading instead (i.e provide my mesh with a set of normals and add light sources to the scene(other than ambient)) what i wanted to do initially with the palette was to export all the vertex colors generated from a function that emulates gouraud shading but because of these interpolation issues i have went for per pixel shading (which looks better anyway, altho ideally i would've wanted to emulate gouraud shading as the game engine i use for which the javafx program is for also uses gouraud shading)
I want to draw millimeter paper into a pdf, but when I measure the printed document I'm a bit off (drawn cm < 1 cm). I'm using the size of an A4 Paper (210 * 297) and the pageWidth and pageHeight to calculate the pixel per mm (used the average of both hoping this would work). I also tried different option when printing the document (with & without margin etc.), but this didn't work as well.
public class TestPrint extends Application {
protected static final float DINA4_IN_MM_WIDTH = 210;
protected static final float DINA4_IN_MM_HEIGHT = 297;
protected static final int LEFTSIDE = 35;
protected static final int TEXT_FONT_SIZE = 11;
protected static final int TOP_MARGIN = 60;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
File file = new File("test.pdf");
double windowWidth = 600;
double windowHeight = 1000;
float x = LEFTSIDE;
PDFont font = PDType1Font.TIMES_ROMAN;
PDPage page = new PDPage(PDRectangle.A4);
PDRectangle pageSize = page.getMediaBox();
PDDocument mainDocument = new PDDocument();
mainDocument.addPage(page);
float stringHeight = font.getFontDescriptor().getFontBoundingBox().getHeight() * TEXT_FONT_SIZE;
float y = pageSize.getHeight() - stringHeight / 1000f - TOP_MARGIN;
float pixelPerMM = (pageSize.getWidth() / DINA4_IN_MM_WIDTH + pageSize.getHeight() / DINA4_IN_MM_HEIGHT) / 2;
float displayW = 520;
float displayH = 300;
try {
PDPageContentStream contents = new PDPageContentStream(mainDocument, page, AppendMode.APPEND, true);
drawBackgroundRaster(contents, x, y, displayW, displayH, pixelPerMM);
contents.close();
mainDocument.save(file);
ImageView imgView = getImageViewFromDocument(mainDocument, windowHeight);
VBox vBox = new VBox(imgView);
Scene scene = new Scene(vBox, windowWidth, windowHeight);
primaryStage.setScene(scene);
primaryStage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
// draw millimeter paper with dots, two boxes shall be 1cm
private void drawBackgroundRaster(PDPageContentStream contents, float x, float y, float displayW, float displayH,
float pixelPerMM) throws IOException {
// rasterColor = grey
Color rasterColor = new Color(175, 175, 175);
contents.setStrokingColor(rasterColor);
float dotSize = 0.5f;
// draw vertical lines
for (int i = 0; i <= displayW; i++) {
float xPos = x + i * pixelPerMM;
if (xPos > displayW + x) {
break;
}
contents.moveTo(xPos, y);
if (i % 5 == 0) {
contents.setLineDashPattern(new float[] {}, 0);
contents.lineTo(xPos, y - displayH);
}
contents.stroke();
}
// draw dots and horizontal lines
for (int i = 0; i <= displayH; i++) {
float yPos = y - i * pixelPerMM;
if (yPos < y - displayH) {
break;
}
contents.moveTo(x, yPos);
if (i % 5 == 0) {
contents.setLineDashPattern(new float[] {}, 0);
contents.lineTo(x + displayW, yPos);
} else {
contents.setLineDashPattern(new float[] { dotSize, pixelPerMM - dotSize }, dotSize / 2);
contents.lineTo(x + displayW, yPos);
}
contents.stroke();
}
contents.setLineDashPattern(new float[] {}, 0);
contents.moveTo(x, y);
contents.lineTo(x + displayW, y);
contents.lineTo(x + displayW, y - displayH);
contents.lineTo(x, y - displayH);
contents.lineTo(x, y);
contents.stroke();
}
private ImageView getImageViewFromDocument(PDDocument mainDocument, double windowHeight) throws IOException {
PDFRenderer pdfRenderer = new PDFRenderer(mainDocument);
BufferedImage bim = pdfRenderer.renderImageWithDPI(0, 150, ImageType.RGB);
Image image = SwingFXUtils.toFXImage(bim, null);
ImageView imageView = new ImageView(image);
double scaleFactor = windowHeight / imageView.getImage().getHeight();
double zoomFactor = scaleFactor * 2d * 2.55d / 3;
double width = imageView.getImage().getWidth() * zoomFactor;
double height = imageView.getImage().getHeight() * zoomFactor;
imageView.setFitWidth(width);
imageView.setFitHeight(height);
return imageView;
}
}
Trying to create dynamically a series of circles with Javafx. After typing the number of circles i got this:
But actually i want that my circles be in that position:
Here is my code and thanks for any hints!!
int k = 5;
for (int i = 0; i < nbNoeuds; i++) {
Noeudfx circle = new Noeudfx(k * 2, k * 2, 1, String.valueOf(i));
Label id = new Label(String.valueOf(i));
noeuds.getChildren().add(id);
id.setLayoutX(k * 2 - 20);
id.setLayoutY(k * 2 - 20);
id.setBlendMode(BlendMode.DIFFERENCE);
k += 10;
FillTransition ft1 = new FillTransition(Duration.millis(300), circle, Color.RED, Color.BLACK);
ft1.play();
noeuds.getChildren().add(circle);
ScaleTransition tr = new ScaleTransition(Duration.millis(100), circle);
tr.setByX(10f);
tr.setByY(10f);
tr.setInterpolator(Interpolator.EASE_OUT);
tr.play();
}
}
public class Noeudfx extends Circle {
Noeud noeud;
Point point;
Label distance = new Label("distance : infinite");
boolean isSelected = false;
List<Noeudfx> circles = new ArrayList<>();
public Noeudfx(double a, double b, double c, String nom) {
super(a, b, c);
noeud = new Noeud(nom, this);
point = new Point((int) a, (int) b);
circles.add(this);
}
}
Here is my solution:
int nbNoeuds = Integer.parseInt(nodeID.getText());
System.out.println("nnnnn"
+ nbNoeuds);
final Timeline animation = new Timeline(
new KeyFrame(Duration.seconds(.5), (ActionEvent actionEvent) -> {
while (noeuds.getChildren().size() <= nbNoeuds) {
// noeuds.getChildren().remove(0);
int radius =10 ;
noeuds.getChildren().add(
new Circle(
rnd.nextInt(SCENE_SIZE - radius * 2) + radius, rnd.nextInt(SCENE_SIZE - radius * 2) + radius,
radius,
Color.GRAY
)
);
}
})
);
animation.setCycleCount(Animation.INDEFINITE);
animation.play();
animation.setOnFinished((ActionEvent actionevent) -> {
animation.stop();
});
Update: i tried to add label to each circle, the problem was that the number of circles in the screen is not correct i don't know why!
Label id = new Label(String.valueOf(i));
id.setTextFill(Color.CADETBLUE);
id.setAlignment(Pos.CENTER);
Circle circle = new Circle(
rnd.nextInt(SCENE_SIZE - radius * 2) + radius, rnd.nextInt(SCENE_SIZE - radius * 2) + radius,
radius,
Color.GRAY
);
Double a = circle.getCenterX();
Double b = circle.getCenterY();
id.setLayoutX(a - 20);
id.setLayoutY(b - 20);
id.setBlendMode(BlendMode.DIFFERENCE);
noeuds.getChildren().add(id);
noeuds.getChildren().add(circle);
I have an arc inherited from QGraphicsItem, which is making use of virtual clone() to allow the editing operations i.e cut, copy and paste. When the arc is pasted/cloned it is displaced from the correct positions (using the context menu position). Here is my Arc class as well as the clone() function:
#include "arc.h"
#include "qmath.h"
#include <QPen>
#include <QPainterPath>
Arc::Arc(int i, QPointF point1, QPointF point2, QPointF point3)
{
id = i;
p1 = point1;
p2 = point2;
p3 = point3;
init();
}
int Arc::type() const
{
// Enable the use of qgraphicsitem_cast with arc item.
return Type;
}
Arc::Arc(QPointF point1, QPointF point2, QPointF point3)
{
p1 = point1;
p2 = point2;
p3 = point3;
p1 = QPointF(0,0);
p2 = QPointF(0,1);
p3 = QPointF(1,1);
init();
}
// Calculates startangle and spanangle
void Arc::init()
{
lineBC = QLineF(p2, p3);
lineAC = QLineF(p1, p3);
lineBA = QLineF(p2, p1);
rad = qAbs(lineBC.length()/(2 * qSin(qDegreesToRadians
(lineAC.angleTo(lineBA)))));
bisectorBC = QLineF(lineBC.pointAt(0.5), lineBC.p2());
bisectorBC.setAngle(lineBC.normalVector().angle());
bisectorBA = QLineF(lineBA.pointAt(0.5), lineBA.p2());
bisectorBA.setAngle(lineBA.normalVector().angle());
bisectorBA.intersect(bisectorBC, ¢er);
circle = QRectF(center.x() - rad, center.y() - rad, rad * 2, rad * 2);
lineOA = QLineF(center, p1);
lineOB = QLineF(center, p2);
lineOC = QLineF(center, p3);
startAngle = lineOA.angle();
spanAngle = lineOA.angleTo(lineOC);
/**
* Make sure that the span angle covers all three points with the
* second point in the middle
*/
if (qAbs(spanAngle) < qAbs(lineOA.angleTo(lineOB)) ||
qAbs(spanAngle) < qAbs(lineOB.angleTo(lineOC)))
{
// swap the end point and invert the spanAngle
startAngle = lineOC.angle();
spanAngle = 360 - spanAngle;
}
int w = 10;
boundingRectTemp = circle.adjusted(-w, -w, w, w);
}
QRectF Arc::boundingRect() const
{
// outer most edges
return boundingRectTemp;
}
void Arc::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
QWidget *widget)
{
QPen paintpen;
painter->setRenderHint(QPainter::Antialiasing);
paintpen.setWidth(1);
// Draw arc
if (isSelected())
{
// sets brush for end points
painter->setBrush(Qt::SolidPattern);
paintpen.setColor(Qt::red);
painter->setPen(paintpen);
painter->drawEllipse(p1, 2, 2);
painter->drawEllipse(p2, 2, 2);
painter->drawEllipse(p3, 2, 2);
// sets pen for arc path
paintpen.setStyle(Qt::DashLine);
paintpen.setColor(Qt::black);
painter->setPen(paintpen);
}
else
{
painter->setBrush(Qt::SolidPattern);
paintpen.setColor(Qt::black);
painter->setPen(paintpen);
painter->drawEllipse(p1, 2, 2);
painter->drawEllipse(p2, 2, 2);
painter->drawEllipse(p3, 2, 2);
}
QPainterPath path;
path.arcMoveTo(circle, startAngle);
path.arcTo(circle, startAngle, spanAngle);
painter->setBrush(Qt::NoBrush);
painter->drawPath(path);
}
getEntity *Arc::clone()
{
Arc *a = new Arc;
a->p1 = p1;
a->p2 = p2;
a->p3 = p3;
a->lineAC = AC;
a->lineBA = lineBA;
a->lineBC = lineBC;
a->lineOA = lineOA;
a->lineOB = lineOB;
a->lineOC = lineOC;
a->bisectorBA = bisectorBA;
a->bisectorBC = bisectorBC;
a->center = center;
a->circle = circle;
a->startAngle = startAngle;
a->spanAngle = spanAngle;
return a;
}
#ifndef GETENTITY_H
#define GETENTITY_H
#include <QGraphicsItem>
class getEntity : public QObject, public QGraphicsItem
{
public:
getEntity(QObject *parent = 0) : QObject(parent) {}
virtual ~getEntity() {}
virtual getEntity *clone(int i)
{
return 0;
}
int id;
};
#endif // GENTITY_H
I want to create complex shape like human bodys for my game project. Can I create complex shape in javafx 8 with trianglemesh. and also i read, it is possible to import 3ds model to javafx. can any one tell me how to import them and do things like rotate move by javafx code
thanks for your help
Also, If you go to Oracle, a 3DViewer is in the jfx samples which will export the 3d file to FXML, thus creating appropriate Groups ... Also being able to re-import the FXML into the Viewer...
Samples ...
Although as the above comment stated, InteractiveMesh has some nice tools.
Thats the easy way, if you already have a 3d file... otherwise the point,texCoord, and face arrays can be quite daunting.
It took me quite a while to create a torus .. points were fine, texCoords were fine, the faces are a pain...
this was just for my customizable torus
/*
Let the radius from the center of the hole to the center of the torus tube be "c",
and the radius of the tube be "a".
Then the equation in Cartesian coordinates for a torus azimuthally symmetric about the z-axis is
(c-sqrt(x^2+y^2))^2+z^2=a^2
and the parametric equations are
x = (c + a * cos(v)) * cos(u)
y = (c + a * cos(v)) * sin(u)
z = a * sin(v)
(for u,v in [0,2pi).
Three types of torus, known as the standard tori, are possible,
depending on the relative sizes of a and c. c>a corresponds to the ring torus (shown above),
c=a corresponds to a horn torus which is tangent to itself at the point (0, 0, 0),
and c<a corresponds to a self-intersecting spindle torus (Pinkall 1986).
*/
public static TriangleMesh createToroidMesh(float radius, float tRadius, int tubeDivisions, int radiusDivisions) {
int POINT_SIZE = 3, TEXCOORD_SIZE = 2, FACE_SIZE = 6;
int numVerts = tubeDivisions * radiusDivisions;
int faceCount = numVerts * 2;
float[] points = new float[numVerts * POINT_SIZE],
texCoords = new float[numVerts * TEXCOORD_SIZE];
int[] faces = new int[faceCount * FACE_SIZE],
smoothingGroups;
int pointIndex = 0, texIndex = 0, faceIndex = 0, smoothIndex = 0;
float tubeFraction = 1.0f / tubeDivisions;
float radiusFraction = 1.0f / radiusDivisions;
float x, y, z;
int p0 = 0, p1 = 0, p2 = 0, p3 = 0, t0 = 0, t1 = 0, t2 = 0, t3 = 0;
// create points
for (int tubeIndex = 0; tubeIndex < tubeDivisions; tubeIndex++) {
float radian = tubeFraction * tubeIndex * 2.0f * 3.141592653589793f;
for (int radiusIndex = 0; radiusIndex < radiusDivisions; radiusIndex++) {
float localRadian = radiusFraction * radiusIndex * 2.0f * 3.141592653589793f;
points[pointIndex] = x = (radius + tRadius * ((float) Math.cos(radian))) * ((float) Math.cos(localRadian));
points[pointIndex + 1] = y = (radius + tRadius * ((float) Math.cos(radian))) * ((float) Math.sin(localRadian));
points[pointIndex + 2] = z = (tRadius * (float) Math.sin(radian));
pointIndex += 3;
float r = radiusIndex < tubeDivisions ? tubeFraction * radiusIndex * 2.0F * 3.141592653589793f : 0.0f;
texCoords[texIndex] = (0.5F + (float) (Math.sin(r) * 0.5D));;
texCoords[texIndex + 1] = ((float) (Math.cos(r) * 0.5D) + 0.5F);
texIndex += 2;
}
}
//create faces
for (int point = 0; point < (tubeDivisions) ; point++) {
for (int crossSection = 0; crossSection < (radiusDivisions) ; crossSection++) {
p0 = point * radiusDivisions + crossSection;
p1 = p0 >= 0 ? p0 + 1 : p0 - (radiusDivisions);
p1 = p1 % (radiusDivisions) != 0 ? p0 + 1 : p0 - (radiusDivisions - 1);
p2 = (p0 + radiusDivisions) < ((tubeDivisions * radiusDivisions)) ? p0 + radiusDivisions : p0 - (tubeDivisions * radiusDivisions) + radiusDivisions ;
p3 = p2 < ((tubeDivisions * radiusDivisions) - 1) ? p2 + 1 : p2 - (tubeDivisions * radiusDivisions) + 1;
p3 = p3 % (radiusDivisions) != 0 ? p2 + 1 : p2 - (radiusDivisions - 1);
t0 = point * (radiusDivisions) + crossSection;
t1 = t0 >= 0 ? t0 + 1 : t0 - (radiusDivisions);
t1 = t1 % (radiusDivisions) != 0 ? t0 + 1 : t0 - (radiusDivisions - 1);
t2 = (t0 + radiusDivisions) < ((tubeDivisions * radiusDivisions)) ? t0 + radiusDivisions : t0 - (tubeDivisions * radiusDivisions) + radiusDivisions ;
t3 = t2 < ((tubeDivisions * radiusDivisions) - 1) ? t2 + 1 : t2 - (tubeDivisions * radiusDivisions) + 1;
t3 = t3 % (radiusDivisions) != 0 ? t2 + 1 : t2 - (radiusDivisions - 1);
try {
faces[faceIndex] = (p2);
faces[faceIndex + 1] = (t3);
faces[faceIndex + 2] = (p0);
faces[faceIndex + 3] = (t2);
faces[faceIndex + 4] = (p1);
faces[faceIndex + 5] = (t0);
faceIndex += FACE_SIZE;
faces[faceIndex] = (p2);
faces[faceIndex + 1] = (t3);
faces[faceIndex + 2] = (p1);
faces[faceIndex + 3] = (t0);
faces[faceIndex + 4] = (p3);
faces[faceIndex + 5] = (t1);
faceIndex += FACE_SIZE;
} catch (Exception e) {
e.printStackTrace();
}
}
}
TriangleMesh localTriangleMesh = new TriangleMesh();
localTriangleMesh.getPoints().setAll(points);
localTriangleMesh.getTexCoords().setAll(texCoords);
localTriangleMesh.getFaces().setAll(faces);
return localTriangleMesh;
}
Consider it to be a personal opinion.
Though, I don't question the power of Javafx and the possibility to build complex 3d models using Javafx, I would not suggest building such complex structures using it, when you can easily achieve them using AutoDesk and other softwares, with less effort !
You have options to import them in your Javafx applications using early access available here
http://www.interactivemesh.org/models/jfx3dimporter.html
This seems to be very promising !
have a look at this as well
http://www.interactivemesh.org/models/jfx3dbrowser.html
I made a 2x6 in sketchup and imported it along with the chick/dude/androgyne. He/she/it is only 2D. You need to get the jar file (jimColModelImporterJFX.jar) here.
import com.interactivemesh.jfx.importer.Viewpoint;
import com.interactivemesh.jfx.importer.col.ColAsset;
import com.interactivemesh.jfx.importer.col.ColModelImporter;
//import com.interactivemesh.jfx.importer.stl.StlMeshImporter;
import javafx.application.Application;
import javafx.scene.*;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.transform.Rotate;
import javafx.scene.transform.Translate;
import java.io.File;
import java.util.Map;
public class Importer3D extends Application {
//you'll have to make your own file in sketchup and save it as *.dae
//http://help.sketchup.com/en/article/114347
private static final String DAE_FILENAME = ("2x6stud-1.dae");
private static final int VIEWPORT_SIZE = 800;
private Group root;
public Rotate rx = new Rotate();
{ rx.setAxis(Rotate.X_AXIS); }
public Rotate ry = new Rotate();
{ ry.setAxis(Rotate.Y_AXIS); }
public Rotate rz = new Rotate();
{ rz.setAxis(Rotate.Z_AXIS); }
Translate t = new Translate();
static Node[] loadNodes() {
File file = new File(DAE_FILENAME);
ColModelImporter importer = new ColModelImporter();
importer.read(file);
Node[] nodes = importer.getImport();
ColAsset colAsset = importer.getAsset();
System.out.println("asset title " + colAsset.getTitle());
System.out.println("asset unit name " + colAsset.getUnitName());
System.out.println("asset unit meter " + colAsset.getUnitMeter());
System.out.println("asset up axis " + colAsset.getUpAxis());
Map<String, PhongMaterial> materials = importer.getNamedMaterials();
for (Map.Entry<String, PhongMaterial> e : materials.entrySet()) {
System.out.println("phong material " + e.getKey() + " -> " + e.getValue());
}
Map<String, Node> namedNodes = importer.getNamedNodes();
for (Map.Entry<String, Node> e : namedNodes.entrySet()) {
System.out.println("nodes " + e.getKey() + " -> " + e.getValue());
}
Viewpoint[] viewpoints = importer.getViewpoints();
if (viewpoints != null) for (Viewpoint v : viewpoints) {
System.out.println("viewpoint " + v);
}
return nodes;
}
private Group buildScene() {
Node[] nodes = loadNodes();
root = new Group(nodes);
return root;
}
private PerspectiveCamera addCamera(Scene scene) {
PerspectiveCamera camera = new PerspectiveCamera();
camera.getTransforms().addAll(t, rz, ry, rx);
camera.setVerticalFieldOfView(true);
camera.setFieldOfView(10d);
System.out.println("Near Clip: " + camera.getNearClip());
System.out.println("Far Clip: " + camera.getFarClip());
System.out.println("FOV: " + camera.getFieldOfView());
scene.setCamera(camera);
return camera;
}
#Override
public void start(Stage stage) {
Group group = buildScene();
group.setScaleX(10);
group.setScaleY(10);
group.setScaleZ(10);
group.setTranslateX(VIEWPORT_SIZE / 2);
group.setTranslateY(VIEWPORT_SIZE / 2);
Scene scene = new Scene(group, VIEWPORT_SIZE, VIEWPORT_SIZE, true);
scene.setFill(Color.rgb(10, 10, 40));
addCamera(scene);
stage.setTitle("Collada importer");
stage.setScene(scene);
stage.show();
scene.setOnKeyPressed((evt) -> {
switch (evt.getCode()) {
case UP:
rx.setAngle(rx.getAngle() + 5);
break;
case DOWN:
rx.setAngle(rx.getAngle() - 5);
break;
case RIGHT:
t.setX(t.getX() + 10);
//camera.setTranslateX(camera.getTranslateX()+10);
break;
case LEFT:
t.setX(t.getX() - 10);
//camera.setTranslateX(camera.getTranslateX()-10);
break;
case Z:
double zoom = evt.isShortcutDown() ? -10 : +10;
t.setZ(t.getZ() + zoom);
//camera.setTranslateZ(camera.getTranslateZ()+zoom);
break;
}
});
}
public static void main(String[] args) {
System.setProperty("prism.dirtyopts", "false");
launch(args);
}
}