oiwly0706
@oiwly0706

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

箱の中に長方形を右下から配置していきたい

解決したいこと

左下から長方形を配置するプログラム(Bottom-Left法)を右下から配置するプログラム(Bottom-Right法)に置き換えたい。

重さ順でソートされているので重い順で右下から配置していきたいです。

発生している問題・エラー

import matplotlib.pyplot as plt
import matplotlib.patches as patches
import pandas as pd
import csv
import tkinter
import time
import numpy as np

def intersection_NFP(p, q):
    if q[0][0] < p[0][1] < q[0][1] and p[1][0] < q[1][1] < p[1][1]:
        return (p[0][1], q[1][1])
    if p[0][0] < q[0][1] < p[0][1] and q[1][0] < p[1][1] < q[1][1]:
        return (q[0][1], p[1][1])
    return False

#p=[(x1,x2),(y1,y2)] NFPとx軸の交点を求め、 BL点候補点とする。
def intersection_x(p):
    if p[0][0] < 0:
        return (0, p[1][1])
    return False

#p=[(x1,x2),(y1,y2)] NFPとy軸の交点を求め、 BL点候補点とする。
def intersection_y(p):
    if p[1][0] < 0:
        return (p[0][1], 0)
    return False

#p=(a,b) q=[(x1,x2),(y1,y2)] 点pが領域q内に含まれているか、いないか。(境界線は含まない)
def include_p(p, q):
    return q[0][0] < p[0] < q[0][1] and q[1][0] < p[1] < q[1][1]

#p=(a,b) q=[(x1,x2),(y1,y2)] 点pが領域q内に含まれているか、いないか。(境界線は含む)
def include_p_e(p, q):
    return q[0][0] <= p[0] <= q[0][1] and q[1][0] <= p[1] <= q[1][1]

#p=(a,b) q=[{(x1,x2),(y1,y2)},{(x3,x4),(y3,y4)},...]
#点pが領域配列qのいずれか1つに含まれているか、いないか。(境界線は含まない)
def include_p_n(p, q, n):
    for i in range(n):
        if include_p(p, q[i]) == True:
            ans = True
            break
        ans = False
    return ans

class Rect:
    def __init__(self, x, y, w, h, weight,name):
        self.x = x
        self.y = y
        self.w = w
        self.h = h
        self.weight=weight
        self.name=name

    #未配置の長方形jの高さhb横幅wb配列 既に配置されている長方形iの高さha横幅ba配列
    def NFP(self, b):
        return [(self.x-b.w, self.x+self.w), (self.y-b.h, self.y+self.h)]

    #箱の中に収まる範囲 UBはHの上界値
    def include_room(self, W, H):
        return [(0, W-self.w), (0, H-self.h)]

    #長方形nにおいて既存に配置されている長方形とのNFPを計算
    def NFP_r2(self, rects_after):
        cand = []
        if not rects_after:
            return cand
        else:
            for rect in rects_after:
                p = Rect.NFP(rect, self)
                cand.append(p)
        return cand

    #p=(a,b) [(x1,y1),(x2,y2),...(xn, yn)] 求めたBL候補点pが既に使われていないか。
    def point_search(rects_after, p):
        for rect in rects_after:
            if p[0] == rect.x and p[1] == rect.y:
                return True
        return False

    #既存にm個の長方形が配置されている状態に長方形nのBL点を求める
    def BL_method(self, rects_after, W, UB):
        if not rects_after:
            return (0, 0)
        BL_point = []
        a = self.NFP_r2(rects_after)
        m = len(rects_after)
        
        for i in range(m):
            if not intersection_y(a[i]) == False \
            and include_p_e(intersection_y(a[i]), self.include_room(W, UB)) == True \
            and Rect.point_search(rects_after, intersection_y(a[i])) == False:
                BL_point.append(intersection_y(a[i]))
                
            if not intersection_x(a[i]) == False \
            and include_p_e(intersection_x(a[i]), self.include_room(W, UB)) == True \
            and Rect.point_search(rects_after, intersection_x(a[i])) == False:
                BL_point.append(intersection_x(a[i]))
                
            for j in range(i):
                if not intersection_NFP(a[j], a[i]) == False \
                and include_p_e(intersection_NFP(a[j], a[i]), self.include_room(W, UB)) == True \
                and Rect.point_search(rects_after, intersection_NFP(a[j], a[i])) == False:
                    BL_point.append(intersection_NFP(a[j], a[i]))
                    
        BL_point = sorted(BL_point, key=lambda t:t[0])
        BL_point = sorted(BL_point, key=lambda t:t[1])

        for BL in BL_point:
            if include_p_n(BL, a, m) == False:
                p = (BL[0],BL[1])
                break
        return p
    
    def __str__(self):
        return "({}, {}, {}, {},{},{})".format(self.x, self.y, self.w, self.h, self.weight,self.name)
    
n=10
W=44.5
sort_key="weight"

h = pd.read_csv("具体的な数値.csv", usecols=[1]).squeeze('columns').values
print('高さ')
print(h[:n])
print('--------------------')

w = pd.read_csv("具体的な数値.csv", usecols=[2]).squeeze('columns').values
print('横幅')
print(w[:n])
print('--------------------')

weight= pd.read_csv("具体的な数値.csv", usecols=[3]).squeeze('columns').values


item=(np.random.randint(0,4,10)+1)//2
print(item)
name=pd.read_csv("具体的な数値.csv", usecols=[0]).squeeze('columns').values

before_list = []
for i in range(len(item)):
    for j in range(item[i]):
        before_list.append((w[i], h[i], weight[i],name[i]))

n = len(before_list)

print('before sort')
print(before_list)
print('--------------------')

UB=n*max(h)

rects_before=[]
for i in range(n):
    new_rect = Rect(None, None, before_list[i][0], before_list[i][1],before_list[i][2],before_list[i][3])
    rects_before.append(new_rect)
    
    
    rects_before = sorted(rects_before, key=lambda t:t.h, reverse=True)
    rects_before = sorted(rects_before, key=lambda t:t.w, reverse=True)
    rects_before = sorted(rects_before, key=lambda t:t.weight, reverse=True)

print('配置前')
for rect in rects_before:
    print(rect)

start = time.time()

rects_after = []

for rect in rects_before:
    BL_point = Rect.BL_method(rect, rects_after, W, UB)
    rect1 = Rect(BL_point[0], BL_point[1], rect.w, rect.h ,rect.weight,rect.name) 
    rects_after.append(rect1)

print('-----------------')
print('配置後')
for rect in rects_after:
    print(rect)
    
H = max(rects_after[i].y+rects_after[i].h for i in range(n))
print(f'H={H}')
    
elapsed_time = time.time() - start
print("実行時間 = {:.3f} (秒)".format(elapsed_time))


%matplotlib inline
fig = plt.figure(figsize=(6,6))
ax = fig.add_subplot(111,aspect='equal') 
ax.set_xlim ([0,W])
ax.set_ylim ([0,H])

name = 0


for rect in rects_after:
    name = name + 1
    
    
    if rect.y <= 24.9:
        ax.text(rect.x+rect.w/2, rect.y+rect.h/2, f'{name}', horizontalalignment="center", verticalalignment="center")
        rect_color = 'burlywood'
    else:
        ax.text(rect.x+rect.w/2, rect.y+rect.h/2, f'{name}', horizontalalignment="center", verticalalignment="center", color='white')
        rect_color = 'lightblue'
       
        
    rect1 = plt.Rectangle((rect.x,rect.y),rect.w,rect.h,fc=rect_color,ec="black")
    ax.add_patch(rect1)
    

ax.text(0, H+1, f'H={H}', horizontalalignment="center")
ax.text(W+1, 0, f'W={W}', verticalalignment="center")

ax.axhline(y=25, color='red', linestyle='--')

plt.show()

「具体的な数値.csv」のエクセルファイルのデータは以下の通りです。

スクリーンショット 2023-10-12 152414.png

書き換える主な個所は以下のBL_methodだと思っているのですが左下を基準としているBL点をどのようにして右下に持っていくかが分かりません。もしかしたらここ以外の箇所も書き換えないといけないかもしれません。

該当するソースコード

def BL_method(self, rects_after, W, UB):
        if not rects_after:
            return (0, 0)
        BL_point = []
        a = self.NFP_r2(rects_after)
        m = len(rects_after)
        
        for i in range(m):
            if not intersection_y(a[i]) == False \
            and include_p_e(intersection_y(a[i]), self.include_room(W, UB)) == True \
            and Rect.point_search(rects_after, intersection_y(a[i])) == False:
                BL_point.append(intersection_y(a[i]))
                
            if not intersection_x(a[i]) == False \
            and include_p_e(intersection_x(a[i]), self.include_room(W, UB)) == True \
            and Rect.point_search(rects_after, intersection_x(a[i])) == False:
                BL_point.append(intersection_x(a[i]))
                
            for j in range(i):
                if not intersection_NFP(a[j], a[i]) == False \
                and include_p_e(intersection_NFP(a[j], a[i]), self.include_room(W, UB)) == True \
                and Rect.point_search(rects_after, intersection_NFP(a[j], a[i])) == False:
                    BL_point.append(intersection_NFP(a[j], a[i]))
                    
        BL_point = sorted(BL_point, key=lambda t:t[0])
        BL_point = sorted(BL_point, key=lambda t:t[1])

        for BL in BL_point:
            if include_p_n(BL, a, m) == False:
                p = (BL[0],BL[1])
                break
        return p

よろしくお願いします。

0

2Answer

左下詰めのプログラムをそのまま流用し「rects_afterが完成したらその内容を左右反転させる」という処理だけ付け加えるのはどうでしょう。:stuck_out_tongue_winking_eye:
左右反転のために増える時間は、「左下詰め処理」と比べればわずかなものでしょうから。

0Like

右下から配置する目的がわかりませんが、
自分なら、今の左下からの配置結果を反転します。
W-x-wするだけですから。

0Like

Comments

  1. @oiwly0706

    Questioner

    Bottom-left法に重心を考えており、配置する長方形の底辺の中心が、箱か他の長方形と接しているような配置を行うようにしていきたいと考えております。この画像で7を配置する際、

    ①4に密接する形で3の上に配置しようとする
    ②7の底辺の中心がどことも接していないため別のBL点を探す
    ③4の上に配置する

    という手順を送っているのですが、なるべく赤点線より上から配置することを避けたいため右下配置の方法で6に密接する形で3の上に配置するようにしたいです。

    スクリーンショット 2023-10-13 095655.png

  2. なるべく赤点線より上から配置することを避けたいため右下配置の方法で6に密接する形で3の上に配置するようにしたいです。

    それは、Bottom-left/right法というより、高さ方向の配置ルールの違いだと思います。

    今のコードでも高さの上限(変数UB)を32とかに変更すれば、横に配置しませんか?

  3. @oiwly0706

    Questioner

    返信ありがとうございます。アドバイスしていただいた通りにUBの値を32に変更したところ、以下のようなエラーメッセージが出ました。

    TypeError                                 Traceback (most recent call last)
    Input In [81], in <cell line: 183>()
        183 for rect in rects_before:
        184     BL_point = Rect.BL_method(rect, rects_after, W, UB)
    --> 185     rect1 = Rect(BL_point[0], BL_point[1], rect.w, rect.h ,rect.weight,rect.name) 
        186     rects_after.append(rect1)
        188 print('-----------------')
    
    TypeError: 'NoneType' object is not subscriptable
    

    またプロットはできたのですが7が表示されない状態でした。スクリーンショット 2023-10-13 205103.png

  4. 上のエラーは必ず起きますか?
    よく見ると、 元のコードの135行目あたりに、乱数が使われてますね。
    item=(np.random.randint(0,4,10)+1)//2
    そのため、実行するたびに、配置が毎回変わります。

  5. @oiwly0706

    Questioner

    大変申し訳ございません。実行していたコードを間違えておりました。ご指摘いただいた意見をもとにもう一度整理して質問を立て直させていただきますのでお時間がありましたら、そちらでアドバイスをいただいてもよろしいでしょうか。余計なお時間をお取りして申し訳ありません。

Your answer might help someone💌