Přidat otázku mezi oblíbenéZasílat nové odpovědi e-mailemVyřešeno JavaFX - Jak jednoduše rozdělit shape?

Dobrý den,

Pomocí tohoto kódu vytvořím Path

import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.*;
import javafx.stage.Stage;

public class DivideShape extends javafx.application.Application {

    @Override
    public void start(Stage primaryStage) {
        var path = new Path(new MoveTo(200, 380), new LineTo(0, 300),
            new ArcTo(300, 300, 0, 300, 0, false, true), new LineTo(380,
                    200), new ArcTo(180, 180, 0, 200, 380, false, false));
        path.setFill(Color.BLUE);
        primaryStage.setTitle("Hello World!");
        primaryStage.setScene(new Scene(new Pane(path)));
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

[]

Ten potřebuji rozdělit na 5 částí (na 5 samostatných Path), tak jsem vytvořil 5 objektů, tvořící dohromady objekt výše.

@Override
public void start(Stage primaryStage) {
    var path2 = new Path(new MoveTo(200, 380), new LineTo(0, 300),
            new ArcTo(300, 300, 0, 300, 0, false, true), new LineTo(380,
                    200), new ArcTo(180, 180, 0, 200, 380, false, false));*/
    var pane = new Pane();
    var moveTo = new MoveTo(200, 380);
    var lineTo = new LineTo(0, 300);
    var arc_big = 300;
    var arc_small = 180;
    for (var deg = 198; deg <= 270; deg += 18) {
        var path = new Path(moveTo, lineTo);
        lineTo = new LineTo(300 + arc_big * Math.cos(Math.toRadians(deg)), 300 + arc_big * Math.sin(Math.toRadians(deg)));
        path.getElements().add(new ArcTo(arc_big, arc_big, 0, lineTo.getX(), lineTo.getY(), false, true));
        var move = new MoveTo(380 + arc_small * Math.cos(Math.toRadians(deg)), 380 + arc_small * Math.sin(Math.toRadians(deg)));
        path.getElements().addAll(new LineTo(move.getX(), move.getY()),
        new ArcTo(arc_small, arc_small, 0, moveTo.getX(), moveTo.getY(), false, false));
        moveTo = move;
        path.setFill(Color.color(Math.random(), Math.random(), Math.random()));
        pane.getChildren().add(path);
    }
    primaryStage.setTitle("Hello World!");
    primaryStage.setScene(new Scene(pane));
    primaryStage.show();
}

[]

Kód sice funguje, ale je příliš složitý. Navíc, i když jsem tento kód vytvořil, přesto jsem si po necelém měsíci nepamatoval, jak vlastně funguje.

Šlo by to, prosím Vás, nějak zjednodušit?

Děkuji

PS. Ještě mě napadla možnost to skutečně rozdělit na 5 částí, ale asi to nepůjde respektive nikde jsem nenašel zmínku o tom, že by něco takového bylo možné provést.

Předmět Autor Datum
Tak on by ten kód byl o dost přehlednější, kdybys neprogramoval stylem copy+paste. Math.toRadians(de…
Wikan 29.04.2020 16:11
Wikan
A opravdu chceš, aby to vypadalo takhle? Vnitřní oblouk je čtvrtkruh, ale vnější ne. Ano, vím, ale…
MichalDM 29.04.2020 18:14
MichalDM
Když tam bude víc správně pojmenovaných proměnných, tak to smysl dávat bude. Nezavrhuj to, dokud to…
Wikan 29.04.2020 18:22
Wikan
Ano, správný počet a pojmenování proměnných může vysvětlit hodně, ale rozhodně ne všechno. Nezavrhu…
MichalDM 29.04.2020 19:37
MichalDM
Že se tam používá sinová věta, jsem si teda opravdu nevšiml. Kde že tam je?
Wikan 29.04.2020 19:48
Wikan
Právě pomocí pravoúhlého trojúhelníku jsem chtěl vypočítat souřadnice. A výpočet jsem dělal právě po…
MichalDM 29.04.2020 22:14
MichalDM
Jenže ty tam pravý úhel nemáš, takže se mi zdá, že jsi dvěma špatnými úvahami čistě náhodou trefil n…
Wikan 29.04.2020 22:27
Wikan
Moc se omlouvám za pozdní odpověď. Děkuji, Váš kód je dobrý, nicméně řešil jsem stejný problém na St…
MichalDM 08.06.2020 12:27
MichalDM
Jenže tenhle kód dělá něco jiného, i když trochu podobného. Takže požadovaného výsledku nedosáhneš ž…
Wikan 08.06.2020 12:41
Wikan
Aha. Tak to je ten problém. A nelze dosáhnout aspoň podobného výsledku? např. aby oblouky tvořily čá…
MichalDM 08.06.2020 12:55
MichalDM
A proč chceš dosahovat podobného výsledku, když ten můj kód dělá přesný výsledek?
Wikan 08.06.2020 13:35
Wikan
Vlastně ano, takže tím bych to uzavřel. Děkuji poslední
MichalDM 08.06.2020 16:00
MichalDM
Tak jsem si to přepsal do JS + SVG (kód je ale i tak velice podobný) a takový je výsledek: [89980-sh…
Wikan 30.04.2020 20:37
Wikan
Děkuji, ale tímhle způsobem to dělat nechci.
MichalDM 08.06.2020 12:28
MichalDM

Tak on by ten kód byl o dost přehlednější, kdybys neprogramoval stylem copy+paste.
Math.toRadians(deg) - je tam 4x
Math.cos(...), Math.sin(...) - je tam obojí 2x

Pojmenování proměnných by taky mohlo být lepší. A mohl bys jich používat víc, čímž by se ten kód rozpadl na menší a srozumitelnější části. Používat výpočty v konstruktoru nebo obecně při volání metod na přehlednosti moc nepřidá.

A opravdu chceš, aby to vypadalo takhle? Vnitřní oblouk je čtvrtkruh, ale vnější ne.

A opravdu chceš, aby to vypadalo takhle? Vnitřní oblouk je čtvrtkruh, ale vnější ne.

Ano, vím, ale takhle to skutečně chci.

Omlouvám se, jak jsem to přepisoval do příkladu, tak jsem někde napsal hotové hodnoty

1. Na začátku vím pouze lineTo a moveToStart a oblouky z nich vypočítám

var arc_big = lineTo.getY() - lineTo.getX();
var arc_small = moveToStart.getY() - moveToStart.getX();

2. To samé i 300 a 380 v cyklu. To je ve skutečnosti taky jenom lineTo.getY() a moveToStart.getY()

A s těmi úhly máte pravdu. Jenže když to upravím, tak vznikne

...
var moveToStart = new MoveTo(200, 380);
var lineTo = new LineTo(0, 300);
var arc_big = lineTo.getY() - lineTo.getX();
var arc_small = moveToStart.getY() - moveToStart.getX();
var lineY = lineTo.getY();
var moveY = moveToStart.getY();
double sin, cos;
Path path;
for (var deg = 198; deg <= 270; deg += 18) {
    sin = Math.sin(Math.toRadians(deg));
    cos = Math.cos(Math.toRadians(deg));
    path = new Path(moveToStart, lineTo);
    lineTo = new LineTo(lineY + arc_big * cos, lineY + arc_big * sin);
    path.getElements().add(new ArcTo(arc_big, arc_big, 0, lineTo.getX(), lineTo.getY(), false, true));
    var moveTo = new MoveTo(moveY + arc_small * cos, moveY + arc_small * sin);
    path.getElements().addAll(new LineTo(moveTo.getX(), moveTo.getY()),
                new ArcTo(arc_small, arc_small, 0, moveToStart.getX(), moveToStart.getY(), false, false));
    moveToStart = moveTo;
    path.setFill(Color.color(Math.random(), Math.random(), Math.random()));
    pane.getChildren().add(path);
}
...

kód neříkající absolutně nic. Ano, mohlo být více proměnných včetně jejich správného pojmenování.

Ale bez komentářů stejně nebude dávat smysl, jak algoritmus vlastně funguje.

Proto jsem chtěl, prosím Vás, vědět, jestli by to bylo možné nějak zjednodušit, případně použít jiný algoritmus?

Právě pomocí pravoúhlého trojúhelníku jsem chtěl vypočítat souřadnice. A výpočet jsem dělal právě pomocí sinovi věty respektive základem byla sinova věta.

a / b = sin a / sin b

Avšak, pokud b bude přepona (poloměr kruhu) a protilehlý úhel je 90°, což je v případě sin 1, lze to upravit na

a = b * sin a

což je v podstatě to samé jako např. arc_big * sin v kódu výše.

Avšak, jak jsem se dopracoval k tomu, že je nutné přičíst hodnotu, navíc u souřadnice x nepoužít sin, ale cos a to zabalit do cyklu začínají 198°, to vážně nevím.

A jsme tam, o čem jsem mluvil. Vymyslel jsem algoritmus, který není jednoduchý a jelikož jsem si k němu nic nenapsal, tak ani nevím, jak vlastně funguje.

Jenže ty tam pravý úhel nemáš, takže se mi zdá, že jsi dvěma špatnými úvahami čistě náhodou trefil na správnou.
Nemám tady žádné IDE pro Javu, takže jsem to psal naslepo a bez vyzkoušení, ale napsal bych to asi takhle:

var smallRadius = 180;
var smallCenter = new Point2D(380, 380);
var bigRadius = 300;
var bigCenter = new Point2D(300, 300);

var startAngle = Math.toRadians(180);
var endAngle = Math.toRadians(270);
var stepCount = 5;

// todo refactor duplicity and constructor calculations
var sin = Math.sin(startAngle);
var cos = Math.cos(startAngle);
var bigArcStart = new Point2D(bigCenter.getX() + bigRadius * cos, bigCenter.getY() + bigRadius * sin);
var smallArcStart = new Point2D(smallCenter.getX() + smallRadius * cos, smallCenter.getY() + smallRadius * sin);

var step = (engAngle - startAngle) / stepCount;

for (var rad = startAngle + step; rad <= engAngle; rad += step) {

    // todo refactor duplicity and constructor calculations
    sin = Math.sin(rad);
    cos = Math.cos(rad);
    var bigArcEnd = new Point2D(bigCenter.getX() + bigRadius * cos, bigCenter.getY() + bigRadius * sin);
    var smallArcEnd = new Point2D(smallCenter.getX() + smallRadius * cos, smallCenter.getY() + smallRadius * sin);

    var path = new Path(
        new MoveTo(smallArcStart.getX(), smallArcStart.getY()),
        new LineTo(bigArcStart.getX(), bigArcStart.getY()),
        new ArcTo(bigRadius, bigRadius, 0, bigArcEnd.getX(), bigArcEnd.getY(), false, false),
        new LineTo(smallArcEnd.getX(), smallArcEnd.getY()),
        new ArcTo(smallRadius, smallRadius, 0, smallArcStart.getX(), smallArcStart.getY(), false, false)
    );

    path.setFill(Color.color(Math.random(), Math.random(), Math.random()));
    pane.getChildren().add(path);

    bigArcStart = bigArcEnd;
    smallArcStart = smallArcEnd;
}

Mělo by to dělat přesně to, co tvůj kód, ale o dost přehledněji.

Moc se omlouvám za pozdní odpověď. Děkuji, Váš kód je dobrý, nicméně řešil jsem stejný problém na StackOverflow a poradil mi tento kód.

public class ConvinienceMethodsExample extends Application {

    private static final double OUTER_RADIUS =300, INNER_RADIUS = 180, ORIGIN_X = 300, ORIGIN_Y = 300;
    private static final double START_ANGLE =20, END_ANGLE = 70, STEP_ANGEL = 10;

    @Override
    public void start(Stage primaryStage) {

        Group group = new Group();

        for(double angle =  START_ANGLE; angle < END_ANGLE ; angle += STEP_ANGEL ){

            Point2D innerArcStart = getPoint(INNER_RADIUS, angle);
            Point2D innerArcEnd = getPoint(INNER_RADIUS, angle + STEP_ANGEL);
            Point2D outerArcStart = getPoint(OUTER_RADIUS, angle);
            Point2D outerArcEnd = getPoint(OUTER_RADIUS, angle + STEP_ANGEL);
            var path = getPath(innerArcStart, innerArcEnd, outerArcStart, outerArcEnd);
            group.getChildren().add(path);
        }
        primaryStage.setScene(new Scene(new Pane(group)));
        primaryStage.show();
    }

    private Point2D getPoint(double radius, double angle){

        double x = ORIGIN_X - radius * Math.cos(Math.toRadians(angle));
        double y = ORIGIN_Y - radius * Math.sin(Math.toRadians(angle));
        return new Point2D(x, y);
    }

    private Shape getPath(Point2D innerArcStart, Point2D innerArcEnd, Point2D outerArcStart, Point2D outerArcEnd){
        var path = new Path(
                new MoveTo(innerArcStart.getX(), innerArcStart.getY()),
                new LineTo(outerArcStart.getX(), outerArcStart.getY()), //left line
                new ArcTo(OUTER_RADIUS, OUTER_RADIUS, 0, outerArcEnd.getX(), outerArcEnd.getY(), false, true), //outer arc
                new LineTo(innerArcEnd.getX(),innerArcEnd.getY()), //right line
                new ArcTo(INNER_RADIUS, INNER_RADIUS, 0, innerArcStart.getX(), innerArcStart.getY(), false, false)
                );
        path.setFill(Color.color(Math.random(), Math.random(), Math.random()));
        return path;
    }

    public static void main(String args[]){
        launch(args);
    }
}

[]

I když se kód jeví jednoduše, nepřišel jsem na to, jak upravit hodnoty, abych dosáhl stejného výsledku. Nejde mi o velikost okna, pouze aby byl oblouk vypadal stejně a dotýkal se levé a horní strany.

Na moji otázku, jak mám hodnoty zadat, mi odpověděl tímhle obrázkem, s fieldy

OUTER_RADIUS = 400, INNER_RADIUS = 100, ORIGIN_X = 400, ORIGIN_Y = 400 // obrázek vlevo
OUTER_RADIUS = 300, INNER_RADIUS = 180, ORIGIN_X = 300, ORIGIN_Y = 300 // obrázek vpravo

[]

a že musím kód správně pochopit.

Jenže, jak jsem řekl, i když se kód jeví jednoduše, nepřišel jsem na to, jak hodnoty správně zadat.

Prosím o radu.

Děkuji

Zpět do poradny Odpovědět na původní otázku Nahoru