Printing pixelates when clipped - macos

On macOS Big Sur, running JDK 17 and JavaFX 18, when you have an irregular clipping region, the clipped image ends up badly pixelated when printed.
The pixelation happens both on actual paper (on my Epson printer), as well as when saved to a PDF (using macOSs innate "Save to PDF" feature).
If you have a clip region with a Rectangle, there is no pixelation, everything looks smooth and crisp.
Note, that it must be a Rectangle, a rectangular Polygon has problems as well.
This is a screen shot of the actual screen rendering:
Next is a screen shot of viewing the resulting PDF.
You can see both the text and lines are pixelated within the circle.
Seems to me that its rendering the node in to the "PDF resolution", which is 72 DPI (not actual printer resolution), and then masking the region using a bitmap operation rather than actually clipping the content to the irregular border. But it special cases the Rectangle and clips to that.
Is there anything that can be done about this?
Below is a sample demonstrating this.
package pkg;
import javafx.application.Application;
import javafx.print.PrinterJob;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class App extends Application {
Pane printPane;
#Override
public void start(Stage stage) {
var scene = new Scene(getView(), 640, 480);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
private Pane getView() {
var flowPane = new FlowPane(getCirclePane(), getRectPane());
var b = new Button("Print");
printPane = flowPane;
b.setOnAction((t) -> {
var job = PrinterJob.createPrinterJob();
if (job != null && job.showPrintDialog(null)) {
if (job.printPage(printPane)) {
job.endJob();
}
}
});
var bp = new BorderPane();
bp.setCenter(flowPane);
bp.setBottom(b);
return bp;
}
private Pane getCirclePane() {
var pane = new Pane();
var circle = new Circle(100, 100, 45);
pane.setClip(circle);
var text = new Text(75, 100, "Circle");
pane.getChildren().add(text);
for (int i = 0; i < 10; i++) {
double ox = i * 25;
pane.getChildren().add(new Line(ox, 200, ox + 25, 0));
pane.getChildren().add(new Line(ox, 0, ox + 25, 200));
}
return pane;
}
private Pane getRectPane() {
var pane = new Pane();
var rect = new Rectangle(50, 50, 200, 125);
pane.setClip(rect);
var text = new Text(125, 100, "Rectangle");
pane.getChildren().add(text);
for (int i = 0; i < 10; i++) {
double ox = i * 25;
pane.getChildren().add(new Line(ox, 200, ox + 25, 0));
pane.getChildren().add(new Line(ox, 0, ox + 25, 200));
}
return pane;
}
}
Update:
I came up with this as a workaround.
Simply, that which I can't clip, I can paint over.
I create a new rect the size of the overall scene, then use Shape.subtract(...) to punch a hole in it in the shape of my clip region. I fill this new shape with my background color. Then I put this in a new pane, and use a Stack pane to layer it on top of my master. It's imperfect, and a little bit fiddly, and it chafes having to do this. But it works and is easy to explain.
So, using the circle as an example:
private Pane getCirclePane() {
var pane = new Pane();
var circle = new Circle(100, 100, 45);
var text = new Text(75, 100, "Circle");
pane.getChildren().add(text);
for (int i = 0; i < 10; i++) {
double ox = i * 25;
pane.getChildren().add(new Line(ox, 200, ox + 25, 0));
pane.getChildren().add(new Line(ox, 0, ox + 25, 200));
}
Rectangle rect = new Rectangle(0, 0, 250, 200);
Shape overlay = Shape.subtract(rect, circle);
overlay.setFill(Color.WHITE);
Pane overlayPane = new Pane();
overlayPane.getChildren().add(overlay);
pane = new StackPane(pane, overlayPane);
return pane;
}

As an interim workaround, you may be able to adapt the approach seen here; it uses Shape.subtract() to create a suitable matte that functions like the corresponding clip.
var c = new Circle(100, 100, 80);
var r = new Rectangle(250, 200);
Shape matte = Shape.subtract(r, c);
matte.setFill(Color.WHITE);
pane.getChildren().add(matte);
As printed:
As tested:
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.print.PrinterJob;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.Shape;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class App extends Application {
#Override
public void start(Stage stage) {
var scene = new Scene(getView());
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
private Pane getView() {
final var printBox = new HBox(8, getCirclePane(), getRectPane());
var b = new Button("Print");
b.setOnAction((t) -> {
var job = PrinterJob.createPrinterJob();
if (job != null && job.showPrintDialog(null)) {
if (job.printPage(printBox)) {
job.endJob();
}
}
});
var bp = new BorderPane();
bp.setCenter(printBox);
bp.setBottom(b);
BorderPane.setAlignment(b, Pos.BOTTOM_CENTER);
return bp;
}
private Pane getCirclePane() {
var pane = new Pane();
var text = new Text(80, 100, "Circle");
pane.getChildren().add(text);
addPattern(pane);
var c = new Circle(100, 100, 80);
var r = new Rectangle(250, 200);
Shape matte = Shape.subtract(r, c);
matte.setFill(Color.WHITE);
pane.getChildren().add(matte);
return pane;
}
private Pane getRectPane() {
var pane = new Pane();
var rect = new Rectangle(50, 50, 200, 125);
pane.setClip(rect);
var text = new Text(125, 100, "Rectangle");
pane.getChildren().add(text);
addPattern(pane);
return pane;
}
private void addPattern(Pane pane) {
for (int i = 0; i < 10; i++) {
double ox = i * 25;
pane.getChildren().add(new Line(ox, 200, ox + 25, 0));
pane.getChildren().add(new Line(ox, 0, ox + 25, 200));
}
}
}

Related

Processing - How to move the extended class to the group

The ColorPicker from cp5 is hardcoded and I can not resize the shapes of the sliders. So I extended the ColorPicker class and made a new one called MyColorPicker.
I have several groups and I want to add my new class to one of them.
Earlier, I could do it by cp5.addColorPicker("") .moveTo("") But in this case there is a new class and I couldn't move it to the group. Here is the code pieces:
import controlP5.*;
ControlP5 cp5;
MyColorPicker cp;
void setup() {
size(500, 300);
noStroke();
cp5 = new ControlP5(this);
cp = new MyColorPicker(cp5, "MYPICKER");
cp.setPosition(50, 50).setColorValue(color(255, 000, 000, 255));
Group SetupGroup = cp5.addGroup("SETUP")
.setPosition(90,100)
.setWidth(150)
.setHeight(30)
.setFont(font2);
background(0); //For transparency
noStroke();
;
Group ColorGroup = cp5.addGroup("COLOR-BRIGHTNESS")
.setPosition(30,240)
.setWidth(300)
.setHeight(30)
.setFont(font2)
.moveTo(SetupGroup);
background(0);
noStroke();
;
//I want to move my own color picker to the colorGroup instead of this one.
/*
cp5.addColorPicker("PICKER")
.setPosition(40, 10)
.moveTo(ColorGroup);
; */
.
.
.
.
.
.
.}
class MyColorPicker extends ColorPicker {
MyColorPicker(ControlP5 cp5, String theName) {
super(cp5, cp5.getTab("default"), theName, 0, 0, 100, 10);
}
void setItemSize(int w, int h) {
sliderRed.setSize(w, h);
sliderGreen.setSize(w, h);
sliderBlue.setSize(w, h);
sliderAlpha.setSize(w, h);
sliderRed.setPosition(10,10);
sliderGreen.setPosition(10,100);
sliderBlue.setPosition(10,180);
sliderAlpha.setPosition(10,260);
}
}
I would recommend fixing syntax errors before posting and also removing any code that may not be related to the issue. Trying to cleanup / minimise code that way made the solution obvious for me so many times in the past.
I'm not sure you can group groups within groups. I might be wrong and hopefully someone more experienced with cp5 can correct me if I'm wrong.
If you use a single group and fix the syntax errors you could run something like this:
import controlP5.*;
ControlP5 cp5;
MyColorPicker cp;
PFont font, font2;
void setup() {
size(500, 300);
noStroke();
font = createFont ("Georgia Bold", 20);
font2 = createFont ("Georgia Bold",15);
cp5 = new ControlP5(this);
Group SetupGroup = cp5.addGroup("SETUP")
.setPosition(90,100)
.setWidth(150)
.setHeight(30)
.setFont(font2);
cp = new MyColorPicker(cp5, "MYPICKER");
cp.setPosition(50, 50).setColorValue(color(255, 000, 000, 255));
cp.moveTo(SetupGroup);
}
void draw(){
background(0);
}
class MyColorPicker extends ColorPicker {
MyColorPicker(ControlP5 cp5, String theName) {
super(cp5, cp5.getTab("default"), theName, 0, 0, 100, 10);
}
void setItemSize(int w, int h) {
sliderRed.setSize(w, h);
sliderGreen.setSize(w, h);
sliderBlue.setSize(w, h);
sliderAlpha.setSize(w, h);
sliderRed.setPosition(10,10);
sliderGreen.setPosition(10,100);
sliderBlue.setPosition(10,180);
sliderAlpha.setPosition(10,260);
}
}
Well done on extending the colour picker, not noobie after all ;)
If you want two colour pickers for two LED rings but not display both at the same time perhaps you could use an accordion ?
import controlP5.*;
ControlP5 cp5;
MyColorPicker cp1, cp2;
PFont font, font2;
void setup() {
size(500, 300);
noStroke();
font = createFont ("Georgia Bold", 20);
font2 = createFont ("Georgia Bold",15);
cp5 = new ControlP5(this);
Group ring1Group = cp5.addGroup("ring 1")
.setPosition(90,100)
.setWidth(150)
.setHeight(30)
.setFont(font2);
cp1 = new MyColorPicker(cp5, "ring 1 picker");
cp1.setPosition(50, 50).setColorValue(color(255, 000, 000, 255));
cp1.moveTo(ring1Group);
Group ring2Group = cp5.addGroup("ring 2")
.setPosition(90,240)
.setWidth(150)
.setHeight(30)
.setFont(font2);
cp2 = new MyColorPicker(cp5, "ring 2 picker");
cp2.setPosition(50, 50).setColorValue(color(000, 255, 000, 255));
cp2.moveTo(ring2Group);
cp5.addAccordion("ring colors")
.setPosition(40,40)
.setWidth(200)
.addItem(ring1Group)
.addItem(ring2Group)
;
}
void draw(){
background(0);
}
class MyColorPicker extends ColorPicker {
MyColorPicker(ControlP5 cp5, String theName) {
super(cp5, cp5.getTab("default"), theName, 0, 0, 100, 10);
}
void setItemSize(int w, int h) {
sliderRed.setSize(w, h);
sliderGreen.setSize(w, h);
sliderBlue.setSize(w, h);
sliderAlpha.setSize(w, h);
sliderRed.setPosition(10,10);
sliderGreen.setPosition(10,100);
sliderBlue.setPosition(10,180);
sliderAlpha.setPosition(10,260);
}
}
I'm not sure multiple nested levels a supported with the accordion component either. I would expect it to support a flat hierarchy.

THREE.js pointerlock controls not moving camera around on mouse movement

I tried to follow the source code at https://threejs.org/examples/misc_controls_pointerlock.html without the divs, but it didn't work. I then tried following the code here https://sbcode.net/threejs/pointerlock-controls/ but it also did not work.
I simply want to be able to look around with the pointerlock controls, for first person view. I will be able to sort out the movement of the controls afterwards.
Currently I have a model (mainChar), where the camera is attached to him. I would like the pointerlock controls to be attached to his position somehow.
This is my code that doesn't work:
//IMPORT STATEMENTS
import { EntityManager } from './EntityManager.js';
import { LightingManager } from './LightingManager.js';
import { Time } from '../Time.js';
import { PauseMenu } from '../SceneSubjects/Menu/PauseMenu.js';
import { keyboardManager } from './KeyboardManager.js';
//lights
import { GeneralLights } from '../SceneSubjects/lighting/GeneralLights.js';
import { CeilingLight } from '../SceneSubjects/lighting/CeilingLight.js';
import { CeilingLightObj } from '../SceneSubjects/objects/CeilingLightObj.js';
//objects
import { House } from '../SceneSubjects/House.js';
import { SceneSubject } from '../SceneSubjects/objects/SceneSubject.js';
import { TestBlock } from '../SceneSubjects/characters/TestBlock.js';
import { Door } from '../SceneSubjects/objects/Door.js';
import { MainChar } from '../SceneSubjects/characters/MainChar.js';
//other
import { PointerLockControls } from '../../jsm/PointerLockControls.js';
import { OrbitControls } from '../../jsm/OrbitControls.js';
import * as THREE from '../../../jsm/three.module.js';
//==================================================================================================
//Global Variables
//lights
var generalLights = new GeneralLights();
//ceiling lights
var bedroomLightObj = new CeilingLightObj();
var kitchenLightObj = new CeilingLightObj();
var studyLightObj = new CeilingLightObj();
var hallwayLightObj1 = new CeilingLightObj();
var hallwayLightObj2 = new CeilingLightObj();
var bathroomLightObj = new CeilingLightObj();
var loungeLightObj = new CeilingLightObj();
var bedroomLight = new CeilingLight();
var kitchenLight = new CeilingLight();
var studyLight = new CeilingLight();
var hallwayLight1 = new CeilingLight();
var bathroomLight = new CeilingLight();
var hallwayLight2 = new CeilingLight();
var loungeLight = new CeilingLight();
//objects
var house = new House();
var sceneSubject = new SceneSubject();
var testBlock = new TestBlock();
var testdoor = new Door();
export var mainChar = new MainChar(testBlock);
export class SceneManager {
constructor(canvas) {
//this entire function renders a scene where you can add as many items as you want to it (e.g. we can create the house and add as
//many items as we want to the house). It renders objects from other javascript files
//------------------------------------------------------------------------------------------------------------------------------------------
//These are supposed to act like constants. DO NOT CHANGE
this.GAME_PAUSE = "pause";
this.GAME_RUN = "run";
this.GAME_START = "start";
//------------------------------------------------------------------------------------------------------------------------------------------
//we use (this) to make variables accessible in other classes
this.time = new Time();
this.objPauseMenu;
this.game_state = this.GAME_RUN;
this.width_screen = canvas.width;
this.height_screen = canvas.height;
this.screenDimensions = {
width: canvas.width,
height: canvas.height
};
//the essentials for rendering a scene
this.scene = this.buildScene();
this.renderer = this.buildRender(this.screenDimensions);
this.camera = this.buildCamera(this.screenDimensions);
//comment this out
// this.controls = new OrbitControls(this.camera, this.renderer.domElement);
//initialise pointerlock controls
this.pointerLockControls = new PointerLockControls(this.camera, this.renderer.domElement);
this.pointerLockControls.unlock();
//this.scene.add(this.pointerLockControls.getObject());
//====================
//adjust the ceiling light properties in the house
this.setCeilingLightProperties();
this.managers = this.createManagers();
//load things to scene
this.loadToScene(this.managers[0].lights);
this.loadToScene(this.managers[1].entities);
//---------------------------------------------------------------------------------------------------------------------------------
// Ok, now we have the cube. Next we'll create the hud. For that we'll
// need a separate scene which we'll render on top of our 3D scene. We'll
// use a dynamic texture to render the HUD.
// We will use 2D canvas element to render our HUD.
//---------------------------------------------------------------------------------------------------------------------------------
}
loadToScene(entities) {
for (let i = 0; i < entities.length; i++) {
this.scene.add(entities[i].object);
}
}
//this function creates our scene
buildScene() {
//create a new scene
const scene = new THREE.Scene();
//set the scene's background-> in this case it is our skybox
const loader = new THREE.CubeTextureLoader();
//it uses different textures per face of cube
const texture = loader.load([
'../skybox/House/posx.jpg',
'../skybox/House/negx.jpg',
'../skybox/House/posy.jpg',
'../skybox/House/negy.jpg',
'../skybox/House/posz.jpg',
'../skybox/House/negz.jpg'
]);
scene.background = texture;
//if we wanted it to be a colour, it would have been this commented code:
//scene.background = new THREE.Color("#000");
return scene;
}
//this creates a renderer for us
buildRender({ width, height }) {
const renderer = new THREE.WebGLRenderer({
canvas: canvas,
antialias: true, alpha: true
});
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
return renderer;
}
//create a camera for the screen
buildCamera({ width, height }) {
//SETTING FIELD OF VIEW, ASPECT RATIO (which should generally be width/ height), NEAR AND FAR (anything outside near/ far is clipped)
const aspectRatio = width / height;
const fieldOfView = 60;
const nearPlane = 1;
const farPlane = 1000;
//there are 2 types of cameras: orthographic and perspective- we will use perspective (more realistic)
const camera = new THREE.PerspectiveCamera(fieldOfView, aspectRatio, nearPlane, farPlane);
//Set camera initial position to main character
let pos = mainChar.returnWorldPosition();
camera.position.set(pos.x, pos.y + 10, pos.z - 10);
return camera;
}
setCeilingLightProperties() {
//set their light positions
bedroomLightObj.setLightPosition(0, 21, 50);
bedroomLight.setLightPosition(0, 16, 50);
loungeLightObj.setLightPosition(-45, 21, -60);
loungeLight.setLightPosition(-45, 16, -60);
studyLightObj.setLightPosition(35, 21, -50);
studyLight.setLightPosition(35, 16, -50);
kitchenLight.setLightPosition(-45, 16, 5);
kitchenLightObj.setLightPosition(-45, 21, 5);
bathroomLight.setLightPosition(45, 16, 15);
bathroomLightObj.setLightPosition(45, 21, 15);
hallwayLightObj1.setLightPosition(0, 21, -60);
hallwayLight1.setLightPosition(0, 16, -60);
hallwayLightObj2.setLightPosition(0, 21, 0);
hallwayLight2.setLightPosition(0, 16, 0);
}
//add subjects to the scene
createManagers() {
const managers = [new LightingManager(), new EntityManager()];
//can be altered so we can add multiple entities, and depending on which position
//it is, certain ones won't be paused, and some will be
//Note that these variables are declared globally before the class definition
/*This is so that we can use any of these object's methods or values later somewhere else*/
//lights
// managers[0].register(generalLights);
managers[0].register(bedroomLight);
managers[0].register(loungeLight);
managers[0].register(studyLight);
managers[0].register(hallwayLight1);
managers[0].register(hallwayLight2);
managers[0].register(kitchenLight);
managers[0].register(bathroomLight);
//entities
managers[1].register(loungeLightObj);
managers[1].register(studyLightObj);
managers[1].register(kitchenLightObj);
managers[1].register(bathroomLightObj);
managers[1].register(bedroomLightObj);
managers[1].register(hallwayLightObj1);
managers[1].register(hallwayLightObj2);
managers[1].register(house);
testdoor.setPosition(0, -0.5, 33);
//testdoor.setRotation(-Math.PI/2);
managers[1].register(testdoor);
managers[1].register(mainChar);
managers[1].register(sceneSubject);
managers[1].register(testBlock);
return managers;
}
updateCameraPosition() {
//Match camera position and direction to the character's position and direction
let pos = mainChar.returnWorldPosition();
let dir = mainChar.returnObjectDirection();
//Set y to 10 to move camera closer to head-height
//UNCOMMENT FOR 3RD PERSON
// this.camera.position.set(pos.x, 10 +10, pos.z + 20);
// this.camera.rotation.set(dir.x - 0.5, dir.y, dir.z);
//UNCOMMENT FOR FIRST PERSON
this.camera.position.set(pos.x, 15, pos.z - 5);
this.camera.rotation.set(dir.x, dir.y, dir.z);
}
//this updates the subject/model every frame
update() {
//won't call this loop if it's paused-> only for objects that need to be paused (managers that need to be paused)
if (this.game_state == this.GAME_RUN) {
//TO EXPERIMENT WITH FOR LOOKING AROUND!
// this.camera.position.x += ( keyboardManager.getMouseX() - this.camera.position.x ) ;
// this.camera.position.y += ( - keyboardManager.getMouseY() - this.camera.position.y );
// this.camera.lookAt( this.scene.position );
const runTime = this.time.getRunTime();
this.managers[0].update(runTime);
this.managers[1].update(runTime);
//update orbit controls
//comment out this.controls.update()
//this.controls.update();
this.renderer.render(this.scene, this.camera);
}
else {
//comment out
// this.controls.update();
this.renderer.autoClear = true;
//render scene1
this.renderer.render(this.scene, this.camera);
//prevent canvas from being erased with next .render call
this.renderer.autoClear = false;
//just render scene2 on top of scene1
this.renderer.render(this.objPauseMenu.scene, this.objPauseMenu.camera);
// renderer.autoClear = true;
}
//update orbit controls
//comment out
// this.controls.update();
//uncomment this
this.updateCameraPosition();
}
//this resizes our game when screen size changed
onWindowResize() {
this.camera.aspect = window.innerWidth / window.innerHeight;
this.camera.updateProjectionMatrix();
this.renderer.setSize(window.innerWidth, window.innerHeight);
}
pause() { //when pause mode is entered. The pause menu needs to be rendered.
this.game_state = this.GAME_PAUSE;
this.time.pause();
//comment out
// this.controls.enabled = false; // stop orbit controls from responding to use input
this.objPauseMenu = new PauseMenu(this.width_screen, this.height_screen);
}
unpause() {
this.game_state = this.GAME_RUN;
this.time.unpause();
//comment out
// this.controls.enabled = true; // start orbit controls tp respond to input
}
}
pointerLockControls.unlock() deactivates the controls
this should work
this.pointerLockControls.lock()

Creating a custom node in JavaFX and adding it to a layout

I am new to GUI programming, I need help creating 7 Custom Nodes and putting them into a layout. I do not know what parent class I should be extending or how to implement this class as a node.
I want the final GUI to look like this:
http://d2vlcm61l7u1fs.cloudfront.net/media%2Fecc%2Fecc3c2db-2a7e-4571-bcf0-37858846dadf%2FphphG0pUA.png
Here is what I have so far
public class HexagonShape extends CustomNode {//what to extend?
//color of each segment
public String A;
public String B;
public String C;
public String D;
public String E;
public String F;
private final double angle30degree = Math.PI / 6;
public HexagonShape() {
// Create a pane, a polygon, and place polygon to pane
Pane pane = new Pane();
Polygon triangle1 = new Polygon();
ObservableList<Double> tri1List = triangle1.getPoints();
Polygon triangle2 = new Polygon();
ObservableList<Double> tri2List = triangle1.getPoints();
Polygon triangle3 = new Polygon();
ObservableList<Double> tri3List = triangle1.getPoints();
Polygon triangle4 = new Polygon();
ObservableList<Double> tri4List = triangle1.getPoints();
Polygon triangle5 = new Polygon();
ObservableList<Double> tri5List = triangle1.getPoints();
Polygon triangle6 = new Polygon();
ObservableList<Double> tri6List = triangle1.getPoints();
Polygon hexagon = new Polygon();
pane.getChildren().addAll(hexagon, triangle1, triangle2, triangle3, triangle4, triangle5, triangle6);
hexagon.setFill(Color.WHITE);
hexagon.setStroke(Color.BLACK);
ObservableList<Double> list = hexagon.getPoints();
triangle1.setFill(Color.GRAY);
triangle1.setStroke(Color.BLUEVIOLET);
final double WIDTH = 250, HEIGHT = 250;
double centerX = WIDTH / 2, centerY = HEIGHT / 2;
double radius = Math.min(WIDTH, HEIGHT) * 0.4;
// Add points to the polygon list
for (int i = 0; i < 6; i++) {
list.add(centerX + radius * Math.cos(2 * i * angle30degree));
list.add(centerY - radius * Math.sin(2 * i * angle30degree));
}
createTriangle(tri2List, radius, centerX, centerY, 0);
createTriangle(tri1List, radius, centerX, centerY, 2);
createTriangle(tri6List, radius, centerX, centerY, 4);
createTriangle(tri5List, radius, centerX, centerY, 6);
createTriangle(tri4List, radius, centerX, centerY, 8);
createTriangle(tri3List, radius, centerX, centerY, 10);
}
private void createTriangle(ObservableList<Double> vectors, double radius, double centerX, double centerY, int radian) {
vectors.add(centerX);
vectors.add(centerY);
vectors.add(centerX + radius * Math.cos(radian * angle30degree));
vectors.add(centerY - radius * Math.sin(radian * angle30degree));
vectors.add(centerX + radius * Math.cos((radian + 2) * angle30degree));
vectors.add(centerY - radius * Math.sin((radian + 2) * angle30degree));
}
public void loadHexagon(ArrayList<String> colors, int id){
this.A = colors.get(0);
this.B = colors.get(1);
this.C = colors.get(2);
this.D = colors.get(3);
this.E = colors.get(4);
this.F = colors.get(5);
}
}
This is the main GUI where I want to instantiate my custom node (hexagon)
package gui;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Scanner;
import javax.swing.JFileChooser;
import hexagon.Hexagon;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.layout.GridPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Polygon;
import javafx.scene.transform.Rotate;
import javafx.stage.Stage;
public class HexagonGUI extends Application {
#Override
public void start(Stage primaryStage) {
GridPane gp = new GridPane();
gp.setGridLinesVisible(true);
gp.setVgap(10);
gp.setHgap(10);
gp.setPadding(new Insets(10,10,10,10));
HexagonShape h1 = new HexagonShape();//custom node to add
GridPane.setConstraints(h1, 10, 10);
HexagonShape h2 = new HexagonShape();
HexagonShape h3 = new HexagonShape();
HexagonShape h4 = new HexagonShape();
HexagonShape h5 = new HexagonShape();
HexagonShape h6 = new HexagonShape();
HexagonShape h7 = new HexagonShape();
Scene scene = new Scene(gp, 260, 80);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) throws IOException{
launch(args);
}
}
You can extend Region class in your HexagonShape class,
Then do this.getChildren.add(pane); in your HexagonShape class
Note: Region extends Parent which extends Node class

Unity | Label width

I want to get the size of my label. It doesn't work like it should. It gets the size, but only for the standard fontsize. But I'm not using the standard one.
Could this be a problem: "Unable to find style 'FontStyle' in skin 'GameSkin' Repaint UnityEngine.GUISkin:GetStyle(String)"
I have this code:
string test ="test";
Vector2 SizeVect;
public int FontSize = 1;
Rect TestRect:
void OnGUI(){
TestRect = new Rect(...);
FontStyle = new GUIStyle();
FontStyle.fontSize = FontSize;
SizeVect = GUI.skin.GetStyle("FontStyle").CalcSize(new GUIContent(test));
GUI.Label(TestRect, test, FontStyle);
}
Try below code, documentation here.
SizeVect = GUI.skin.label.CalcSize(new GUIContent(test));
Update: When font size is changed, this also works.
private float fontSize = 12;
void OnGUI()
{
fontSize = GUI.HorizontalSlider(new Rect(50, 50, 200, 20), fontSize, 8, 64);
var str = "ABCDEFG";
var style = GUI.skin.label;
style.fontSize = (int)fontSize;
var size = style.CalcSize(new GUIContent(str));
var rect = new Rect(100, 100, size.x, size.y);
GUI.Label(rect,str );
GUI.Box(rect,"");
}
See snapshot below:

Why won't my canvas clear between animation frames?

I decided to start playing around with dart again today and for some reason I can't get my viewport to clear between frames. I've tried to use clearRect and fillRect to draw a white rectangle over the entire canvas element. Here is my code.
import 'dart:html';
void main() {
var canvasFun = new CanvasFun(query("#Game"));
}
class CanvasFun{
CanvasElement canvas;
num _width;
num _height;
Square square;
CanvasFun(this.canvas){
this.square = new Square(10,10, new Point(50,50));
requestRedraw();
}
void draw(num _){
var ctx = canvas.context2d;
//clear the viewport
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
ctx.fillStyle = "white";
ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
//draw the square
this.square.draw(ctx);
requestRedraw();
}
void requestRedraw() {
window.requestAnimationFrame(draw);
}
}
class Point {
num x, y;
Point(this.x, this.y);
}
class Square{
num width;
num height;
num vectorX;
num vectorY;
Point location;
Square(this.width, this.height, this.location){
vectorX = 1;
vectorY = 1;
}
void draw(CanvasRenderingContext2D context){
context.save(); //I thought this might fix the blue viewport problem
this.update(context);
context.rect(this.location.x, this.location.y, this.width, this.height);
context.fillStyle = "blue";
context.fill();
}
void update(CanvasRenderingContext2D context)
{
num xBound = context.canvas.width;
num yBound = context.canvas.height;
if((this.location.x + this.width) > xBound){
this.vectorX = -1;
}
else if(this.location.x < 0){
this.vectorX = 1;
}
if((this.location.y + this.height) > yBound){
this.vectorY = -1;
}
else if(this.location.y < 0){
this.vectorY = 1;
}
this.location.x += (this.vectorX * 10);
this.location.y += (this.vectorY * 20);
}
}
The resulting animation draws a rectangle at the correct location but as it moves about the canvas the previous instance of the rectangle is still drawn. I'd like the rectangle to only appear once on the canvas and to look like it is moving about the screen.
Here is a screenshot of the output (notice the blue square is never cleared):
I simplified your code to get it working:
In CanvasFun:
void draw(num _){
var ctx = canvas.context2d;
//clear the viewport
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
//draw the square
this.square.draw(ctx);
requestRedraw();
}
In Square:
void draw(CanvasRenderingContext2D context){
this.update(context);
context.fillStyle = "blue";
context.fillRect(this.location.x, this.location.y, this.width, this.height);
}
I'm not sure what the exact problem was with the original version though. :)

Resources