---
category: swing
folder: RoundingSpecificCorners
title: Path2Dで指定した隅を丸めたランウド矩形図形を作成する
tags: [Graphics, Path2D, Rectangle2D, Ellipse2D]
author: aterai
pubdate: 2024-03-04T01:43:01+09:00
description: Path2Dを使用して指定した隅のみをベジェ曲線で丸めたランウド矩形図形を作成します。
image: https://drive.google.com/uc?id=1F3aKQ3EgrQD9MXA8sJoeXRKXWj-gB6h2
---
* 概要 [#summary]
`Path2D`を使用して指定した隅のみをベジェ曲線で丸めたランウド矩形図形を作成します。

#download(https://drive.google.com/uc?id=1F3aKQ3EgrQD9MXA8sJoeXRKXWj-gB6h2)

* サンプルコード [#sourcecode]
#code(link){{
enum Corner { TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT }

public Shape makeRoundedRect2(
    Rectangle2D r, double aw, double ah, Set<Corner> corners) {
  double x = r.getX();
  double y = r.getY();
  double w = r.getWidth();
  double h = r.getHeight();
  double arh = ah * .5;
  double arw = aw * .5;
  double kappa = 4d * (Math.sqrt(2d) - 1d) / 3d; // = 0.55228...;
  double akw = arw * kappa;
  double akh = arh * kappa;
  Path2D.Double p = new Path2D.Double();
  if (corners.contains(Corner.TOP_LEFT)) {
    p.moveTo(x, y + arh);
    p.curveTo(
      x, y + arh - akh,
      x + arw - akw, y,
      x + arw, y);
  } else {
    p.moveTo(x, y);
  }
  if (corners.contains(Corner.TOP_RIGHT)) {
    p.lineTo(x + w - arw, y);
    p.curveTo(
      x + w - arw + akw,
      y, x + w, y + arh - akh,
      x + w, y + arh);
  } else {
    p.lineTo(x + w, y);
  }
  if (corners.contains(Corner.BOTTOM_RIGHT)) {
    p.lineTo(x + w, y + h - arh);
    p.curveTo(
      x + w, y + h - arh + akh,
      x + w - arw + akw,
      y + h, x + w - arw, y + h);
  } else {
    p.lineTo(x + w, y + h);
  }
  if (corners.contains(Corner.BOTTOM_LEFT)) {
    p.lineTo(x + arw, y + h);
    p.curveTo(
      x + arw - akw, y + h,
      x, y + h - arh + akh,
      x, y + h - arh);
  } else {
    p.lineTo(x, y + h);
  }
  p.closePath();
  return p;
}
}}

* 解説 [#explanation]
- `ROUND_RECTANGLE2D`
-- `new RoundRectangle2D.Double(x, y, w, h, aw, ah)`で生成した図形を黒線で描画

- `SUBTRACT`
-- `Rectangle2D`から`Ellipse2D`を除去して作成した図形で矩形の指定した隅を丸めてランウド矩形を作成し赤線で描画
-- `RoundRectangle2D`と`Ellipse2D`のランウド半径は同一なので指定して丸めた隅は上記の黒色で描画した`RoundRectangle2D`と完全に一致する

#code{{
public Shape makeRoundedRect0(
    Rectangle2D rect, double aw, double ah, Set<Corner> corners) {
  double x = rect.getX();
  double y = rect.getY();
  double w = rect.getWidth();
  double h = rect.getHeight();
  double arw = aw * .5;
  double arh = ah * .5;
  Area r = new Area(rect);
  if (corners.contains(Corner.TOP_LEFT)) {
    Area tl = new Area(new Rectangle2D.Double(x, y, arw, arh));
    tl.subtract(new Area(new Ellipse2D.Double(x, y, aw, ah)));
    r.subtract(tl);
  }
  if (corners.contains(Corner.TOP_RIGHT)) {
    Area tr = new Area(new Rectangle2D.Double(x + w - arw, y, arw, arh));
    tr.subtract(new Area(new Ellipse2D.Double(x + w - aw, y, aw, ah)));
    r.subtract(tr);
  }
  if (corners.contains(Corner.BOTTOM_LEFT)) {
    Area bl = new Area(new Rectangle2D.Double(x, y + h - arh, arw, arh));
    bl.subtract(new Area(new Ellipse2D.Double(x, y + h - ah, aw, ah)));
    r.subtract(bl);
  }
  if (corners.contains(Corner.BOTTOM_RIGHT)) {
    Area br = new Area(new Rectangle2D.Double(x + w - arw, y + h - arh, arw, arh));
    br.subtract(new Area(new Ellipse2D.Double(x + w - aw, y + h - ah, aw, ah)));
    r.subtract(br);
  }
  return r;
}
}}

- `PATH2D1`
-- `Path2D#curveTo(...)`を使用して指定した隅を丸めたランウド矩形を作成し緑線で描画
-- ベジェ制御点はラウンド半径を単純に`0.5`倍した点を使用しているので指定して丸めた隅は同じランウド半径を使用した`RoundRectangle2D`などと少しずれる

#code{{
public static Path2D makeRoundedRect1(Rectangle2D r, double aw, double ah, Set<Corner> corners) {
public static Path2D makeRoundedRect1(
    Rectangle2D r, double aw, double ah, Set<Corner> corners) {
  double x = r.getX();
  double y = r.getY();
  double w = r.getWidth();
  double h = r.getHeight();
  double arw = aw * .5;
  double arh = ah * .5;
  Path2D.Double path = new Path2D.Double();
  if (corners.contains(Corner.TOP_LEFT)) {
    path.moveTo(x, y + arh);
    path.curveTo(x, y + arh, x, y, x + arw, y);
  } else {
    path.moveTo(x, y);
  }
  if (corners.contains(Corner.TOP_RIGHT)) {
    path.lineTo(x + w - arw, y);
    path.curveTo(x + w - arw, y, x + w, y, x + w, y + arh);
  } else {
    path.lineTo(x + w, y);
  }
  if (corners.contains(Corner.BOTTOM_RIGHT)) {
    path.lineTo(x + w, y + h - arh);
    path.curveTo(x + w, y + h - arh, x + w, y + h, x + w - arw, y + h);
  } else {
    path.lineTo(x + w, y + h);
  }
  if (corners.contains(Corner.BOTTOM_LEFT)) {
    path.lineTo(x + arw, y + h);
    path.curveTo(x + arw, y + h, x, y + h, x, y + h - arh);
  } else {
    path.lineTo(x, y + h);
  }
  path.closePath();
  // path.transform(AffineTransform.getTranslateInstance(x, y));
  return path;
}
}}

- `PATH2D2`
-- `Path2D#curveTo(...)`を使用して指定した隅を丸めたランウド矩形を作成し青線で描画
-- `RoundRectangle2D`などの円で使用するベジェ制御点と同じラウンド半径を`4d * (Math.sqrt(2d) - 1d) / 3d`倍(約`0.55228...`倍)した点を使用するため指定して丸めた隅は上記の黒色で描画した`RoundRectangle2D`と完全に一致する

* 参考リンク [#reference]
- [[Path2Dで額縁風の図形を作成しBorderとして使用する>Swing/PlaqueBorder]]
- [https://cat-in-136.github.io/2014/03/bezier-1-kappa.html ベジェ曲線による円の描画の制御点の位置はなぜ0.55228…なのか?]
- [https://stackoverflow.com/questions/77889724/how-to-make-half-rounded-border java - How to make half rounded border? - Stack Overflow]

* コメント [#comment]
#comment
#comment